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.
Related
How to set/supply value (default to 0) for blank data in hours as shown in attached image below.
The target result should have all graphs even for blank hours.
You can create a loop and add points to your data array with some default y value (50 in the example below) and x value with the same interval as tickInterval:
var data = [
[1561593600000, 102.5],
[1561658400000, 177.45],
[1561723200000, 115.5]
],
interval = 2 * 60 * 60 * 1000,
j = 1,
i = data[0][0] + interval;
for (j; j < data.length; j++) {
for (i; i < data[j][0]; i += interval) {
data.push([i, 50])
}
i += interval;
}
Highcharts.chart('container', {
chart: {
type: 'column'
},
series: [{
data: data.sort(function(a, b) {
return a[0] - b[0]
})
}],
xAxis: {
type: 'datetime',
tickInterval: interval
},
});
Live demo: http://jsfiddle.net/BlackLabel/m0zfeyd4/
I have a Flot Chart that is live and generates random numbers that displays on the chart. It is used only as a dummy so I don't need real live data. I want the chart to start at 0 on yaxis and 100 on xaxis to make it look as real as possible. Below is my code in the js file.
var data = [], totalPoints = 100
h = 0
function getRandomData() {
//h = h + 1
//return h
//data.push(h)
if (data.length > 0)
data = data.slice(1)
// Do a random walk
while (data.length < totalPoints) {
var prev = data.length > 0 ? data[data.length - 1] : 50,
y = prev + Math.random() * 10 - 5,
if (y < 0) {
y = 0
} else if (y > 100) {
y = 100
}
data.push(y)
}
// Zip the generated y values with the x values
var res = []
for (var i = 0; i < data.length; ++i) {
res.push([i, data[i]])
}
return res
}
var interactive_plot = $.plot('#interactive', [getRandomData()], {
grid : {
borderColor: '#f3f3f3',
borderWidth: 1,
tickColor : '#f3f3f3'
},
series: {
shadowSize: 0, // Drawing is faster without shadows
color : '#3c8dbc'
},
lines : {
fill : true, //Converts the line chart to area chart
color: '#3c8dbc'
},
yaxis : {
min : 0,
max : 100,
show: true
},
xaxis : {
show: true
}
})
var updateInterval = 500 //Fetch data ever x milliseconds
var realtime = 'on' //If == to on then fetch data every x seconds. else stop fetching
function update() {
interactive_plot.setData([getRandomData()])
// Since the axes don't change, we don't need to call plot.setupGrid()
interactive_plot.draw()
if (realtime === 'on')
setTimeout(update, updateInterval)
}
//INITIALIZE REALTIME DATA FETCHING
if (realtime === 'on') {
update()
}
//REALTIME TOGGLE
$('#realtime .btn').click(function () {
if ($(this).data('toggle') === 'on') {
realtime = 'on'
}
else {
realtime = 'off'
}
update()
})
/*
* END INTERACTIVE CHART
*/
There is a button that calls the Flot chart into action and it works great but it looks too much like a staged chart. I need it start at 0 on the y-axis and 100 on the x-axis. Any information on how to go about this would be great and very much appreciated.
Fill your data array with zeroes before you start, then your update function starts adding random points one at a time instead of starting with 100 random data points:
for (var i = 0; i < totalPoints; i++) {
data.push(0);
}
See this fiddle for a full example.
PS: Please end your code lines with a semicolon!
I was playing around with the waterfall series of the jqxChart.
According to its API, the following piece of code defines the values of the axis, in this case it's the y-axis:
valueAxis:
{
title: {text: 'Population<br>'},
unitInterval: 1000000,
labels:
{
formatFunction: function (value) {
return value / 1000000 + ' M';
}
}
}
Is it possible to define the intervals not with absolute values, but with relative values. So that the interval are e.g. 10% and the overall value is 100%?
Simply doing unitInterval: '10%' doesn't work.
This is how it should look like:
Here is a fiddle.
I think you're looking for these options :
logarithmicScale: true,
logarithmicScaleBase: 1.10,
Example:
valueAxis:
{
title: {text: 'Population<br>'},
logarithmicScale: true,
logarithmicScaleBase: 1.10,
labels:
{
formatFunction: function (value) {
return value / 1000000 + ' M';
}
}
},
Edit:
var accuracy = 2;
var first = data[0].population;
var last = data[data.length - 2].population;
var unit = (100 / last);
// convert raw data to differences
for (var i = 0; i < data.length - 2; i++)
data[i].population = (data[i].population * unit).toFixed(accuracy);
I have a highcharts grouped column chart with two columns for each value on the x axis. i would like to be able to add a label above each group with difference between the two in percent. I cant seem to find any way to reference the two columns in the formatter-option.
This is the section I'm having trouble with:
column: {
dataLabels: {
formatter: function()
{
return this.y;
}
}
}
Where this.y should be the difference.
This is how it is at this time http://jsfiddle.net/LLExL/4548/
All i want changed from this is a label above each of the two columns with a percent difference.
Inside the formatter callback you can use the series.chart.series[1].yData property to get the y values of the second column/series. yData is an array of all the y values. Then you can use the point.index property to get the corresponding point of the second column/series.
column: {
dataLabels: {
formatter: function()
{
var firstColumnValue = this.y;
var secondColumnValue = this.series.chart.series[1].yData[this.point.index];
var yourCalculation = (firstColumnValue - secondColumnValue) / firstColumnValue * 100;
return yourCalculation.toFixed(2) + '%';
}
}
}
Updated JSFiddle
One possibility is to pre-calculate all the differences, and simply reference them in your formatter. For example, define your series in a variable, and loop over it to create a separate diffs array of the differences, like this:
var series = [{
name: 'Omsetning',
data: [
// ...
]
}
// ...
];
var diffs = [];
for(i = 0; i < series[0].data.length; i++) {
var v1 = series[0].data[i].y;
var v2 = series[1].data[i].y;
diffs.push( (Math.abs(v1-v2) / ((v1+v2) / 2)) * 100 );
}
$('#container').highcharts({
plotOptions: {
column: {
dataLabels: {
formatter: function()
{
return Highcharts.numberFormat(diffs[this.x])+"%";
}
}
}
}
series: series
// ...
});
See this JSFiddle demonstration of how it looks.
The Goal
I'm attempting to render a long series of data (around 200 ticks, from small float values like 1.3223) into a line chart.
The Issue
When I use a series of data that changes only a small amount (around 0.0001 every tick), the chart is rendered as very jagged (scissor like). I would like to somehow fix it to have a "saner" radius between each point on the graph.
A Good Example
On the other hand, when rendering higher values (around 1382.21) with bigger difference between ticks (from 0.01 to 0.05 +/-) the graph is rendered more smooth and aesthetically pleasing.
Edit: As user Arie Shaw pointed out, the actual low or high values don't make a difference and it remains an issue of representing small "monotonous" changes is a less jagged form.
The Code
var initChart = function(data, container) {
new Highcharts.Chart({
chart: {
type: "area",
renderTo: container,
zoomType: 'x'
},
title: {
text: ''
},
xAxis: {
labels: {
enabled: false
}
},
yAxis: {
title: {
text: ''
}
},
legend: {
enabled: false
},
color: '#A3D8FF',
plotOptions: {
area: {
fillColor: '#C6E5F4',
lineWidth: 1,
marker: {
enabled: false
},
shadow: false,
states: {
hover: {
lineWidth: 1
}
},
threshold: null
}
},
exporting: {
enabled: false
},
series: [{
name: "TEST",
data: data
}]
});
};
Both graphs, and sample data sets are presented in the following fiddle:
http://jsfiddle.net/YKbxy/2/
The problem you're experiencing is unavoidable: If you have a lot of small variations over time, the graph is going to appear jagged provided that you show each data point.
The key point is that last phrase.
One way to 'smooth out' the rough parts would be to average the data. For example:
var myData = []; //... Some array of data; assuming just numbers
var averageData = function (data, factor) {
var i, j, results = [], sum = 0, length = data.length, avgWindow;
if (!factor || factor <= 0) {
factor = 1;
}
// Create a sliding window of averages
for(i = 0; i < length; i+= factor) {
// Slice from i to factor
avgWindow = data.slice(i, i+factor);
for (j = 0; j < avgWindow.length; j++) {
sum += avgWindow[j];
}
results.push(sum / avgWindow.length)
sum = 0;
}
return results;
};
var var initChart = function(data, container) {
new Highcharts.Chart({
series: [{
name: "TEST",
data: averageData(myData, 2)
}]
});
});
This method also has the advantage that you could (potentially) reuse the function to compare the averaged data to the regular data, or even toggle between how much to average the data.
You can always use areaspline instead of area, see: http://jsfiddle.net/YKbxy/3/
why dont you treat you .00001 data as 1, so times 10000, and then write it in your legend like that.
You should even do that as a test, since if the chart looks fine then, it means there is a problem in the dataset numbers when you return it to normal, since high charts takes the difference between high and low...
Either you must approximate your data by only using a few decimal places or you must average out the values using something like:
var data = new Array(200);
var smallArray = new Array(5);
var averagedData = new Array(20);
for (var index=0; index<=averagedData.length; index++){
for(var i = 0; i<=smallArray.length; i++){
smallArray[i] = data[i + index * 5];
}
averagedData[index] = (smallArray[1] + smallArray[2] + smallArray[3] + smallArray[4] + smallArray[5])/smallArray.length;
}
Then you will only need to plot 20 averaged points on an array of 200 data points. You can change the values for what you need.
In the end the issue is in the frequency of the points or their plotting on yAxis.
When I provide more realistic positioning (e.g timestamp) it will look good.
Meaning that jaggedness is a result of the small changes over constant yAxis progression, which is most similar to nt3rp's answer