Here is the structure of data1
var data1 = [{
"Date": "2016-07-09",
"StockList": [{"Name": "H1", "PNL2": 20, "NAV2" : 20}, {"Name": "H2", "PNL2": 20, "NAV2" : 20}]
"NAV": 26.28,
"PNL": 7.61
}, {
"Date": "2016-07-10",
"StockList": [{"Name": "H1", "PNL2": 20, "NAV2" : 20}, {"Name": "H2", "PNL2": 20, "NAV2" : 20}]
"NAV": 27.55,
"PNL": 12.89
}];
since nested data is not allowed in the Amcharts(if there is a way to deal with the nested data, that is better), is it possible flat the into the top level object(I can not change the original inputed data). For example:
var data1 = [{
"Date": "2016-07-09",
"H1_PNL2": 20,
"H1_NAV2" : 20
"H2_PNL2": 20,
"H2_NAV2" : 20,
"NAV": 26.28,
"PNL": 7.61
}, {
"Date": "2016-07-10",
"H1_PNL2": 20,
"H1_NAV2" : 20
"H2_PNL2": 20,
"H2_NAV2" : 20,
"NAV": 27.55,
"PNL": 12.89
}];
In such Amcharts code:
var dataSet1 = new AmCharts.DataSet();
dataSet1.color = "#b0de09";
//create your field mappings for each valueField
dataSet1.fieldMappings = valueFields.map(function(valueField) {
return {
fromField: valueField,
toField: valueField
};
});
dataSet1.dataProvider = data;
dataSet1.categoryField = "Date";
chart.dataSets = [dataSet1];
// PANELS
var stockPanel = new AmCharts.StockPanel();
stockPanel.showCategoryAxis = true;
stockPanel.title = "PNL2";
stockPanel.eraseAll = false;
//stockPanel.addLabel(0, 100, "Click on the pencil icon on top-right to start drawing", "center", 16);
//create a graph for each valueField
valueFields.forEach(function(valueField) {
var graph = new AmCharts.StockGraph();
graph.title = valueField;
graph.valueField = valueField;
graph.bullet = "round";
graph.bulletColor = "#FFFFFF";
graph.bulletBorderColor = "#00BBCC";
graph.bulletBorderAlpha = 1;
graph.bulletBorderThickness = 2;
graph.bulletSize = 7;
graph.lineThickness = 2;
graph.lineColor = "#00BBCC";
graph.useDataSetColors = false;
stockPanel.addStockGraph(graph);
});
chart.addPanel(stockPanel);
chart.write("chartdiv");
}
createStockChart(data1);
Here is my data structure
[{,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…},…]
[0 … 99]
0:{,…}
CashLeft:0
Commission:0
Date:"/Date(1454428800000)/"
NAV:0
PNL:0
Performance:[{
0:{Stock: {Symbol: "1044 HK", Close: 66.714, Date: "/Date(1454428800000)/"}, Position: 0,…}
1:{Stock: {Symbol: "1088 HK", Close: 10.9, Date: "/Date(1454428800000)/"}, Position: 0,…}
2:{Stock: {Symbol: "12 HK", Close: 35.955, Date: "/Date(1454428800000)/"}, Position: 0,…}
And here is the code I write down
function createStockChart(data) {
var chart = new AmCharts.AmStockChart();
data.forEach(function (data1) {
data1.Performance.forEach(function (stockItem) {
data1[stockItem.Symbol + "_PNL2"] = stockItem.PNL2;
});
//the following are completely optional - AmCharts won't look at StockList, but if you
//want to conserve memory and don't need the StockList for anything else, you can remove
//it
data1.StockList.length = 0; //delete the StockList Array. Not necessary if you need it for something else
delete data1.StockList; //remove the property from the object if you want. Also not necessary.
});
But when I see the data structure it does not change, Maybe here is the problem that the List of stock(I mean the name(Symbol)) will be changed some days later, that means the properties of data will changed periodically, I don't know whether it is the point?
Assuming your StockList will always have a Name, PNL2 and NAV2, you can just loop through your data and assign the properties and values to the object prior to the rest of your code.
data1.forEach(function(arrayElement) {
arrayElement.StockList.forEach(function(stockItem) {
arrayElement[stockItem.Name + "_PNL2"] = stockItem.PNL2;
arrayElement[stockItem.Name + "_NAV2"] = stockItem.NAV2;
});
//the following are completely optional - AmCharts won't look at StockList, but if you
//want to conserve memory and don't need the StockList for anything else, you can remove
//it
arrayElement.StockList.length = 0; //delete the StockList Array. Not necessary if you need it for something else
delete arrayElement.StockList; //remove the property from the object if you want. Also not necessary.
});
From there you can just run the rest of your code from my previous answer.
Demo:
var data1 = [{
"Date": "2016-07-09",
"StockList": [{
"Name": "H1",
"PNL2": 20,
"NAV2": 20
}, {
"Name": "H2",
"PNL2": 20,
"NAV2": 20
}],
"NAV": 26.28,
"PNL": 7.61
}, {
"Date": "2016-07-10",
"StockList": [{
"Name": "H1",
"PNL2": 20,
"NAV2": 20
}, {
"Name": "H2",
"PNL2": 20,
"NAV2": 20
}],
"NAV": 27.55,
"PNL": 12.89
}];
data1.forEach(function(arrayElement) {
arrayElement.StockList.forEach(function(stockItem) {
arrayElement[stockItem.Name + "_PNL2"] = stockItem.PNL2;
arrayElement[stockItem.Name + "_NAV2"] = stockItem.NAV2;
});
//the following are completely optional - AmCharts won't look at StockList, but if you
//want to conserve memory and don't need the StockList for anything else, you can remove
//it
arrayElement.StockList.length = 0; //delete the StockList Array. Not necessary if you need it for something else
delete arrayElement.StockList; //remove the property from the object if you want. Also not necessary.
});
console.log(data1);
Related
Is it possible to create multiple series from the json outlined below? If not, what would be the most efficient way to do so?
[
{
"Label": "Series 1",
"Std_Dev": 12.3003,
"Return": 3.1513
},
{
"Label": "Series 2",
"Std_Dev": 10.0244,
"Return": 3.1808
},
{
"Label": "Series 3",
"Std_Dev": 7.7506,
"Return": 3.1057
},
{
"Label": "Series 4",
"Std_Dev": 5.5022,
"Return": 2.9264
}
]
My example code is below, I'm trying to produce a scatter chart where each series will be labelled on the legend. The chart data is the json.
am4core.ready(function () {
am4core.useTheme(am4themes_animated);
// Create chart instance
var chart = am4core.create("chart-container", am4charts.XYChart);
// Create axes
var valueAxisX = chart.xAxes.push(new am4charts.ValueAxis());
valueAxisX.title.text = 'Standard Deviation';
// Create value axis
var valueAxisY = chart.yAxes.push(new am4charts.ValueAxis());
valueAxisY.title.text = 'Return';
chart.data = myJSON;
// Function to create series
function createSeries(r, sd, name, fill) {
var lineSeries = chart.series.push(new am4charts.LineSeries());
lineSeries.dataFields.valueY = Return;
lineSeries.dataFields.valueX = Std_Dev;
lineSeries.strokeOpacity = 0;
lineSeries.name = name;
lineSeries.stroke = fill;
lineSeries.fill = am4core.color(fill).lighten(0.0);
var bullet = lineSeries.bullets.push(new am4charts.CircleBullet());
}
// Create Series
createSeries(what, to, put, here);
});
}
I managed to achieve this by:
chart.data = data;
function createSeries(data) {
var lineSeries = chart.series.push(new am4charts.LineSeries());
lineSeries.data = data;
lineSeries.dataFields.valueY = "Return";
lineSeries.dataFields.valueX = "Std_Dev";
lineSeries.valueY = 1;
lineSeries.valueX = 2;
lineSeries.strokeOpacity = 0;
lineSeries.name = lineSeries.data[0]['Label'];
var bullet = lineSeries.bullets.push(new am4charts.CircleBullet());
};
// Create Series by looping through object
chart.data.forEach(function (obj) {
createSeries([obj]);
});
Most examples I have found are using data that has time and number
var data = [
{
"Time": "19-Jan-2018 11:24:49.000 UTC",
"Speed": 1.885
},
{
"Time": "19-Jan-2018 11:24:59.000 UTC",
"Speed": 1.875
},
{
"Time": "19-Jan-2018 11:25:00.000 UTC",
"Speed": 1.878
},
{
"Time": "19-Jan-2018 11:25:01.000 UTC",
"Speed": 1.876
}
]
I am looking to stack type
var data = [
{
"Time": "19-Jan-2018 11:24:49.000 UTC",
"type": "CAT"
},
{
"Time": "19-Jan-2018 11:24:59.000 UTC",
"type": "DOG"
},
{
"Time": "19-Jan-2018 11:25:00.000 UTC",
"type": "CAT"
},
{
"Time": "19-Jan-2018 11:25:01.000 UTC",
"Type": "BAT"
}
]
How can I stack categorical data, while allowing the user to select time/category pairs, as in the following Example?
I adapted the example to time/category data in this fiddle.
Those dates would only parse in Chrome, so
const parseDate = d3.utcParse("%d-%b-%Y %H:%M:%S.%L UTC");
data.forEach(d => {
d.Time = parseDate(d.Time);
})
I changed the key functions to use ,
function multikey(x,y) {
return x + ',' + y;
}
function splitkey(k) {
return k.split(',');
}
I also changed fake group stack_second to convert string-dates from the multikeys back into Dates, and to initialize categories to 0 (since every stack has to be present for every X).
function stack_second(group, categories) {
return {
all: function() {
var all = group.all(),
m = {};
// build matrix from multikey/value pairs
all.forEach(function(kv) {
var ks = splitkey(kv.key);
m[ks[0]] = m[ks[0]] || Object.fromEntries(categories.map(c=>[c,0]));
m[ks[0]][ks[1]] = kv.value;
});
// then produce multivalue key/value pairs
return Object.keys(m).map(function(k) {
return {key: new Date(k), value: m[k]};
});
}
};
}
Get the array of categories from the source data:
const categories = Array.from(new Set(data.map(d => d.Type)).values());
When dealing with date/time, you have to choose a d3 time interval appropriate for your data. Here minutes looked right. Using UTC d3-time methods everywhere because your source data is UTC.
const interval = d3.utcMinute;
Calculate xscale domain and apply:
let extent = d3.extent(data, d=>d.Time);
extent[1] = interval.offset(extent[1], 1)
chart
.x(d3.scaleTime().domain(extent))
.xUnits(interval.range)
Right number of ticks, also formatted in UTC:
chart.xAxis().ticks(d3.utcMinute).tickFormat(d3.utcFormat('%H:%M'))
Match colors between stacks and wedges with
.colors(d3.scaleOrdinal().domain(categories).range(d3.schemeCategory10))
Crossfilter initialization, using the interval and categories:
const interval = d3.utcMinute; // choose appropriate to your data
var cf = crossfilter(data),
timeTypeDim = cf.dimension(function(d) { return multikey(interval(d.Time), d.Type); }),
timeTypeGroup = timeTypeDim.group(), // reduceCount by default
stackedGroup = stack_second(timeTypeGroup, categories);
And here's the chart code for completeness, although we've already discussed the relevant parts:
function sel_stack(i) {
return function(d) {
return d.value[i];
};
}
chart
.width(600)
.height(400)
.colors(d3.scaleOrdinal().domain(categories).range(d3.schemeCategory10))
.controlsUseVisibility(true)
.x(d3.scaleTime().domain(extent))
.xUnits(interval.range)
.margins({left: 80, top: 20, right: 10, bottom: 20})
.brushOn(false)
.clipPadding(10)
.title(function(d) {
return d.key + '[' + this.layer + ']: ' + d.value[this.layer];
})
.legend(dc.legend().x(540).y(50))
.dimension(timeTypeDim)
.group(stackedGroup, categories[0], sel_stack(categories[0]))
.renderLabel(true);
I found the code from here:
On load Google LineChart animation
google.load('visualization', '1', { packages: ['corechart'], callback: function() {
var data = new google.visualization.DataTable();
data.addRows(5);
data.addColumn('string', '');
data.addColumn('number', 'Sales');
data.addRows(5);
data.setValue(0, 0, 'Jan');
data.setValue(1, 0, 'Feb');
data.setValue(2, 0, 'Mar');
data.setValue(3, 0, 'Apr');
data.setValue(4, 0, 'May');
var options = {
title: 'Sales by months for 2013 year', curveType: 'function',
"vAxis": { "minValue": "0", "maxValue": 6 }, "hAxis": { "slantedTextAngle": "45", "slantedText": "true" }, "legend": { "position": "top" }, "pointSize": "5",
animation: { duration: 250 }
};
var chart = new google.visualization.LineChart(document.getElementById('test'));
var index = 0;
var chartData = [ 5, 1, 4, 2, 3 ]
var drawChart = function() {
console.log('drawChart index ' + index);
if (index < chartData.length) {
data.setValue(index, 1, chartData[index++]);
chart.draw(data, options);
}
}
google.visualization.events.addListener(chart, 'animationfinish', drawChart);
chart.draw(data, options);
drawChart();
}});
If I want to create multi-line, how to modify this code? Thanks!
I'm not a javascript programmer and not familiar with OOP
The author is OneMoreVladimir, but I don't have the access to comment under his post.
google.load('visualization', '1', {
packages: ['corechart'],
callback: function() {
var data = new google.visualization.DataTable();
data.addRows(5);
data.addColumn('string', '');
data.addColumn('number', 'Sales');
data.addColumn('number', 'Sales2');
data.addRows(5);
data.setValue(0, 0, 'Jan');
data.setValue(1, 0, 'Feb');
data.setValue(2, 0, 'Mar');
data.setValue(3, 0, 'Apr');
data.setValue(4, 0, 'May');
var options = {
title: 'Sales by months for 2013 year',
curveType: 'function',
"vAxis": {
"minValue": "0",
"maxValue": 8
},
"hAxis": {
"slantedTextAngle": "45",
"slantedText": "true"
},
"legend": {
"position": "top"
},
"pointSize": "5",
animation: {
duration: 600
}
};
var chart = new google.visualization.LineChart(document.getElementById('test'));
var index = 0;
var chartData = [5, 1, 4, 2, 3]
var drawChart = function() {
console.log('drawChart index ' + index);
if (index < chartData.length) {
data.setValue(index, 1, chartData[index++]);
chart.draw(data, options);
}
}
var index1 = 0;
var chartData1 = [1, 3, 4, 6, 5]
var drawChart1 = function() {
console.log('drawChart1 index1 ' + index1);
if (index1 < chartData1.length) {
data.setValue(index1, 2, chartData1[index1++]);
chart.draw(data, options);
}
}
google.visualization.events.addListener(chart, 'animationfinish', drawChart);
google.visualization.events.addListener(chart, 'animationfinish', drawChart1);
chart.draw(data, options);
drawChart();
drawChart1();
}
});
I figure out! Here is the code.
But another question, the code pass the http://jsfiddle.net and run very well, but when I copy paste it into the HTML why it doesn't work? Did I miss something?
I would like to show sum of multiple values as one chart output with amCharts. I am using dataLoader with JSON to get the data. I know I have to create a function for but I couldn't understand how to get the data from the dataLoader to calculate
{
"balloonText": "[[title]] of [[valueAxis]]:[[value]]",
"lineThickness": 3,
"id": "sumValue",
"title": "sum Value",
"valueField": (function() {
var sumValues = "calculation";
return sumValues
}
this attempt is probably not correct but this is how I started
{
"balloonText": "[[title]] of [[valueAxis]]:[[value]]",
"lineThickness": 3,
"id": "LoadigTime",
"title": "Loadig Time",
"valueField": (function() {
var sumValues = (HomePageLoad + LoginToParametersLoad + ParametersLoad + AlarmsLoad + SwitchSideLoad + LoginToAdminLoad + AdminLoad) / 7;
return sumValues
})
}
valueField cannot be a function, only a string reference to a field in your data.
If the chart is meant to be displaying the sum of all of those fields in your data as a chart, simply add logic to your postProcess callback to create a new dataset containing your sums, e.g.
postProcess: function(data) {
var newData = [];
data.forEach(function(dataItem) {
var item = {
YOUR_CATEGORY_FIELD: dataItem.YOUR_CATEGORY_FIELD, //replace with your category field name
sum: 0
};
//loop through your item's keys and sum everything up, filtering out
//your category property
item.sum = Object.keys(dataItem).reduce(function(sum, key) {
if (key !== "YOUR_CATEGORY_FIELD") {
sum += dataItem[key]
}
return sum;
}, 0);
newData.push(item);
});
return newData;
},
// ...
graphs: [{
valueField: "sum",
// other props here
}]
I have the following code (see below). The question is how to hide the formatter textbox for column quantity_value1 whenever the value of the column condition1 is 0.
var response = {
"id": 1,
"items": [
{"condition1": 1, "quantity_value1":"value1"},
{"condition1": 1, "quantity_value1":"value2"},
{"condition1": 0, "quantity_value1":"value3"},
{"condition1": 1, "quantity_value1":"value4"},
{"condition1": 0, "quantity_value1":"value5"}
]
};
var bpColumns = [
{label:'header1', children:[
{
key:"condition1"
},
{
key:"quantity_value1",
formatter:YAHOO.widget.DataTable.formatTextbox
}]}
];
YAHOO.example.Data = response;
this.bpDataSource = new YAHOO.util.DataSource(YAHOO.example.Data);
this.bpDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
this.bpDataSource.responseSchema = {
resultsList: "items",
fields: ["condition1","quantity_value1"]
};
this.standardSelectDataTable = new YAHOO.widget.ScrollingDataTable("mydiv",
bpColumns, this.bpDataSource, {height:"15em"});
You will have to write your own formatter as shown here in the user guide. There is an example here. There is no way to configure the built-in one, but you might use it as a the basis for your version, search for formatTextbox in the source.
This works:
var response = {
"id": 1,
"items": [
{"condition1": 1, "quantity_value1":"value1"},
{"condition1": 1, "quantity_value1":"value2"},
{"condition1": 0, "quantity_value1":"value3"},
{"condition1": 1, "quantity_value1":"value4"},
{"condition1": 0, "quantity_value1":"value5"}
]
};
var bpColumns = [
{label:'header1', children:[
{
key:"condition1"
},
{
key:"quantity_value1",
formatter:"formatTextbox"
}]}
];
var vrecord = null;
YAHOO.example.Data = response;
this.formatTextbox = function(el, oRecord, oColumn, oData, oDataTable) {
if (oRecord.getData("condition1") === 1){
var value = (YAHOO.lang.isValue(oData)) ? YAHOO.lang.escapeHTML(oData.toString()) : "";
var input = document.createElement("input");
input.type = "text";
input.value = value;
input.onchange = function()
{
vrecord.setData("quantity_value1",this.value);
console.log(vrecord);
};
el.appendChild(input);
}
};
// Add the custom formatter to the shortcuts
YAHOO.widget.DataTable.Formatter.formatTextbox = this.formatTextbox;
this.bpDataSource = new YAHOO.util.DataSource(YAHOO.example.Data);
this.bpDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
this.bpDataSource.responseSchema = {
resultsList: "items",
fields: ["condition1","quantity_value1"]
};
this.standardSelectDataTable = new YAHOO.widget.ScrollingDataTable("mydiv",
bpColumns, this.bpDataSource, {height:"15em"});
this.standardSelectDataTable.subscribe("rowClickEvent", this.standardSelectDataTable.onEventSelectRow);
this.standardSelectDataTable.subscribe("rowClickEvent", function(event, target) {
var selectedRows = this.getSelectedRows();
vrecord = this.getRecord(selectedRows[0]);
});