I'm trying to draw a stacked (area) line chart using C3.
My code, as it stands, allows me to create a line chart without stacking :
var chart = c3.generate({
data: {
x: 'x',
url: 'GeneratedData.csv',
type: 'area',
/* groups: [ ['data1', 'data2'] ] */
},
axis : {
x : {
type : 'timeseries',
tick : {
format : "%y-%m-%d"
}
}
}
});
My problem is that the data is generated in such a way that I do not know the name of the columns in advance, so I cannot set their type or group them
(hence the comments around groups: [ ['data1', 'data2'] ])
My CSV looks something like this :
x,LT62Ag,5NwafDw,Pac0dA
2017-01-22,85797,145417,626803
2017-01-23,71837,105246,440776
2017-01-24,77650,108834,442359
...
2017-03-31,87359,102618,467113
How should I proceed to create the groups from the dynamic data to stack the charts ?
You could try adding this to your chart declaration, it'll pull out the names of the data series (apart from x) and turn them into one big group:
onrendered: function () {
var seriesNames = this.data.targets.map (function (d) {
return d.id;
}).filter (function (sname) {
return sname !== "x";
});
this.api.groups ([seriesNames]);
},
Ideally it should be done with the 'oninit' declaration rather than the groups reset on every rendering, but there seems to be some sort of bug that makes the bars go 1 pixel wide when you do that...
I guess a flag that decides whether the groups have already been set could be employed though...
https://jsfiddle.net/1bb60dd9/
Related
Let's take a look at the simple chart data:
it has (x,y) pairs:
x
y
0
-3
1
2
2
7
3
8
4
15
5
0
The idea is to create a basic line chart, using VueJS in my case, but the idea can be generalized to JavaScript.
I have a series array of objects, where each object has x and y coordinates:
series = [
{
x: 0,
y: -3
},
{
x: 1,
y: 2
},
...
]
This series is part of options object:
const options = {
chart: {
type: 'line'
},
series: series
}
const chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
And the chart is rendered.
Now, let's say I want to append the data to that chart - add 2 new (x,y) pairs
const newData = [
{
x: 6,
y: 20
},
{
x: 7,
y: -10
}
]
chart.appendData([{ data: chartData }])
I would also like that newly appendedData has, for example, different color, fill, or something else - so newly added data displays differently than old data.
Feel free to point me to documentation if I missed anything, but I searched through apex chart methods, and the only thing that looks remotely close to this would be inside
updateOptions() method, the redrawPath flag (updateOptions docs:
When the chart is re-rendered, should it draw from the existing paths
or completely redraw the chart paths from the beginning. By default,
the chart is re-rendered from the existing paths
In order to style the new data differently, you'll want to put these data points into a different series using the appendSeries method:
const newData = [
{
x: 6,
y: 20
},
{
x: 7,
y: -10
}
]
chart.appendSeries({
name: "series-2", // optional
data: newData
})
Most of the styling in ApexCharts is done based on series (more specifically seriesIndex). So by placing the new data in a separate series you'll be able to style this second series using an array of, for example, colors.
You could either specify the color you would like to use as you append the new series of data using the updateOptions method you mention, or you can specify it in advance.
chartOptions: {
colors: ["#546E7A", "#E91E63"],
}
When working with "Numeric paired values in XY properties", the xaxis type also has to be explicitly set to numeric:
chartOptions: {
xaxis: {
type: 'numeric',
},
}
The tricky bit comes when you want to add more data a second time (or third, more time). There are two approaches I can think of here:
Shuffle the existing data across to the original series (append series-2 to series-1) - and overwrite series-2 with your new data. You don't need to edit the colors in chartOptions.
You could shuffle the colors along. If you want all "old" data to have the same color, simply prepend the colors array with your base color every time you add a new series. Or if you want each series to have a different color, just append a color every time you add a new series.
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";
I'm trying to create a horizontal stacked bar chart with labels on the bars themselves (currently using dataLabels) as well as labels just above the bars.
Here is a JSFiddle I've created that somewhat resembles what I am trying to accomplish. In this example I've placed the labels using plotLine labels, but I'm having a difficult time figuring out how to place the bars correctly in my real use case as I get the data for the bars through Ajax calls.
JSFiddle
I've experimented with stackLabels, plotLine labels as well as plotBand labels but to no avail. How can I place labels above the bars correctly every time?
I would use plotBands instead of plotLines - you can better control the alignment within the band, which can be set to cover the same portion of the graph as the bar itself.
An example.
Using your series data:
var series = [{
name: 'John',
data: [5]
},
{
name: 'Jane',
data: [2]
}, {
name: 'Joe',
data: [3]
}]
We can loop through and build a plotBands array from the same data:
var bands = [],
prevData = 0;
$.each(series, function(i,ser) {
var point = ser.data[0],
name = ser.name;
bands.push({
from: prevData,
to: (prevData + point),
color: 'rgba(255,255,255,0)',
label: {
text: name,
align: 'center'
}
});
prevData = (prevData+point);
});
To make this match the order of the series as plotted, we need to set reversedStacks: false on the yAxis, as by default, Highcharts reverses the order of the series. Then we populate the plotBands with our generated array:
yAxis: {
reversedStacks: false,
plotBands: bands
}
In this way you can make use of the full feature set of the plotBands label properties.
Updated fiddle:
http://jsfiddle.net/jlbriggs/xwzfjbhe/4/
And this adjusts to fit however many data points you have, provided you keep the same format (series.name, series.data[0]):
http://jsfiddle.net/jlbriggs/xwzfjbhe/5/
There are a number of other ways you can go about this as well, including:
The labels property: http://api.highcharts.com/highcharts/labels
The renderer function: http://api.highcharts.com/highcharts/Renderer
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,
});
I use Flot library for charting, I need to present a bars chart and there one special series which should be distinguished, giving it a certain color would be best option.
I've already did so in previous charts giving the color parameter to the series pushed to the data but it's not working here.
Bar for 21 should be in red and it's not.
First I tried the usual:
series.push({
color: 'anyHexColorCode',
data: [[parseFloat(k).toFixed(9).toString(),respuesta['puntos'][k]]]
})
In a loop I checked for the wished value and gave that a different color, same as I also do in the current function I got shown below.
This is how I'm sending to plot:
function plotInterpolacion(respuesta) {
clearCharts()
var series = []
var colorsList = ['#8B0000','#FFA614']
alert("La aproximación para x=" + $("#a").val() + " es igual a: " + respuesta['aproximacion'])
var xs = Object.keys(respuesta['puntos']).sort().forEach(function (k,i) {
if (k == respuesta['aproximacion']) {
series.push({
color: 0,
data: [[parseFloat(k).toFixed(9).toString(),respuesta['puntos'][k]]]
})
}
else {
series.push({
color: 1,
data: [[parseFloat(k).toFixed(9).toString(),respuesta['puntos'][k]]]
})
}
})
$.plot($("#bars"), series, {
bars: {
show:true,
align: "center",
barWidth: 0.6
},
xaxis: {
mode: "categories",
tickLength:0
},
colors: colorsList
})
}
In that example, bar for 21 should be in red.
This is what respuesta['puntos'] looks like:
"puntos": {
"18.0": 79.0,
"17.8": 72.0,
"21.0": 184.0000000000009
}
I have added jquery.colorhelpers.js flot plugin but it didn't make any difference.
The barWidth is expressed in axis units. So with a barWidth of 0.5, and only 0.2 x-units between the first and second bars, they will of course overlap.
A series can have only one color, and all of your bars are in the same series. If you want them to have different colors, split them into separate series.
Use the array directly when you are giving the color to data set? Like this:
color:colorsList[0]
http://jsfiddle.net/Margo/ZRkJN/4/