I'm using a line chart (I think) for my data, and I'm trying to have red, yellow or green dots based upon the value of the data. The problem is, I can't even change the symbols used on the graph!
I'm using data pulled from a database, so I can't simply define the data within a series[] and then define the symbol from there, it's added using the chart.addSeries() function.
I'm sorry if this is a total noob question, I'm a total noob when it comes to JavaScript and Highcharts.
EDIT: For security reasons, I can't post the code.
Answer may not be 100% accurate, but I would do something like this:
// Loop over series and populate chart data
$.each(results.series, function (i, result) {
var series = chart.get(result.id);
//I think I have to do some sort of marker: set here
$.each(result.data, function (i, point) {
var x = point.x, // OR point[0]
y = point.y; // OR point[1]
result.data[i] = {
color: y > 100 ? 'red' : 'blue',
x: x,
y: y
}
});
if (series) {
series.update(result, false);
} else {
chart.addSeries(result, false);
}
});
chart.redraw();
As you can see, here I am adding color property to the point. Right now there is simple logic (value < 100), but you can apply there anything you want to, for example function which will return correct color etc.
Note that I am extracting x and y values. How to get them depends on how your data is formatted. It can be {x: some_valueX, y: some_valueY} or [some_valueX, some_valueY] or even some_valueY only.
Important: if you have a lot of points (1000+), don't forget to increase turboThreshold or disable it.
Related
I've already figured out how to make a chart using highcharts where there are three variables- one on the X axis, one on the Y axis, and one on the tooltip. The way to do this is to add the following to the tooltip:
tooltip: {
formatter () {
// this.point.x is the timestamp in my original chartData array
const pointData = chartData.find(row => row.timestamp === this.point.x)
return pointData.somethingElse
}
}
See this fiddle for the full code:
https://jsfiddle.net/m9e6thwn/
I would simply like to do the same, but with two series instead of one. I can't get it to work. I tried this:
tooltip: {
formatter () {
// this.point.x is the timestamp in my original chartData array
const pointData = chartData1.find(row => row.timestamp === this.point.x)
return pointData.somethingElse
const pointData2 = chartData2.find(row => row.timestamp === this.point.x)
return pointData2.somethingElse
}
}
Here is the fiddle of the above: https://jsfiddle.net/hdeg9x02/ As you can see, the third variable only appears on one of the two series. What am I getting wrong?
There are some issues with the way you are using the formatter now. For one, you cannot have two returns in the same function without any if clauses. That will mean that only the first return will be used.
Anyway, here are some improvements I suggest you do for your code.
Add the extra information for each point to highcharts, that makes it a lot easier to access this information through highcharts. E.g. in a tooltip. You can set the data like this:
chartData1.map(function(row) {
return {
x: row.timestamp,
y: row.value,
somethingElse: row.somethingElse
}
})
If you do that, then returning the correct tooltip for each series is a simple matter of doing this:
tooltip: {
formatter () {
// this.point.x is the timestamp in my original chartData array
return this.point.somethingElse
}
}
Working JSFiddle example: https://jsfiddle.net/ewolden/dq7L64jg/6/
If you wanted more info in the tooltip you could then do:
tooltip: {
formatter () {
// this.point.x is the timestamp in my original chartData array
return this.point.somethingElse + ", time: " + str(this.x) + ", value: " + str(this.y)
}
}
Addtionally, you need to ensure that xAxis elements, i.e. your timestamps are sorted. This is a requirement for highcharts to function properly. As it is, your example is reporting
Highcharts error #15: www.highcharts.com/errors/15
in console, because chartData2 is in reverse order. It looks okay for this example, but more complicated examples can lead to the chart not looking as you expect it to.
For this example using reverse is easy enough: data: chartData2.reverse().map(function(row) {return {x: row.timestamp, y: row.value, somethingElse: row.somethingElse}})
Working JSFiddle example: https://jsfiddle.net/ewolden/dq7L64jg/7/
I am trying to create a bubble chart using the JS HighChart in Angular2+. Whenever there are more than 50 data points (bubbles), the graph breaks. There are the correct number of bubbles in the correct positions (x,y plots) with all different colors but the sizes are all the same even though the z-values are all different. (I am outputing the z-values in a tooltip and the z-values are accurate)
This function is how I am passing in data to the high-chart configuration.
setSeries() {
this.objData = []
this.Data.forEach(element => {
var x= element['xVal'];
var y = element['yVal'];
var z = element['zVal'].toFixed(0);
var name = element['seriesName'].trim();
var newData =[{
x:x,
y:y,
z:+z,
}]
// SetSeriesData is how i am creating the obj to pass into series=[] in highchart configuration
if(i<50) //If I comment this condition, the graph breaks. Right now, the graph is working properly
this.setSeriesData(sumData, name, this.objData)
i++
})
this.options.series = this.objData;
this.generateChart();
}
This is my setSeriesData function.
setSeriesData(graphData: any, dataName: any, objData: any){
var obj = {};
obj['name'] = dataName;
obj['data'] = graphData;
obj['events'] = {click: function(e) {
//takes me to another link
}};
objData.push(obj)
}
In the above function, I configured the chart so that when you click the bubble, it takes you to another page. When the data points >50, this click functionality is not working either. In addition, the fillOpacity is not correct.
Just a few things to point out
1. I am using Angular 2+
2. The discovered issues are, fillOpacity, click, and size based on z-value.
3. It works perfectly when the data points are less than 50
How can I fix this?
I'm using Chart.js 2.6. I have a chart to which I've added custom pagination to step through the dataset, as it is quite large. My pagination and everything works great, I simply grab the next chunk of data in my set and update the chart.config.data with the new data object, and then call .update() on the chart. However, in order to make the chart make sense, I needed to keep the left (Y-axis) scale the same when the user is paginating through. Normally Chart.js would rebuild it based on the data in the chart, but I want it to always reflect the same values.
I've set the max value on the yAxes object of the chart to the maximum value in my data set. I've also set the beginAtZero option to true, and the maxTicksLimit to 10. However, even though my Yaxis does stay the same, it doesn't always look that great (see below screenshot). In this example, my max is set to 21,000 in the chart. Does anyone have any suggestions as to how I can either provide a better max (rounding up to next 5,000, 500, 100, etc based on the value) or some way to get it to create the Y axis without crunching the top number the way it does now?
Here is the function I currently use to determining the max data value to set as the max value in the Yaxes object in the chart. the plugin.settings.chartData variable represents an array of the data values used in the chart. I am trying to get it to increment correctly to the next 1000, 500, etc based on what the maxValue is, but as you can see my math is not correct. In the screenshot example, the maxValue is coming back as 20,750 and my function is rounding it up to 21,000. In this example it SHOULD round it up to the next increment which would be 25,000.
var determineMaxDataValue = function() {
var maxValue = Math.max.apply(Math, plugin.settings.chartData);
var step = maxValue > 1000 ? 1000 : 500;
plugin.settings.maxDataValue = (Math.ceil(maxValue / step) * step);
};
I too had the same problem. You needn't write any special function for determining the max value in the Yaxes. Use 'suggestedMax' setting. Instead for setting 'max' as maximum value in your graph, set suggestMax as the maximum value in your graph. This never works if you have set 'stepsize'.
options: {
scales: {
yAxes: [{
ticks: {
suggestedMax: maxvalue+20
}
}]
}
}
20 is added, so that the tooltip on max value will be clearly visible.
For more info, refer http://www.chartjs.org/docs/latest/axes/cartesian/linear.html#axis-range-settings
Figured it out. Instead of supplying the max value on the Y Axis as I have been, I instead implemented the afterBuildTicks callback and updated the ticks to have the correct increments.
yAxes: [{
afterBuildTicks: function(scale) {
scale.ticks = updateChartTicks(scale);
return;
},
beforeUpdate: function(oScale) {
return;
},
ticks: {
beginAtZero:true,
// max:plugin.settings.maxDataValue,
maxTicksLimit: 10
}
}]
my updateChartTicks function loops over the existing ticks and determines the correct increment amount between the ticks. Then I use that value to add my final "tick" which will always be greater than the largest data in the dataset.
var updateChartTicks = function(scale) {
var incrementAmount = 0;
var previousAmount = 0;
var newTicks = [];
newTicks = scale.ticks;
for (x=0;x<newTicks.length;x++) {
incrementAmount = (previousAmount - newTicks[x]);
previousAmount = newTicks[x];
}
if (newTicks.length > 2) {
if (newTicks[0] - newTicks[1] != incrementAmount) {
newTicks[0] = newTicks[1] + incrementAmount;
}
}
return newTicks;
};
I need to create a rowchart in dc.js with inputs from multiple columns in a csv. So i need to map a column to each row and each columns total number to the row value.
There may be an obvious solution to this but i cant seem to find any examples.
many thanks
S
update:
Here's a quick sketch. Apologies for the standard
Row chart;
column1 ----------------- 64 (total of column 1)
column2 ------- 35 (total of column 2)
column3 ------------ 45 (total of column 3)
Interesting problem! It sounds somewhat similar to a pivot, requested for crossfilter here. A solution comes to mind using "fake groups" and "fake dimensions", however there are a couple of caveats:
it will reflect filters on other dimensions
but, you will not be able to click on the rows in the chart in order to filter anything else (because what records would it select?)
The fake group constructor looks like this:
function regroup(dim, cols) {
var _groupAll = dim.groupAll().reduce(
function(p, v) { // add
cols.forEach(function(c) {
p[c] += v[c];
});
return p;
},
function(p, v) { // remove
cols.forEach(function(c) {
p[c] -= v[c];
});
return p;
},
function() { // init
var p = {};
cols.forEach(function(c) {
p[c] = 0;
});
return p;
});
return {
all: function() {
// or _.pairs, anything to turn the object into an array
return d3.map(_groupAll.value()).entries();
}
};
}
What it is doing is reducing all the requested rows to an object, and then turning the object into the array format dc.js expects group.all to return.
You can pass any arbitrary dimension to this constructor - it doesn't matter what it's indexed on because you can't filter on these rows... but you probably want it to have its own dimension so it's affected by all other dimension filters. Also give this constructor an array of columns you want turned into groups, and use the result as your "group".
E.g.
var dim = ndx.dimension(function(r) { return r.a; });
var sidewaysGroup = regroup(dim, ['a', 'b', 'c', 'd']);
Full example here: https://jsfiddle.net/gordonwoodhull/j4nLt5xf/5/
(Notice how clicking on the rows in the chart results in badness, because, what is it supposed to filter?)
Are you looking for stacked row charts? For example, this chart has each row represent a category and each color represents a sub-category:
Unfortunately, this feature is not yet supported at DC.js. The feature request is at https://github.com/dc-js/dc.js/issues/397. If you are willing to wade into some non-library code, you could check out the examples referenced in that issue log.
Alternatively, you could use a stackable bar chart. This link seems to have a good description of how this works: http://www.solinea.com/blog/coloring-dcjs-stacked-bar-charts
I have a spiderweb graph with just the one data series. In the interests of printing I'd like to incorporate the value plotted in the label after the actual label name but I can't seem to work out how to do so.
This example to help explain what I mean is a bar-graph that I found in my search. The code layout is similar and a lot less cluttered than mine and I'm fairly sure a solution on this one would be easily transferable.
labels: {
formatter: function() {
return this.value + ' XX.X';
}
}
JSFiddle Here
So for the X axis I'm trying to replace the "XX.X" with the relevant Y value to get the labels: "Foo 29.9 | Bar 71.5 | Foobar 106.4"
I dug through "this" in the xAxis labels formatter and got as far as finding the Y figures at "this.chart.series[0].yData", but couldn't work out a way to associate these back to each relevant X label, and I thought it seemed the wrong way of going about things anyway.
You can do this after rendering of the chart in the chart.events.load method using setCategories. This code below assumes that each index element on the xAxis has a corresponding data element. If it doesn't then you would need to handle that case.
chart: {
events: {
load: function (event) {
var cats = this.xAxis[0].categories;
var theData = this.series[0].data;
var newCats = [];
for (var i = 0; i < cats.length; i++) {
newCats.push(cats[i] + ' ' + theData[i].y)
}
this.xAxis[0].setCategories(newCats);
}
}
}
Live demo.