Google Chart 2 Lines with not the same number of data - javascript

I'm trying to create a dynamic Google Chart line.
I have 2 lines in a range of a day (from 00h00 to 23h50 - 0am to 12pm) with a point for each lines every 10 minutes (00h00, 00h10, 00h20 etc...).
So for each lines I have 144 values for a day (1*6*24 - 6 / hours because I have 1 every 10 minutes).
For the first line, I have all the values at the start of the day (144 values) but for the other one, I'm drawing it according the current hour (exemple if it's 01:00 - 1am, I have only 6 values).
So i'm trying to draw the lines but I have a problem if my secondes lines doesn't have 144 values.
Here is the function to draw the chart with the different lines
function drawChart(chart_div) {
var data = get_data();
var chart = new google.visualization.ComboChart(document.getElementById(chart_div));
chart.draw(data, {
height: 300,
width: 1000,
chartArea:{left:40,top:5,width:"100%",height:"80%"},
seriesType: 'line',
series: {
0: {
color: '#0080FF'
},
1: {
color: '#bdbdbd',
enableInteractivity: false
}
},
tooltip: {
isHtml: true
},
legend: {
position: 'none'
},
vAxis: {
gridlines: {
color: '#e5e5e1'
}
},
hAxis: {
viewWindow: {
min: 0,
max: 144
},
ticks: [0, 24, 48, 72, 96, 120, 144] // display labels every 24
}
});}
Here is the code to launch the function to draw the chart
google.load('visualization', '1', {'packages':['corechart']});
google.setOnLoadCallback(function() {
drawChart("chart_one");
});
And here is the function to generate the data (They are currently randomly generate but I'll take them from DB later)
function get_data(){
var data = new google.visualization.DataTable();
data.addColumn('string', 'Time');
data.addColumn('number', 'Heartbeat (%)');
data.addColumn('number', 'Heartbeat moy (%)');
var total = 0;
for(i = 0; i < 24; i++){
for(j=0; j <= 5; j++){
var num = "";
if(i < 10){
num += "0";
}
if(total < 98){
data.addRows([[num + i + "h"+j+"0", Math.floor((Math.random() * 10) + 91), Math.floor((Math.random() * 10) + 91)]]);
} else {
data.addRows([[num + i + "h"+j+"0", "", Math.floor((Math.random() * 10) + 91)]]);
}
total++;
}
}
return data;
}
So basically I would have something like that :
But if I'm setting the secondes line values to "", it say
Error: Type mismatch. Value does not match type number in column index 2
Does anyone have any idea how to bypass the value for the seconde lines if I doesn't have it ?

Use null instead of an empty string "" for points that don't have any data

Related

how to get dynamic step size in react-chartjs-2

I am using Chart.js to draw graphs in typescript.
I want to get a dynamic weight and bring a minimum and a maximum. And with maxTicksLimit as 5, I want to keep 5 Ticks no matter what data comes in.
The decimal point of body weight is taken to the first decimal point.
ex) 50.3
I want to show the difference between minimum and maximum as much as possible.
please help me!!!
ex1) maximum weight: 74.5, minimum weight: 71
result
Y Axis maximum weight: 76 , Y Axis minimum weight: 71
ex1 result image
enter image description here
ex2) maximum weight: 76.9, minimum weight: 62
result
Y Axis maximum weight: 76 , Y Axis minimum weight: 61
ex2 result image
enter image description here
The beforeBuildTicks callback also works to create a dynamic tick step size. Here is the code:
{
maintainAspectRatio: false,
tooltips: {
mode: 'label',
position: 'nearest',
},
scales: {
xAxes:[
{...}
],
yAxes: [
{
position: 'left',
id: 'y-axis-0',
scaleLabel: {
display: true,
labelString: 'label string goes here',
fontSize: 16,
},
ticks: {
beginAtZero: true,
stepSize: .25,
},
beforeBuildTicks: function(axis) {
if (axis.max >= 17) {
axis.options.ticks.stepSize = 1;
} else if (axis.max >= 10) {
axis.options.ticks.stepSize = .5;
}
}
}
]
}
}
I solved this problem in the following way.
There was a callback function of afterBuildTicks in chart.js
※ chart.js documents link.
It becomes possible to customize the ticks.
//... middle code skip
const getChartMaxAndMin = (
maxValue,
minValue
): { max: number; min: number } => {
// Convert all decimal places to integers
let max = Math.ceil(maxValue);
let min = Math.floor(minValue);
// a multiple of 5
const MULTIPLES = 5;
const DIVIDED_REMAINING_VALUE_LIMIT = 3;
// Maximum to Minimum difference
const diff = max - min;
const diffDividedRemainingValue = diff % MULTIPLES;
const remainingValue =
MULTIPLES *
(diffDividedRemainingValue > DIVIDED_REMAINING_VALUE_LIMIT
? Math.floor(diff / MULTIPLES) + 2
: Math.floor(diff / MULTIPLES) + 1) -
diff;
if (remainingValue % 2 !== 0) {
max = max + Math.floor(remainingValue / 2) * 2;
min = min - Math.floor(remainingValue % 2);
return { max, min };
}
max = max + remainingValue / 2;
min = min - remainingValue / 2;
return { max, min };
};
const customizedAxesTicks = (axis) => {
const EQUAL_PARTS = 5; // set 5 parts
const max = axis.max;
const min = axis.min;
const steps = (max - min) / EQUAL_PARTS;
const ticks = [];
for (let i = min; i <= max; i += steps) {
ticks.push(i);
}
axis.ticks = ticks;
return;
};
const {max, min} = getChartMaxAndMin(68,57);
const chartOptions = {
//... code skip
scales: {
yAxes: [
{
//... code skip
ticks: {
max: max,
min: min
},
afterBuildTicks: customizedAxesTicks,
//... code skip
};
I would appreciate it if you could let me know if there is a better way😀

Spurious point is being added in the end C3 charts

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.

Highcharts sets default value for blank data

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/

Start live Flot Chart at 0

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!

Defining 'step' attribute in Zingchart renders the graph unusable

Scenario:
I am able to get a status graph with Zingchart, using a stacked hbar and plotting data intervals where the status of an element doesn't change:
However, I would like to have more resolution in the axis that provides the time.
Problem:
I am not able to increase the number of ticks in the y-axis (this is an stacked hbar graph, so the usual x-axis is actually the y-axis).
If I introduce step:"1hour", in the scaleY field I get back to epoch 0 and this is what I obtain:
Question:
What am I doing wrong? I would like either:
To have more time references in the axis of the time.
Or (and this may be another question) to be able to know on mouse over (in the tooltip?) at what time I am. Since I am plotting time increments I lost the actual time on where the change of status happens. Is there a direct way to do this in this particular situation? Or do I have to carry another data-customValue for the actual date of the start / end of the interval?
In the code, I jump from epoch 0 to the time I start having data, and define minValue to plot as the first epoch I have data minus one second.
This is the working code (no step attribute defined):
<!DOCTYPE html>
<html>
<head>
<script src="/home/eballes/Work/backup/zingchart_test/zingchart_2.3.3/zingchart.min.js"></script>
<script>
zingchart.MODULESDIR = "/home/eballes/Work/backup/zingchart_test/zingchart_2.3.3/modules/";
</script>
<style></style>
</head>
<body>
<div id='myChart'></div>
<script>
var myConfig = {
type: "hbar",
utc:true,
title: {
text: 'Status'
},
scaleY:{
transform:{
type:'date',
all:"%d/%M/%Y\n%H:%i:%s",
},
minValue:1456693864000,
zooming:true,
label:{
"text": "Time",
},
tick:{
"line-color":"black",
"line-width":"2px",
"size":8,
},
maxItems:10,
itemsOverlap:true,
item:{
"font-size":10
},
},
scaleX:{
label:{
"text": "Item",
},
},
plot:{
stacked:true,
exact:false,
barWidth:10,
rules:[
{
rule:'%data-customValue == 0',
alpha:0, // Transparent
},
{
rule:'%data-customValue == 1',
backgroundColor:'#0000FF' // Dark Blue
},
{
rule:'%data-customValue == 2',
backgroundColor:'#00FFFF' // Light Blue
},
{
rule:'%data-customValue == 3',
backgroundColor:'#FF7F50' // Orange
},
{
rule:'%data-customValue == 4',
backgroundColor:'#FFFF00' // Yellow
},
{
rule:'%data-customValue == 5',
backgroundColor:'#EE82EE' // Purple
},
{
rule:'%data-customValue == 6',
backgroundColor:'#00FF00' // Green
},
{
rule:'%data-customValue == 7',
backgroundColor:'#FF0000' // Red
},
]
},
tooltip:{
jsRule:"CustomFn.formatTooltip()",
},
series : [
{
values : [
[1,1456693864000],
[2,1456693864000],
.... // From 1 to 36
[36,1456693864000],
],
'data-customValue':[0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,0,0,0,0],
},
{
values : [
[11, 32000], [12, 10270000], [14, 5033000], [18, 79000], [20, 43000], [24, 76000], [26, 4043000], [8, 33000], [9, 63000],
],
'data-customValue':[2, 6, 6, 0, 1, 2, 6, 1, 0, ],
},
{
values : [
[14, 3000], [19, 20000], [26, 1000], [8, 86000], [9, 13000],
],
'data-customValue':[2, 2, 2, 0, 1, ],
},
// All intervals
]
};
CustomFn = {};
CustomFn.formatTooltip = function(p){
var hours = Math.floor(p.value / 3600000);
var minutes = Math.floor((p.value % 3600000) / 60000);
var seconds = (p.value % 60000)/1000;
var tooltipText = "Item: " + p.scaletext + "\nDuration: ";
var hoursText = (hours == 0) ? "" : (hours + "h ");
var minutesText = (minutes == 0) ? "" : (minutes + "m");
var secondsText = (seconds == 0) ? "" : (seconds + "s");
tooltipText = tooltipText + hoursText + minutesText + secondsText + "\n";
tooltipText = tooltipText + "Value: " + p['data-customValue'];
var alpha = 70;
// We don't want any tooltip for the time-jump
if (hours > 300000) {
tooltipText = "";
alpha = 0;
}
return {
text : tooltipText,
backgroundColor : "#222",
alpha: alpha,
}
};
zingchart.render({
id : 'myChart',
data : myConfig,
height: 670,
width: '100%'
});
</script>
</body>
</html>
This is the link to the Plunker with the whole example, not working. If you remove the line step:"1hour", it will work.
Funny enough, step:20 works fine. But I would prefer to have my steps in a normal way, like hourly.
Note: Just to provide some more context, this question is the continuation of this other previous question.
Full Disclosure, I'm a member of the ZingChart team.
The first problem being
step:'1hour'
That string value is being interpreted by our library as the value 0. This is because the string '1hour' does not match any of our keywords nor does it evaluate to a Number type. We are not simply using parseInt so it does not evaluate to 1.
If you want an hourly step you would do the step count in milliseconds. This is documented in our scales page.
step:3600000
For display the scale value we do have a tokens list which allows you to add values from your scale to your tooltip. In your customFn.formatTooltip you would add this line.
...
tooltipText = tooltipText + "Value: " + p['data-customValue'];
tooltipText += '<br> %vv'
The %vv is in our tokens list and will grab the transformed value off the y-axis. If you did %vt it would give you the millisecond value.

Categories

Resources