Chart Js - Limit points on graph without limiting data - javascript

I have a chart.js chart that needs to get plotted from a large number of points (say 1000). When I plot all these points it looks pretty bad, so I looked for a way to limit those. I used the method described here:
Limit data points with chart js
This works, but there is a big problem. It misses some important highs and lows of the 1000 points, basically plotting an incorrect chart.
Is there a way to not do this without missing some values? Basically plotting the chart with all the 1000 points but displaying like 30 on it.
I have tried a few plugins (decimation, downsample), but it seems they require vectors to work (like {x,y}). My data is an array of strings used for dates on the x axis and an array of float numbers used for prices on the y axis.
Thanks!

You can use the Chart.js inbuilt Data Decimation plugin.
Your base data consists of two arrays, one contains the date strings, the other contains the prices. These can easily be converted into an array of data points (objects having an x and y property each) as follows.
data: dateStrings.map((d, i) => ({ x: Date.parse(d), y: prices[i] }))
Further you must meet all the requirements of the decimation plugin. I also had to explicitly define options.parsing: false.
Please take a look at the runnable code and see how it works.
const dateStrings = [];
const prices = [];
// create sample data (dateStrings & prices)
const date = new Date();
date.setDate(date.getDate() - 100);
for (let i = 0; i < 100; i ++) {
date.setDate(date.getDate() + 1);
dateStrings.push(date.toISOString().substring(0,10));
prices.push(parseInt(Math.random() * 1000));
}
new Chart('myChart', {
type: 'line',
data: {
datasets: [{
label: 'My Dataset',
data: dateStrings.map((d, i) => ({ x: Date.parse(d), y: prices[i] })),
lineTension: 0.3,
borderColor: 'rgb(100, 100, 255)'
}],
},
options: {
parsing: false,
plugins: {
decimation: {
enabled: true,
algorithm: 'lttb',
samples: 20,
threshold: 20
}
},
scales: {
x: {
type: 'time',
time: {
unit: 'day',
displayFormats: {
day: 'D MMM yyyy'
},
tooltipFormat: 'D MMM yyyy'
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-adapter-moment/1.0.0/chartjs-adapter-moment.min.js"></script>
<canvas id="myChart" height="100"></canvas>

Related

How to draw 2d line time plot with chart.js

I want to draw line chart with chart.js but all example show single array as data I have 2d data. X is day (date object) and y is price of the product.
I have function like this that create data:
function format(data) {
const result = [];
Object.entries(data).forEach(([key, value]) => {
var data = {
label: key,
backgroundColor: 'rgba(0,0,0,0)',
data: value.map(data => ({y: data[0], x: new Date(data[1])}))
};
result.push(data);
});
return result;
}
from documentation I see that I can use object with {x,y} but this render only 2 points.
the data output look like this:
[
{
"label": "cyfrowe.pl",
"backgroundColor": "rgba(0,0,0,0)",
"data": [
{
"y": 9299,
"x": "2020-08-01T05:19:28.000Z"
},
{
"y": 9299,
"x": "2020-08-02T04:15:01.000Z"
},
my code for chart look like this:
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
console.log(ctx);
fetch('price.json')
.then(res => res.json())
.then(data => {
data = format(data);
console.log(data);
console.log(JSON.stringify(data.slice(0,1), true, 4));
var myLineChart = new Chart(ctx, {
type: 'line',
data:{ datasets: data }
});
});
In fact The time should be in days since I have prices for each date at about same hour.
Here is my demo that is not working. (the demo was updated with the answer code).
To make your code work, you could use a scatter chart and add the options showLine: true to each dataset.
In order to obtain formatted labels for each day, you need to define the x-axis as a time cartesian axis by adding the following configuration to chart options.
scales: {
xAxes: [{
type: 'time',
time: {
unit: 'day',
tooltipFormat: 'MMM DD'
}
}]
}
Please note that Chart.js internally uses Moment.js for the functionality of the time axis. Therefore you should use the bundled version of Chart.js that includes Moment.js in a single file.
When it comes to also show the labels in the tooltip, add the following to the chart options.
tooltips: {
mode: 'point',
callbacks: {
title: (tooltipItem, data) => data.datasets[tooltipItem[0].datasetIndex].label
}
}
Please take a look at your amended CodePen.

Price history with chart js

I have this kind of data array:
[
{
"price":"49",
"date":"21\/01\/2018"
},
{
"price":"30",
"date":"01\/01\/2018"
},
{
"price":"32",
"date":"15\/11\/2017"
}
]
Now I want to create a chart with chartjs, that shows me a price curve for the last 12 month.
I wrote this little script to generate me the past months:
function getPreviousMonths() {
var months = [];
for (i = 0; i < 12; i++) {
var month = moment().subtract(i, 'months').format('MMMM Y');
months.push(month);
}
return months.reverse();
}
How can I create the chartjs chart now? I looked in the docs, but got very confused when it comes to set dates within axes...
See http://www.chartjs.org/docs/latest/axes/cartesian/time.html for setting time scale on xAxes, then you have to convert your date field to a real date object:
xAxes: [{
type: 'time',
distribution: 'linear',
ticks: {
source: 'labels'
},
time: {
unit: 'month',
unitStepSize: 1,
displayFormats: {
'month': 'MMM'
}
}
}
Check this jsfiddle showing an example of time serie rendered as a line: https://jsfiddle.net/beaver71/9f9a2z88/
You have 2 separate your data array into 2 different arrays. One of dates (say dates_array) and another of price (say price_array). Then you just have to create new chart.
var chart = new Chart(element, {
type: 'line',
data: {
labels: dates_array,
datasets: [{
label: '# price',
data: price_array
}]
}
});
Here, element is the element in which chart will be shown. labels will be assigned the date array and data will be assigned price array. You can check this jsfiddle https://jsfiddle.net/j7gta8yn/

How to properly feed data to ChartJS with different number of x(labels) and y(data) points

I have a dataset that has data something like this
var data =[10,30,20,50,80,60,120,40,20,90,30,10];
var labels = [moment("12:00:00", 'HH:mm:ss'),moment("12:00:01", 'HH:mm:ss'),moment("12:00:02", 'HH:mm:ss'),moment("12:00:03", 'HH:mm:ss')];
I fed the data to chartJS like this
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Voltage Fluctuation',
data: [10,20,30,40,50],
borderWidth: 1
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
time: {
unit: 'minute',
displayFormats: {
hour: 'HH:mm:ss'
}
}
}]
},
}
});
However, I'm only getting data for the first four points i.e for each label.
Here's the JSFiddle
I want the data to be distributed for all the labels, in this case one data point for every (4/12)seconds and adjust the graph accordingly.
Is there any possible way I can achieve that without hardcoding it by converting the labels to milliseconds format?
I went ahead and hardcoded the entire thing by chopping seconds into milliseconds in order to create arrays of equal length

Zingchart plotting X-axis with Times, not Dates

I want to plot data with my X-axis representing Timespans (elapsed times), not actual dates.
I have a series with the following (string) values:
times: "00:00:00", "00:01:00", "00:10:00", "00:11:00"
I parse these values into (int)
times: 0, 6000, 60000, 66000
But when I draw the graph, the hour field is wrong. It shows "2" instead of "0" or "00". Minutes and seconds seem fine:
Here is my json code. I played with the Hours field, with no success:
// Description of the graph to be displayed
vm.chartJson = {
type: 'line',
scaleX: {
transform: {
type: 'date',
all: '%H:%h:%G:%g:%i:%s'
}
},
series: [{ values: data }]
};
How can I display the Hours field, while still manipulating TIMES and not Datetimes? How would that go if the total number of hours goes above 24? I would be okay with either displaying the total number of hours, or adding a day field. Example:
"124:22:01" or
"5:4:22:01"
Thank you
One issue I can note is we take time in milliseconds. So one minute = 60000 milliseconds. This could be the first thing off. Tack on a zero to the end of all your values.
The second issue, I cannot duplicate your times exactly because your local machine timezone is being used and I think mine is different. We have attributes that account for this, but it may not be necessary. Read further.
You cannot display 124 hours natively in the library. Depending on your input data you can just format and plot your own values with a custom x-axis label and tokens. Since you seem to already have the string format you want, why not just continue to use that?
var customLabels = ['00:00:00', '00:01:00', '00:10:00','00:11:00'];
var myConfig = {
type: 'line',
scaleX: {
labels: customLabels
},
tooltip: {
textAlign: 'left',
text: '%kl<br>OR<br>%data-dates: %v'
},
series: [
{
values: [475, 420, 400, 500],
dataDates: customLabels, // one for each point in values array
}
]
};
zingchart.render({
id: 'myChart',
data: myConfig,
height: '100%',
width: '100%'
});
html, body {
height:100%;
width:100%;
margin:0;
padding:0;
}
#myChart {
height:100%;
width:100%;
min-height:150px;
}
.zc-ref {
display:none;
}
<!DOCTYPE html>
<html>
<head>
<script src= "https://cdn.zingchart.com/zingchart.min.js"></script>
</head>
<body>
<div id="myChart"><a class="zc-ref" href="https://www.zingchart.com">Powered by ZingChart</a></div>
</body>
</html>
Relative Documentation:
demo link
tokens. Third one down in the grid is custom tokens starting with data-.
tooltips
scales
Here is what I did to solve the issue:
// Determine the format of x-axis
var format = '%i:%s';
if (data[data.length - 1][0] >= 3600000) format = '%G:%i:%s';
// Description of the graph to be displayed
vm.chartJson = {
type: 'line',
scaleX: {
transform: {
type: 'date',
all: format
}
},
series: [{ values: data }],
"utc": true,
"timezone": 0
};
I can't display Hours more than 24, so I could display days if needed.

flot date bar chart alignment askew

I am trying to make a chart that shows the number of search results my app pulls in for each day using flot. My code (BTW it uses underscore.js):
flotDataSet = _.countBy(resultSet, function(file) {
var exactDate = new Date(parseInt(file.get('start_utc'), 10));
//constructing a new date with 0 time so that all days get grouped together.
return new Date(exactDate.getUTCFullYear(), exactDate.getUTCMonth(), exactDate.getUTCDate()).getTime();
});
flotDataSet = _.map(flotDataSet, function(value, key) {
return [key, value];
});
$.plot(
$graphDiv,
[{
data: flotDataSet,
color: '#012D4C',
bars: { show: true, fillColor: '#024985', align: 'center', fill: 0.7, barWidth: DateUtil.msInDay/2 }
}],
{
grid: { color: '#012D4C' },
xaxis: {
mode: 'time',
tickSize: [1, 'day'],
autoscaleMargin: 0.001
}
}
);
Outputs something like this:
I really need the bars to center around the day. Any thoughts? Thanks!
Found the problem. My Date generation code was off because JavaScript's long-form Date(year, month, date [, time...]) constructor uses the local time or something weird like that. Anyway, I replaced the call to new Date(...).getTime() to be Date.UTC(...) and everything magically worked.
Seems like JavaScript's Date handling tends to be the root of most of my JS problems!

Categories