I'm trying to adapt this jQuery DataTables example to a d3 chart that I've been developing.
Here's a link to the semi-working Plunker: http://plnkr.co/edit/kXvBjNsCbblC3ykkuPsL?p=preview
The problem is that some of the values are showing up in the table, while others are not (in particular, the values that come from an array within an array).
Oddly enough, the error message I'm getting, Cannot read property '0' of undefined, refers to line 1074, on which recordCol is defined. This is strange because the values for recordCol and stateName both show up just fine in the DataTable. Also strange is that all of the column headers do appear, even for the nested array (though not their values).
Here's the problematic code:
function tables(dataset) {
var recordCol = Object.keys(dataset[0])[0];
var stateName = Object.keys(dataset[0])[3];
var dateCol = Object.keys(dataset[0].values[0])[0];
var valCol = Object.keys(dataset[0].values[0])[1];
var monthDateFormat = d3.time.format("%B");
var yearDateFormat = d3.time.format("%Y");
var properDateFormat = d3.time.format("%B %Y");
var tableData = dataset.map(function(d) {
d[recordCol] = d[recordCol].toString().slice(0, 15);
d[stateName] = d[stateName].toString().slice(0, 20);
d[dateCol] = d[dateCol];//.toString().slice(0, 20);
d[valCol] = d[valCol];///.toString().slice(0, 20);
return d;
})
$(".data-table").empty();
if (dataTable) {
dataTable.destroy();
}
dataTable = $(".data-table").DataTable({
data: tableData,
columns: [{
"data": recordCol
}, {
"data": stateName
}, {
"data": dateCol
}, {
"data": valCol
}]
});
d3.selectAll("thead th").each(function(d, i) {
if (i === 0) {
this.textContent = recordCol;
} else if (i === 1) {
this.textContent = stateName;
} else if (i === 2) {
this.textContent = dateCol;
} else if (i === 3) {
this.textContent = valCol;
}
});
}
As you'll see in my semi-working Plunker, I've been beating a dead horse in console.log, trying to troubleshoot the error messages I've been getting, the other of which is this.
In sum, what I'm trying to do is get all the values for x and y to appear in the DataTable alongside state and record -- as well as relabel the column headers that currently read x and y as as date and value, respectively.
In advance, thanks very much for any assistance you're able to offer.
Update:
The following changes, I've discovered, make the full subarray, containing all the x and y values, appear in every row of the DataTable:
dataTable = $(".data-table").DataTable({
data: tableData,
columns: [{
"data": recordCol
}, {
"data": stateName
}, {
"data": "values[, ].x"
}, {
"data": "values[, ].y"
}]
});
Here's an updated Plunker. The search for a solution continues.
Update 2:
The following changes make the x and y value appear as Sun May 01 2011 00:00:00 GMT-0400 (Eastern Daylight Time) and 2761, but the same in every row:
var tableData = dataset.map(function(d) {
d[recordCol] = d[recordCol].toString().slice(0, 15);
d[stateName] = d[stateName].toString().slice(0, 20);
for (var i = 0; i < dataset.length; i++) {
d[dateCol] = dataset[0].values[i].x;
d[valCol] = dataset[0].values[i].y;
}
dataTable = $(".data-table").DataTable({
data: tableData,
columns: [{
"data": recordCol
}, {
"data": stateName
}, {
"data": dateCol
}, {
"data": valCol
}]
});
Another updated Plunker. Obviously, this is wrong; still searching for a solution.
Update 3:
Still searching for a solution, but in this third updated Plunker, I discovered how to take all of the values contained in the subarray of the parent array's first row, and (incorrectly) map them onto all of the rows of the parent array. Here's an illustration of what's going wrong:
Is anyone able to demonstrate a solution?
You need to change your table map. Try it:
var table_map = [];
for (var i = 0; i < dataset.length; i++) {
for (var j = 0; j < dataset[i].values.length; j++) {
var table_row = {};
table_row[recordCol] = dataset[i][recordCol];
table_row[stateName] = dataset[i][stateName];
table_row[dateCol] = dataset[i].values[j][dateCol];
table_row[valCol] = dataset[i].values[j][valCol];
table_map.push(table_row);
}
}
Related
I'm using C3 charts library to draw charts. I send data to the chart using two arrays, which are 'timeArray' and 'dataArray', one for the X-Axis and the other one for Y-Axis respectively. This simple logic was working fine.
Later I had to implement a change such that I had to take average of every three elements of an array and then make a new array and then plot the graph using averaged values.
I started facing a problem that a spurious point was being plotted on the graph. Whenever this error occurs, only one spurious point is added in the end. I've checked the arrays that are used to plot the graph, they do not have that spurious point. When I take the average of every three elements, I face this problem almost every time, however when I take average of 500 or 1000 points I face this error only sometimes.
As you can see in the code I have already tried removing the last point of the final array since the spurious point that was being added was always the last point in the chart. I've also tried changing the graph type, it did not help.
socket.on('get-avg-graph', function(data) {
// dataPoints = Points for Y-Axis
// mili = Points for X-Axis
var dataPoints = data.dataPoints;
var mili = data.mili;
var sumX = 0;
var sumY = 0;
var avgXGraph = 0;
var avgYGraph = 0;
var avgXArray = [];
var avgYArray = [];
for (var i = 0; i < dataPoints.length - 999; i++) {
for (var j = i; j < i + 999; j++) {
sumX = sumX + mili[j];
sumY = sumY + dataPoints[j];
}
if (sumY !== 0) {
avgXGraph = ( sumX / 1000 );
avgXArray.push(avgXGraph);
avgYGraph = ( sumY / 1000 );
avgYArray.push(avgYGraph);
sumX = 0;
sumY = 0;
avgXGraph = 0;
avgYGraph = 0;
}
}
io.emit('get-avg-graph-response', avgXArray, avgYArray);
});
socket.on('get-avg-graph-response', function(avgXArray, avgYArray) {
plot_X_axis = [];
plot_Y_axis = [];
drawChart();
avgXArray.splice( -1, 1);
avgYArray.splice( -1, 1);
plot_X_axis.push.apply(plot_X_axis, avgXArray);
plot_Y_axis.push.apply(plot_Y_axis, avgYArray);
drawChart();
});
function drawChart() {
var graphTitle = $("#test_type_show").val();
dataArray = [];
dataArray[0] = "PRESSURE";
dataArray.push.apply(dataArray, plot_Y_axis);
timeArray = [];
timeArray[0] = "TIME";
timeArray.push.apply(timeArray, plot_X_axis);
if (chart==null) {
chart = c3.generate({
bindto: '#chart1',
title: {
text: graphTitle
},
data: {
x: 'TIME',
columns: [
timeArray,
dataArray
],
type: 'spline'
},
axis: {
x: {show:false},
y: {show: true}
},
grid: {
x: {
show: true
},
y: {
show: true
}
},
point: {
show: false
}
});
} else {
chart.load({
x: 'TIME',
columns: [
timeArray,
dataArray
],
type: 'spline'
});
}
chart.internal.xAxis.g.attr('transform', "translate(0," + chart.internal.y(0) + ")");
chart.internal.yAxis.g.attr('transform', "translate(" + chart.internal.x(0) + ", 0)");
}
I expect the output of the code to be the actual graph without any spurious data added anywhere.
I have this jsfiddle that will illustrate my problem: http://jsfiddle.net/qner9hwc/1/
Whenever you add a series that doesn't have the highest integer value, whether it would be at the bottom or in the middle somewhere, the top values get pushed off the chart area or the categories just don't fit inside the graph area at all.
The categories are being added dynamically and expect integer values. I have them being sorted in ascending order.
Try this use case:
Add a series "10", then "5" (you will see my problem here), then add "15" and it will correct itself
$('#addSeries').on('click', function () {
//first check if value is already a series
var seriesid = document.getElementById('txtValue').value;
getSeriesIDIndex(seriesid, function (idindex) {
//if it doesnt exist, add it to chart
if (idindex == -1) {
categories.push(parseInt(seriesid));
categories = categories.sort(sortNumber);
console.log(categories);
chart.yAxis[0].setCategories(categories, false);
getCategoryIndex(parseInt(seriesid), function (cindex) {
chart.addSeries({
name: seriesid,
data: [{
x: currentDate.getTime(),
y: cindex
}]
}, false);
for (i = 0; i < chart.series.length; i++) {
var cur_series = chart.series[i];
var cur_cindex;
getCategoryIndex(chart.series[i].name, function (cindex) {
cur_cindex = cindex;
});
for (var z = 0; z < cur_series.data.length; z++) {
if (cur_series.data[z].y >= cindex && cur_series.name !== seriesid) {
console.log('change!' + cur_series.data[z].y);
cur_series.data[z].y = cur_cindex;
}
}
}
console.log(chart.yAxis[0].categories);
chart.redraw();
});
}
});
});
This line is reponsible for your troubles:
cur_series.data[z].y = cur_cindex;
Proper code:
cur_series.data[z].update(cur_cindex, false);
You are updating only value, while you should be using point.update() for updating point. Demo: http://jsfiddle.net/qner9hwc/2/
I have this in my code
series: [{
name: 'Velocidad',
turboThreshold: 5000,
data: (function() {
// generate an array of random data
var data = [],
i;
for (i = 0; i < valor; i++) {
data.push({
x: dataUtc[i],
y: dataVel[i],
id: i
});
}
return data;
})(),
tooltip: {
valueDecimals: 0,
valueSuffix: 'Kms/H'
}
}]
When I try to get the maximum value I only get the x or y value.
I need to get the id value though.
What can I do?
I tried the following:
var chart = $('#containerVelocidad').highcharts();
min = chart.xAxis[0].min;
max = chart.xAxis[0].max;
Unfortunately that didn't help.
First of all, use yAxis.dataMax not yAxis.max. The second one is returning actual extremes, when sometimes you may have different extremes, than just from one series (zooming, setting extremes, multiple series etc.).
Demo: http://jsfiddle.net/ez4hjazk/1/
And code:
var max = chart.yAxis[0].dataMax,
maxIndex = chart.series[0].yData.indexOf(max), //get index from yData, read more about .indexOf() !
data = chart.series[0].data;
// make sure point is within actual extremes
if(maxIndex > -1) {
console.log("max = " + max, 'id = ' + data[maxIndex].id);
}
Do you need to get the maximum/minimum of id?
If so, try chart.series[0].data[i].id. Here's an example: http://jsfiddle.net/ez4hjazk/
I have function for updating sensors. Idea is simple, i add new points from sensors (1 sensor = 1 line) and after appending I update charts. But something went wrong and all series equal to themself
seriesArr - just array of sensors like {"sensorId1", sensorId2 etc}
chartObj - object of charts like chart 1: { chart: highcharts,
seriesArr: seriesArr }
Simple explanations:
chartObject.chart.series[0].points = chartObject.chart.series[1].points = chartObject.chart.series[2].points = etc
My code:
updateAllCharts: function() {
var allCharts = this.charts;
console.log('charts');
for (var chart in allCharts) {
var chartObject = allCharts[chart];
for (var i = 0; i < chartObject.seriesArr.length; i++) {
var elemId = chartObject.seriesArr[i][0];
var val = this.sensors[elemId].get('value');
console.log(val);
if (val === undefined) { // if sensor cant get data
continue;
}
var now = new Date;
var x = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(),
now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds());
var y = parseFloat(val);
chartObject.chart.series[i].addPoint([x, y], false, false);
console.log(x, y);
console.log(chartObject.chart.series[i].data);
}
}
for (var chart in allCharts) {
var chartObject = allCharts[chart];
chartObject.chart.redraw();
}
}
Screen:
UPDATE:
ProcessedDataX and Y arrays are changing, but there problem with points array, I always get some weird (maybe cached) points. WTF.
Playground
If you wanna play, i've setted up here jsfiddle, but actually it probably doesn't work. Can't add points to highchart object with setInterval.
JSFIDDLE
UPDATE2:
Actually it works fine in jsfiddle but i don't know wtf is going on in my project.
UPDATE3:
Found, that the same last point adds to each series. For example, series[i].lastPoints = series[n] , where n = last iteration.
Works, only if i REDRAW EVERYTIME AFTER ADDING EACH POINT. so bad solution, so bad.
just put true in parameter after Point object
chartObject.chart.series[z].addPoint(Point, true, false);
To all chart.redraw() you need to set isDirty flag
so try to use this code:
for (var chart in allCharts) {
var chartObject = allCharts[chart];
chartObject.yAxis[0].isDirty = true;
chartObject.chart.redraw();
}
I need to create the following array dynamically, for example:
var data = { point: [
{ x:5, y:8 },
{ x:8, y:10},
]};
console.log(data.point[0].x); // 5 n=0
console.log(data.point[1].y); // 10 n=1
At some point my application needs to expand the array to more than 2 items (n=0, n=1). Please let me know how to do that (i.e. n = 9 ).
You could use Array.push method to add element to an array.
var point = {x:1,y:1};
data.point.push(point);
you can use method 'push' like this code
var data = { point: [
{ x:5, y:8 },
{ x:8, y:10},
]};
console.log(data.point[0].x); // 5 n=0
console.log(data.point[1].y); // 10 n=1
data.point.push({x:4,y:3});
console.log(JSON.stringify(data.point));
You could do something like this:
var data = {'points' : []};
function addPoint(x, y) {
data.points.push({'x' : x, 'y' : y});
}
function getPoint(index) {
return data.points[index];
}
addPoint(10, 20);
addPoint(5, 3);
var p = getPoint(1);
alert(p.x + ", " + p.y); //alerts => "5, 3"
This would work for any arbitrary number of points.
Update
To clear the array
function clearPoints() {
data.points = [];
}
Update 2
These little functions will work okay if you have a simple page. If this points handling is going to end up being part of a larger system, it may be better to do something like this:
var data = {
'points' : [],
addPoint : function(x, y) {
this.points.push({
'x' : x,
'y' : y
});
},
getPoint : function(index) {
return this.points[index];
},
clearPoints : function() {
this.points = [];
},
removePoint : function(index) {
this.points.splice(index, 1);
}
};
Example usage:
alert(data.points.length); // => 0
data.addPoint(1, 2);
data.addPoint(8, 12);
data.addPoint(3, 7);
alert(data.points.length); // => 3
var p = data.getPoint(2); //p = {x:3, y:7};
data.removePoint(1);
alert(data.points.length); // => 2
data.clearPoints();
alert(data.points.length); // => 0
It may allow you to keep your point handling a little cleaner and easier to use and update.
You can either use the push() function as stated, or add additional items to the array using an index, which is preferred in the Google Style Guide. The latter method does require that you have a pointer to the last index.
Note that since assigning values to an array is faster than using
push() you should use assignment where possible.
for(var i=lastIndex; i < numOfNewPoints; i++){
data.point[i] = {x:4, y:3};
}