I'm currently trying to write a short program that captures data on mouse movements, scrolling and keyboard clicks from my laptop. This data is then saved in a firebase real time database and deployed to a simple webpage.
I'm using chart.js to plot this data on the page, but when I plot the data, the first adn last points get connected together (see image). I'm having to use the scatter plot as opposed to the line plot as I want the values to be distributed relatively along the x axis. The line chart.js plot places them with equal spacing (which looks weird for a time series bit of data!).
As this is a time series data set this is clearly quite annoying! Anyone have any ideas how I can stop this from happening??
Any help much appreciated!
//This code is taken from a larger script, I'm fairly sure these are all the relevant parts
//This is a js file which manages 3 chart.js charts on a webpage.
//This function generates the chart template
function chartTemplateCreation(InputLabel)
{
const chartTemplate = {
type: 'scatter',
data: {
datasets: [{
label: InputLabel,
data: [],
//backgroundColor: 'rgba(255, 205, 210, 0.5)'
}]
},
options: {
legend: {
display: false
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [{
ticks: {
suggestedMin: 7,
suggestedMax: 22
}
}]
}
}
}
return chartTemplate
}
//These values create links to the html positioning of the charts
const blankMouseChart = document.getElementById('mouse-chart').getContext('2d')
const blankKeysChart = document.getElementById('keys-chart').getContext('2d')
const blankScrollChart = document.getElementById('scroll-chart').getContext('2d')
//Charts are created
const mouseChart = new Chart(blankMouseChart,chartTemplateCreation('mouse'))
const keysChart = new Chart(blankKeysChart, chartTemplateCreation('keys'))
const scrollChart = new Chart(blankScrollChart, chartTemplateCreation('scroll'))
//This function is used to add to new data to the chart
function addDataScatter(chart, time, value)
{
//chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) =>
{
dataset.data.push({x:time,y:value})
})
chart.update()
}
//collect the firebase reference
const dataRefTenMin = firebase.database().ref(date + "/10min_culmalative/")
//initially add all the historic values from today
dataRefTenMin.once("value").then(function(snapshot)
{
snapshot.forEach(function(childSnapshot)
{
var childData = childSnapshot.val()
var decTime = childData.decTime
addDataScatter(mouseChart, decTime, childData.mouse)
addDataScatter(scrollChart, decTime, childData.scroll)
addDataScatter(keysChart, decTime, childData.keys)
})
})
//then live update the graph by adding data points every time a child is added to the firebase realtime database
dataRefTenMin.on("child_added", function(data)
{
var newData = data.val()
var decTime = newData.decTime
addDataScatter(mouseChart,newData.decTime, newData.mouse)
addDataScatter(scrollChart,newData.decTime, newData.scroll)
addDataScatter(keysChart,newData.decTime, newData.keys)
})
Ah! Figured out the answer by mistake! Accidently commented out a section of code and it now works. All you have to do is remove the section which was meant to add the historic data. As it turns out, both:
dataRefTenMin.once("value").then(function(snapshot)
{
snapshot.forEach(function(childSnapshot)
{
var childData = childSnapshot.val()
var decTime = childData.decTime
addDataScatter(mouseChart, decTime, childData.mouse)
addDataScatter(scrollChart, decTime, childData.scroll)
addDataScatter(keysChart, decTime, childData.keys)
})
})
and
dataRefTenMin.on("child_added", function(data)
{
var newData = data.val()
var decTime = newData.decTime
addDataScatter(mouseChart,newData.decTime, newData.mouse)
addDataScatter(scrollChart,newData.decTime, newData.scroll)
addDataScatter(keysChart,newData.decTime, newData.keys)
})
add historic data to the chart. Hence the error comes as they plot the data over each other (I think).
If any one else has a similar problem, remove the first one of the two portions of code above! Then the chart will both plot the historic data and update live.
Related
Is there a way to set a different color to a datapoint in a Line Chart if its above a certain value?
I found this example for dxChart - https://stackoverflow.com/a/24928967/949195 - and now looking for something similar for ChartJS
In updating to version 2.2.2 of ChartJS, I found that the accepted answer no longer works. The datasets will take an array holding styling information for the properties.
In this case:
var pointBackgroundColors = [];
var myChart = new Chart($('#myChart').get(0).getContext('2d'), {
type: 'line',
data: {
datasets: [
{
data: dataPoints,
pointBackgroundColor: pointBackgroundColors
}
]
}
});
for (i = 0; i < myChart.data.datasets[0].data.length; i++) {
if (myChart.data.datasets[0].data[i] > 100) {
pointBackgroundColors.push("#90cd8a");
} else {
pointBackgroundColors.push("#f58368");
}
}
myChart.update();
I found this looking through the samples for ChartJS, specifically this one: "Different Point Sizes Example"
With recent versions of chart.js I would recommend doing this with scriptable options.
Scriptable options give you an easy way to vary the style of a dataset property (e.g. line point colour) dynamically according to some function you provide. Your function is passed a 'context' object that tells it the index and value of the point etc. (see below).
Most chart properties can be scripted; the dataset properties for each chart type tell you the exact list (e.g. see here for line chart).
Here is how you might use scriptable options on a line chart (based on the example in the docs). On this chart negative data points are shown in red, and positive ones in alternating blue/green:
window.myChart = Chart.Line(ctx, {
data: {
labels: x_data,
datasets: [
{
data: y_data,
label: "Test Data",
borderColor: "#3e95cd",
fill: false,
pointBackgroundColor: function(context) {
var index = context.dataIndex;
var value = context.dataset.data[index];
return value < 0 ? 'red' : // draw negative values in red
index % 2 ? 'blue' : // else, alternate values in blue and green
'green';
}
}
],
}
});
The context object passed to your function can have the following properties. Some of these won't be present for certain types of entity, so test before use.
chart: the associated chart
dataIndex: index of the current data
dataset: dataset at index datasetIndex
datasetIndex: index of the
current dataset
hover: true if hovered
Here's what worked for me (v 2.7.0), first I had to set pointBackgroundColor and pointBorderColor in the dataset to an array (you can fill this array with colours in the first place if you want):
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
data: dataPoints,
pointBackgroundColor: [],
pointBorderColor: [],
}
]
}
});
Then you can monkey with the colours of the points directly:
myChart.data.datasets[0].pointBackgroundColor[4] = "#cc00cc";
myChart.data.datasets[0].pointBorderColor[4] = "#cc0000";
myChart.update();
Some other properties to play with to distinguish a point: pointStrokeColor (it apparently exists but I can't seem to get it to work), pointRadius & pointHoverRadius (integers), pointStyle ('triangle', 'rect', 'rectRot', 'cross', 'crossRot', 'star', 'line', and 'dash'), though I can't seem to figure out the defaults for pointRadius and pointStyle.
For chartjs 2.0 see this following answer.
Original answer below.
Good question regarding ChartJS. I've been wanting to do a similar thing. i.e dynamically change the point colour to a different colour. Have you tried this below. I just tried it and it worked for me.
Try this:
myLineChart.datasets[0].points[4].fillColor = "rgba(000,111,111,55)" ;
Or Try this:
myLineChart.datasets[0].points[4].fillColor = "#FF0000";
Or even this:
myLineChart.datasets[0].points[4].fillColor = "lightgreen";
Then do this:
myLineChart.update();
I guess you could have something like;
if (myLineChart.datasets[0].points[4].value > 100) {
myLineChart.datasets[0].points[4].fillColor = "lightgreen";
myLineChart.update();
}
Give it a try anyway.
Just adding what worked for me in the new 2.0 version.
Instead of:
myLineChart.datasets[0].points[4].fillColor = "lightgreen";
I had to use:
myChart.config.data.datasets[0].backgroundColor[4] = "lightgreen";
Not sure if that's because of a change in 2.0 or because I'm using a bar chart and not a line chart.
If you initialize the myChart in this manner,
var myChart = new Chart(ctx, {
type: 'line',
data: {
you have to change line color by this code
myChart.data.datasets[0].backgroundColor[0] ="#87CEFA";
If you initialize the myChart in this manner,
myBar = new Chart(ctx).Line(barChartData, {
you have to change line color by this code
myLineChart.datasets[0].points[4].fillColor = "#FF0000";
Dynamically updating a chartjs chart and creating the labels in an array format (["A","B","C"]). However chartjs doesn't accept a push of the label array unless it is in the format "A","B","C" (without brackets). Anyone else experience this or have I misunderstood? Se code below. Produces this
Instead of this (ok when adding labels as chart.data.labels.push("A","B","C","D")
var chart = new Chart(document.getElementById("element"), {
type: 'bar',
options: {
legend: {
display: false
}
}
});
//PUSH DATA TO GRAPH.
var verserier = [];
var veromslperserie = [];
var stat = seriestat(); //function to retrieve data for labels
$.each(stat, function(i, item) {
verserier.push(i);
veromslperserie.push(item["omsl"]);
});
chart.data.labels.push(verserier); //error occurs here
chart.data.datasets.push({
label: "Omsl",
data: veromslperserie,
backgroundColor: colorarray,
});
chart.update();
When you push outside of the loop you are actually pushing verserier into position [n], which in this case is 0.
If you do not add values again you can do
chart.data.labels = verserier
I don't have the explanation of the "why", but here's a workaround :
var data_array = new Array();
// This doesn't work (but with no error on my side, appart from visually wrong labels)
myChart.data.labels.push(data_array);
// But this works
for(i=0;i<data_array.length;i++)
{
myChart.data.labels.push(data_array[i]);
}
I need to stack the bars in the bar chart to the left as per the image attached
is there a way to do that in chart.js?
EDIT:
Just to clarify what I am looking for.
The number of the bars in my chart is dynamic, if there are 10 of them then chart looks fine but if there are only 2 they each take 50% of the width of the chart (see picture #2)
I want both of those bars to be exactly the same width as if there were 10 of them and be stacked to the left.
One option that I'm currently considering is just to add (10 - no of bars) bars with 0 value so that they won't be visible. But I'm hoping that there is a better solution.
Thanks.
Instead of creating a graph with 10 empty bar charts, then populate it with your values, I think it would be better to add empty values to reach the number of 10 (same idea though).
If you take a look in the Chart.js documentation, you can see that you can create plugins for your charts and graphs. Plugins are extremely useful when editing your chart (instead of just hardcoding what you want) since they allow you to handle what is happening while creating your charts.
For instance : beforeUpdate, afterDraw are some of the events you can handle with plugins.
Now, you must know that the chart object contains a lot of information :
If you want to edit a global option, you'd check chart.config.options
If you want to edit a specific chart, you'd check chart.config.data
In our case, we'd need the data attribute.
If you take a deep look in it, you'd see that the number of values come from the lengh of both data.labels and data.datasets[n].data (n being the nth dataset).
Now that you know what to do, and how to do it, you can do it.
I still made a quick example of what you are looking for :
var ctx = document.getElementById("myChart").getContext("2d");
// stores the number of bars you have at the beginning.
var length = -1;
// creates a new plugin
Chart.pluginService.register({
// before the update ..
beforeUpdate: function(chart) {
var data = chart.config.data;
for (var i = data.labels.length; i < data.maxBarNumber; i++) {
length = (length == -1) ? i : length;
// populates both arrays with default values, you can put anything though
data.labels[i] = i;
data.datasets[0].data[i] = 0;
}
},
// after the update ..
afterUpdate: function(chart) {
console.log(chart);
var data = chart.config.data;
if (length == -1) return;
// prevents new charts to be drawn
for (var i = length; i < data.maxBarNumber; i++) {
data.datasets[0]._meta[0].data[i].draw = function() {
return
};
}
}
});
var data = {
// change here depending on how many bar charts you can have
maxBarNumber: 10,
labels: [1, 2, 3, 4, 5, 6, 7],
datasets: [{
label: "dataset",
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1,
data: [65, 59, 80, 81, 56, 55, 40],
}]
};
var myBarChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
scales: {
xAxes: [{
display: false
}],
yAxes: [{
stacked: true
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.2.1/Chart.min.js"></script>
<canvas id="myChart" width="400" height="400"></canvas>
You can use Array.prototype.reverse() to reverse the data if it is currently stacked to the right. Otherwise you will need to use some type of sorting to go from largest data to smallest data.
I am working on Google Visulization charts .
I am trying to draw candlestick charts with sample data , the issue i am facing is that the falling color and raising color are being represented in single color only
This is my code
var mydata = [['13-Oct',1109.95,1132,1097.95,1113.45],['14-Oct',1113.45,1117,1095.6,1101.15],['15-Oct',1116,1132,1092.1,1129.2],['16-Oct',1130,1182.4,1130,1170.3],['19-Oct',1174,1182.2,1144.5,1162.15]];
function drawChart() {
var data = google.visualization.arrayToDataTable(mydata, true);
var options = {
legend:'none',
colors:['red','brown'],
candlestick: {
fallingColor:{ fill: "orange", strokeWidth:0.5,stroke:'black'},
risingColor:{fill:"yellowgreen",strokeWidth:0.5,stroke:'black'}}
};
var chart = new google.visualization.CandlestickChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
and this is my fiddle
http://jsfiddle.net/pdmpb9w1/7/
Could you please let me know how to resolve this issue ??
Like Kevin Brown said, the problem is with your data. Your data should be structured like so:
var mydata = [
//category start err start end end err
['13-Oct', 1109.95, 1132, 1097.95, 1113.45],
['14-Oct', 1113.45, 1117, 1095.6, 1101.15],
['15-Oct', 1116, 1132, 1092.1, 1129.2],
['16-Oct', 1130, 1182.4, 1130, 1170.3],
['19-Oct', 1174, 1182.2, 1144.5, 1162.15]
];
As you can see, all the values in the end column are smaller than the values in the start column, so all the candlesticks are colored as though falling.
On top of that, all your start err values are smaller than your start value, so you're not seeing the error/tolerance line because its hidden behind the bar. Vice versa for the end values.
I have no idea how your data should be ordered, but if I had to guess it would be something like this:
var mydata = [
// note the first two numbers are swapped with the second two
['13-Oct', 1097.95, 1113.45, 1109.95, 1132],
['14-Oct', 1095.6, 1101.15, 1113.45, 1117],
['15-Oct', 1092.1, 1129.2, 1116, 1132],
['16-Oct', 1130, 1170.3, 1130, 1182.4],
['19-Oct', 1144.5, 1162.15, 1174, 1182.2]
];
Which would render the following:
JSFiddle
I am trying to use Chart.js Bar Chart to display a set of data.
My data is weekly based so to my method I send the year and week and get the data back in 3 columns; Product, Area and Amount.
What I want is to have to Products horizontaly and in each Product I want different bars for each Area and offcourse the Amount verticaly. (Bonus: If an Area nothing in that product it should not be shown in that particular Product)
The problem is that the number of Products and the number of Areas can vary from each week. And I can't seem to find a good way to loop through the data and create the datasets the way chart.js wants.
Also tried using Underscore.js to group it but the fact that the each Area doesn't always have an amount for a spesific product seems to be causing some issues.
So I guess you have to loop through the data and map that data to another predefined array for each Area so it can match this structure somehow??
Also open for other Chart plugins, but really liked how Chart.js animates the data. And if I get this working I can probably figgure out an update method for when you change week.
To get the labels i can f.ex do this:
$.ajax({
....
success: function (d) {
var a = _.groupBy(d.data, function (d) { return d.Product });
var labels = [];
$.each(a, function (i, value) {
labels.push(i);
});
}
});
With data in this format
var myJSONData = [
{
Product: 'P1',
Area: 'A1',
Value: 12
},
...
]
You can use this function to convert it into the format Chart.js requires
var data = {
labels: [],
datasets: []
}
var colors = ['Red','Blue','Green', ...] // add as many colors as there will be areas (maximum)
myJSONData.forEach(function (e) {
// create labels
var labelIndex = data.labels.indexOf(e.Product)
if (labelIndex === -1) {
labelIndex = data.labels.length;
data.labels.push(e.Product);
// dummy entries for each dataset for the label
data.datasets.forEach(function (dataset) {
dataset.data.push(0)
})
}
// get the area dataset
var area = data.datasets.filter(function(area){
return (area.label === e.Area);
})[0]
// otherwise create it
if (area === undefined) {
area = {
label: e.Area,
// create a dummy array with an entry for each of the existing labels
data: data.labels.map(function () {
return 0;
}),
fillColor: colors[data.datasets.length]
};
data.datasets.push(area)
}
// set the value
area.data[labelIndex] = e.Value;
})
and use that to display the chart.
Fiddle - http://jsfiddle.net/jt4Lqkn3/
(Bonus: If an Area nothing in that product it should not be shown in
that particular Product)
You can't change any configuration to do this - there will be a space left for each series.
However you might want to set the strokeColor to a transparent value (e.g. strokeColor: "rgba(0, 0, 0, 0)", just below the fillColor line) and set the barStrokeWidth option to 0, so that 0 values don't show up at all on the chart (otherwise there will be thin line shown)
new Chart(ctx).Bar(data, {
barStrokeWidth: 0,
});