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!
Related
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>
I am using a line chart. I feed the data the following:
var scheduled = [[51,1700],[52, 1750],[1,1600],[2,1675]];
var actual = [[51,1320],[52, 1550],[1,1575],[2,1600]];
In the above the first number of each set is the week of the year and I am trying to show the last 4 months of data.
However, when the chart is drawn Flot charts re-sorts the data by the first value (lowest to highest) which creates all kinds of issues. Instead of 4 columns in the series there are now 52, and the lines are quite out of whack.
I don't see anything in the documentation that says this is supposed to happen, nor do I see anything that says I can prevent it. However, for the data to be meaningful, the data must not be re-ordered.
Is there a setting I'm unaware of that can stop this behavior?
Edit : Adding plot code
var plot = $.plot('#scheduled-actual-flot-line', [
{
label: 'Scheduled Hours',
data: scheduled,
lines: { show: true, lineWidth: 2, fill: true, fillColor: { colors: [{ opacity: 0.5 }, { opacity: 0.5 }] } },
points: { show: true, radius: 4 }
},
{
label: 'Actual Hours',
data: actual,
lines: { show: true, lineWidth: 2, fill: true, fillColor: { colors: [{ opacity: 0.5 }, { opacity: 0.5 }] } },
points: { show: true, radius: 4 }
}],
{
series: {
lines: { show: true },
points: { show: true },
shadowSize: 0 // Drawing is faster without shadows
},
colors: ['#afd2f0', '#177bbb'],
legend: {
show: true,
position: 'nw',
margin: [15, 0]
},
grid: {
borderWidth: 0,
hoverable: true,
clickable: true
},
yaxis: { ticks: 4, tickColor: '#eeeeee' },
xaxis: { ticks: 12, tickColor: '#ffffff' }
}
);
Flot takes the x values as numbers and displays / sorts them accordingly. If you don't want that, you can use the category mode (see this example and this fiddle with your data).
xaxis: {
//ticks: 12,
tickColor: '#ffffff',
mode: 'categories'
}
PS: 12 ticks are not possibly with your data, as there are only 4 datapoints defined.
That flot reads all data as numbers by default is described here in the documentation.
Flotr examples use a for loop to create random data, so the first index will always be sequential.
[[51,1700],[52, 1750],[1,1600],[2,1675]];
Your arrays show that flotr must be doing a sort on the array before painting the data sets as lines, bar-graphs or whatever.
I can only suggest you create a timestamp from the months and there's a time setting you can in flotr settings to format the dates as you want.
The other way is replace your anomalous data (months) with sequential indices:
var arr = [[51,1700],[52, 1750],[1,1600],[2,1675]];
for(var i=0; i<arr.length; i++) arr[1][0] = i;
Flot is doing exactly what it should do for a line chart (or any type of x-y graph). It's showing the last two points of your dataset on the left because 1 and 2 are indeed less than 51 and 52. I'm guessing that you're trying to show data that crosses a year boundary. You need to make the first two weeks of the second year later than the last two of the first. You could use actual dates instead of week numbers, in which case Flot would handle it fine. That would also give you more flexibility in labeling the x-axis. But as a quick fix, just add 52 to the second year's data, e.g.:
var scheduled = [[51,1700],[52, 1750],[53,1600],[54,1675]];
var actual = [[51,1320],[52, 1550],[53,1575],[54,1600]];
I am trying to graph a month out in flot (per day) or possibly every other day, have a tick.
To get my time values, i am doing this:
$date = strtotime($rowData[0]); // 2013-01-01
$myData = json_encode(array($date*1000, 1234));
// I Then echo it out as an array into the javascript var.
For some reason, i can change the line: minTickSize: [1, day] to 2 (label every other tick i think) and nothing will change. Also, the time values are not showing up as well? What am i doing wrong?
Here is a JSFIDDLE
Here is an update with every two days...is there a rendering issue? Also, why is Oct 10, the only date shown? : JSFIDDLE UPDATE
Here is what i have as far as the javascript goes:
var myData = [[1380603600000,0],[1380690000000,0],[1380776400000,0],[1380862800000,0],[1380949200000,0],[1381035600000,0],[1381122000000,0],[1381208400000,0],[1381294800000,0],[1381381200000,0],[1381467600000,0],[1381554000000,0],[1381640400000,1],[1381726800000,0],[1381813200000,0],[1381899600000,0],[1381986000000,0],[1382072400000,0],[1382158800000,0],[1382245200000,0],[1382331600000,0],[1382418000000,0],[1382504400000,0],[1382590800000,0],[1382677200000,0],[1382763600000,0],[1382850000000,0],[1382936400000,0],[1383022800000,0],[1383109200000,0],[1383195600000,0]];
var plot = $.plot($(".subsChart"),
[ { data: myData, label: "myData"} ], {
series: {
lines: { show: true,
lineWidth: 2,
fill: true, fillColor: { colors: [ { opacity: 0.5 }, { opacity: 0.2 } ] }
},
points: { show: true,
lineWidth: 2
},
grow: { active: true,
steps: 30
},
shadowSize: 0
},
grid: { hoverable: true,
clickable: true,
tickColor: "#DDD",
borderWidth: 1,
borderColor: "#DDD"
},
colors: ["#FF0000"],
xaxis: {mode: "time", timeformat: "%b %m", ticks: myData, minTickSize: [5, "day"]},
yaxis: {ticks:3, tickDecimals: 0},
tooltip: true,
});
UPDATE
When i remove the ticks: myData option, the labels do show up, but all of them are on the same date? Also, none of the points even land on the dates.
As #Smokie suggested, you need to remove the ticks option. When you provide ticks, Flot skips any tick generation that it would normally do and uses what you provided instead, which in this case is invalid.
Once you do that, you'll notice that you have labels, but they all show Oct. 10. That's because your date format is %b (short month name) %m (month number). I'd guess that what you actually want is %b %d.
Note that you probably won't get a tick for every day because, depending on the size of your window, they wouldn't all fit. That's why minTickSize is a minimum rather than an exact value; Flot will ensure that at least one day (or however many you specify) separates the ticks, but may choose to spread them out further to ensure that they all fit.
I'm trying to chart the duration of something using flot and having some issues. In my sql table I have two datetime columns and I am performing the below select to get the difference between them as a timestamp that works with flot (I think)
$p1Query = $db->rawQuery("SELECT AVG((synth.page2-synth.page1)) AS difference FROM (SELECT (UNIX_TIMESTAMP(pageoneloadtime))*1000 AS page1, (UNIX_TIMESTAMP(pagetwoloadtime))*1000 AS page2 FROM usersession) synth");
$p1Average = floor($p1Query[0]['difference']);
The javascript section of my chart looks like this.
var pageTimeData = [["Page 1",<?= $p1Average ?>],["Page 2",<?= $p2Average ?>],["Page 3",<?= $p3Average ?>],["Page 4",<?= $p4Average ?>]];
$.plot("#pageTimeChart", [ pageTimeData ], {
series: {
bars: {
show: true,
barWidth: 0.6,
align: "center"
}
},
xaxis: {
mode: "categories",
tickLength: 0
},
yaxis: {
mode: "time",
timeformat: "%M:%S",
minTickSize:[2,"seconds"]
}
});
But I can't figure out why my chart isn't displaying any bars. I have verified that I have loaded the necessary javascript files
I realized it was just a simple type. minTickSize:[2,"seconds"] needs to be minTickSize:[2,"second"]
Highcharts has an example using irregular time intervals, I want to do the same on highstock howeverthere is no parameter for the xAxis-type.
JSFiddle code
My options are
$(function() {
$.getJSON('http://184.173.195.228/~ktwsn/temp2.php?action=get_sensor&sensor_serial%5B%5D=3B74F41400000069&sensor_serial%5B%5D=3BB2FA14000000E6&sensor_serial%5B%5D=3B91F11400000079&sensor_serial%5B%5D=3BC7F114000000E5&sensor_serial%5B%5D=3BC0F314000000E3&callback=?',
{action: "get_sensor"},
function(data) {
var seriesOptions = [];
$.each(data, function(key, val) {
seriesOptions.push({
name: val.name,
data: val.samples,
marker : {
enabled : true,
radius : 3
}
});
});
// create the chart
chart = new Highcharts.StockChart({
chart: {
renderTo: 'container',
},
xAxis: {
type: 'datetime'
},
rangeSelector: {
selected: 4
},
series: seriesOptions
});
});
});
Found it. The nomenclature between highcharts and highstock are different.
For Hightstock it's
xAxis: {
ordinal: false
}
The company really needs to combine highcharts and highstock. A single API documentation would be easier to follow.
I would verify that the output data is actually using the same time stamp.
Many cases like this include time stamps for the same date, but different times, which will always result in different x axis placement.
(I know that may be an obvious check, but I have seen it many times...)