I want add number of series based on the data. Example sometimes for the same request, High-chart may include 2 series or 4 series.
Ex:
Request 1
[[ser1,ser2,datetime],[ser1,ser2,datetime]]
Request 2
[[ser1,ser2, ser3,ser4, datetime],[ser1,ser2, ser3,ser4, datetime]]
Where "datetime" is x-axis values
Could you please suggest me how to approach this.
You can convert your data to the right format in the preprocessing.
var json = [
[2, 4, 1500284119000],
[10, 20, 1500284141000]
],
series = [],
each = Highcharts.each,
len;
each(json, function(items, i) {
len = items.length;
each(items, function(item, j) {
if (j < len - 1) {
if (i === 0) { // create series structure
series.push({
data: []
});
}
series[j].data.push({
x: items[len - 1],
y: item
})
}
});
});
Example:
http://jsfiddle.net/hx97ak00/
I would suggest you to change the response structure if your xAxis is a datetime.
better include the timestamp along with data as below
seriesData = [[timestamp1, val1],[timestamp2, val],[timestamp3, val3],......]
now your request looks like this
Request 1
[seriesData1, seriesData2, seriesData3,seriesData4]
Request 2
[seriesData1, seriesData2, seriesData3, seriesData4, seriesData5, seriesData6, seriesData7,seriesData8]
I think you went to have a separate array for datetime because you have diffent number of series of data for 2 different timestamps.
In the above mentioned approach. you can directly feed the series section with the incoming response of the request.
Hope this will help you.
Related
I am using highcharts for statistical data displaying. I want to display , on the stack label , the average of all the values .
Below is what i am doing but i cant seem to get it right :
yAxis: {
min: 0,
title: {
text: 'Task'
},
stackLabels: {
style: {
color: 'black'
},
enabled: true,
formatter: function() {
return (this.axis.series[1].yData[this.x]).toPrecision(2) + '%';
}
}
},
The above only takes the last value on the stack and shows the percentage. For instance , if the last value is 50 , the above displays 50% as the stack value . I want to take an average of all the values. Any help would be appreciated.
If you want to show any stack's percentage mean if your stacked column has two stack A-5 and B-10 , then the % of B in column is 66% and % of A is 33%. If you want to show that use following in formatter function ( refer This Fiddle Link)
formatter: function() {
return (this.axis.series[1].yData[this.x] / this.total * 100).toPrecision(2) + '%';
}
Updating as per OP 's comment Refer Working fiddle here
Use following code : Make your data in a variable and calculate the sum
var seriesData = [{
name: 'Incomplete',
data: [1, 3, 4, 7, 20]
}, {
name: 'Complete',
data: [3, 4, 4, 2, 5]
}];
var total = 0;
$.each(seriesData,function(item){
$.each(seriesData[item].data,function() {
total += this;
});
});
And then use following in stacklabel formatter :
formatter: function() {
return ( this.total/ total * 100).toPrecision(2) + '%';
}
series:seriesData
hope it helps :)
This doesn't seem to be as easy as it should be.
I would accomplish this by pre-processing the data to generate an array of averages, and then referencing that array from the stackLabels formatter function.
Example, build the averages (assumes array 'data' with sub array for each series data values):
var sum = 0;
var averages = [];
var dataLen = data.length;
$.each(data[0], function(x, point) {
sum = 0;
for(var i = 0; i < dataLen; i++) {
sum += data[i][x];
}
averages.push(sum/dataLen);
})
And then in the formatter:
yAxis : {
stackLabels: {
enabled: true,
formatter: function() {
return Highcharts.numberFormat(averages[this.x],2);
}
}
}
Example:
http://jsfiddle.net/jlbriggs/vatdrecb/
If I could find a way to get a reliable count of the points in each stack, you could just use
return this.total/countOfPoints;
in the formatter without needing to build an array, but I can't seem to reliably get that count.
I'm trying to convert the following options in Highcharts to multiple series with multiple drilldowns. The problem is that I've changed the loop to progress over the points so as to add both drilldown series. However, in the loop I've written, it seems to be the case that after one go through the loop, the points array is overwritten with nulls, which makes the whole thing irrelevant.
I'm a beginner with the options, and after having spent quite a bit of time on it I can't crack it. An explanation and a solution would be an ideal answer to the question: "How do I do the following with multiple drilldowns?" It's all asynchronous requests on datasets.
I'm starting with
options.chart = options.chart || {};
options.chart.events = options.chart.events || {};
var dd = options.chart.events.drilldown || function(e) {};
options.chart.events.drilldown = function(e) {
var chart = this;
chart._drilldowns = chart._drilldowns || {};
var series = chart._drilldowns[e.point.drilldown];
if (series) {
e.seriesOptions = series;
chart.addSeriesAsDrilldown(e.point, series);
dd(e);
}
if (!e.seriesOptions) {
chart.showLoading('Fetching data...');
$.getJSON(
'%(url)s?' + analytics.get_form_data(),
function(drilldowns) {
chart.hideLoading();
chart._drilldowns = drilldowns;
var series = drilldowns[e.point.drilldown];
chart.addSeriesAsDrilldown(e.point, series);
e.seriesOptions = series;
dd(e);
}
);
}
};
''' % {'url': self.get_drilldown_url()}
and I've tried to change the second part to:
...
function(drilldowns) {
chart.hideLoading();
chart._drilldowns = drilldowns;
e.points.forEach(function(value, key){
var series = drilldowns[value.drilldown];
chart.addSeriesAsDrilldown(value, series);
})
e.seriesOptions = series;
dd(e);
}
);
}
};
But I don't get both my series drilling down. I actually get a Property xAxis of null is not allowed error, as when I go through the loop the second time, the set of points has been changed to null.
UPDATE
We eventually fixed (actually a collegue did!) using promises. We engineered it to wait until the first async request for data was retrieved, and then called addSingleSeriesAsDrilldown on each one until we hit the last one (derived off the chart state) at which point we called addSeriesAsDrilldown which does the applyDrilldown as part of the code.
It's not the part of official API, but this way you can get multiple drilldowns: http://jsfiddle.net/p2xw9416/
In the lowest level of AJAX requests (if you have multiple of them), add each series as single object:
chart.hideLoading();
chart.addSingleSeriesAsDrilldown(e.point, series_1);
chart.addSingleSeriesAsDrilldown(e.point, series_2);
chart.applyDrilldown(); // update && animate
I seem to be running into some kind of limit to the number of datapoints I can have in my series datahash. I am creating my data hash like so:
var data_hash = [];
var limit = 1000;
for(var i = 0; i < limit; i++)
{
data_hash.push({myData:'blah',
x: i,
y: (i+1)});
}
$(function() {
$('#container').highcharts('StockChart', {
tooltip: {
formatter: function() {
var s = '';
$.each(this.points, function(i, point) {
s += 'x: '+ point.x;
s += ', y: '+point.y;
});
return s;
}
},
series: [{
name: 'series_limit',
data: data_hash
}]
});
});
If I set the limit variable to 1000 or lower the graph will render just fine. However if I were to increase it to any value higher than that the graph will stop rendering. Is there something wrong with the way I am constructing my hash? Or is there some kind of configuration setting I can change to increase the number of datapoints allowed?
Here is a link to the jsfiddle:
http://jsfiddle.net/hYtUj/13/
Default number of datapoints before highcharts starts using arrays is 1000.You can change this value in chart options(parameter threshold)
Set turboThreshhold option to 0 or use two dimensional array with x and y values (turboThreshold)
data_hash.push([i+1, i]); // instead of {x: i, y: i+1}
I have an area chart with negative values. Nothing insanely different from the example they give, but there's one twist: I'd like to keep zero centered on the Y axis.
I know this can be achieved by setting the yAxis.max to some value n and yAxis.min to −n, with n representing the absolute value of either the peak of the chart or the trough, whichever is larger (as in this fiddle). However, my data is dynamic, so I don't know ahead of time what n needs to be.
I'm relatively new to Highcharts, so it's possible I'm missing a way to do this through configuration and let Highcharts take care of it for me, but it's looking like I'll need to use Javascript to manually adjust the y axis myself when the page loads, and as new data comes in.
Is there an easy, configuration-driven way to keep zero centered on the Y axis?
I ended up finding a way to do this through configuration after digging even further into the Highcharts API. Each axis has a configuration option called tickPositioner for which you provide a function which returns an array. This array contains the exact values where you want ticks to appear on the axis. Here is my new tickPositioner configuration, which places five ticks on my Y axis, with zero neatly in the middle and the max at both extremes :
yAxis: {
tickPositioner: function () {
var maxDeviation = Math.ceil(Math.max(Math.abs(this.dataMax), Math.abs(this.dataMin)));
var halfMaxDeviation = Math.ceil(maxDeviation / 2);
return [-maxDeviation, -halfMaxDeviation, 0, halfMaxDeviation, maxDeviation];
},
...
}
I know this is an old post, but thought I would post my solution anyway (which is inspired from the one macserv suggested above in the accepted answer) as it may help others who are looking for a similar solution:
tickPositioner: function (min, max) {
var maxDeviation = Math.ceil(Math.max(Math.abs(this.dataMax), Math.abs(this.dataMin)));
return this.getLinearTickPositions(this.tickInterval, -maxDeviation, maxDeviation);
}
You can do this with the getExtremes and setExtremes methods
http://api.highcharts.com/highcharts#Axis.getExtremes%28%29
http://api.highcharts.com/highcharts#Axis.setExtremes%28%29
example:
http://jsfiddle.net/jlbriggs/j3NTM/1/
var ext = chart.yAxis[0].getExtremes();
Here is my solution. The nice thing about this is that you can maintain the tickInterval.
tickPositioner(min, max) {
let { tickPositions, tickInterval } = this;
tickPositions = _.map(tickPositions, (tickPos) => Math.abs(tickPos));
tickPositions = tickPositions.sort((a, b) => (b - a));
const maxTickPosition = _.first(tickPositions);
let minTickPosition = maxTickPosition * -1;
let newTickPositions = [];
while (minTickPosition <= maxTickPosition) {
newTickPositions.push(minTickPosition);
minTickPosition += tickInterval;
}
return newTickPositions;
}
Just in case someone is searching,
One option more. I ended up in a similar situation. Follows my solution:
tickPositioner: function () {
var dataMin,
dataMax = this.dataMax;
var positivePositions = [], negativePositions = [];
if(this.dataMin<0) dataMin = this.dataMin*-1;
if(this.dataMax<0) dataMax = this.dataMax*-1;
for (var i = 0; i <= (dataMin)+10; i+=10) {
negativePositions.push(i*-1)
}
negativePositions.reverse().pop();
for (var i = 0; i <= (dataMax)+10; i+=10) {
positivePositions.push(i)
}
return negativePositions.concat(positivePositions);
},
http://jsfiddle.net/j3NTM/21/
It is an old question but recently I have had the same problem, and here is my solution which might be generalized:
const TICK_PRECISION = 2;
const AXIS_MAX_EXPAND_RATE = 1.2;
function setAxisTicks(axis, tickCount) {
// first you calc the max from the data, then multiply with 1.1 or 1.2
// which can expand the max a little, in order to leave some space from the bottom/top to the max value.
// toPrecision decide the significant number.
let maxDeviation = (Math.max(Math.abs(axis.dataMax), Math.abs(axis.dataMin)) * AXIS_MAX_EXPAND_RATE).toPrecision(TICK_PRECISION);
// in case it is not a whole number
let wholeMaxDeviation = maxDeviation * 10 ** TICK_PRECISION;
// halfCount will be the tick counts on each side of 0
let halfCount = Math.floor(tickCount / 2);
// look for the nearest larger number which can mod the halfCount
while (wholeMaxDeviation % halfCount != 0) {
wholeMaxDeviation++;
}
// calc the unit tick amount, remember to divide by the precision
let unitTick = (wholeMaxDeviation / halfCount) / 10 ** TICK_PRECISION;
// finally get all ticks
let tickPositions = [];
for (let i = -halfCount; i <= halfCount; i++) {
// there are problems with the precision when multiply a float, make sure no anything like 1.6666666667 in your result
let tick = parseFloat((unitTick * i).toFixed(TICK_PRECISION));
tickPositions.push(tick);
}
return tickPositions;
}
So in your chart axis tickPositioner you may add :
tickPositioner: function () {
return setAxisTicks(this, 7);
},
I'm pulling some data from a database that I'm trying to render into a Highcharts stock chart. The data is pulled from the database with PHP and passed to the chart with a $.get(..php/line-data.php) call, and the data retrieved is supposed to be the data that is rendered on the chart.
The data is being returned in the following manner, and I have verified this by logging data in the console. It appears as such, with the first value being the UNIX-to-Javascript converted date/time (x-axis), and the second being the value (y-axis):
[[1362639600000, 8],[1362726000000, 20],[1362985200000, 28],[1363071600000, 51],[1363158000000, 64],[1363244400000, 11],[1363330800000, 4],[1363503600000, 4],[1363590000000, 21],[1363676400000, 10],[1363762800000, 31],[1363849200000, 13],[1363935600000, 17],[1364194800000, 10],[1364454000000, 1],[1365058800000, 30],[1365145200000, 10],[1366009200000, 55],[1366182000000, 18],[1366268400000, 22],[1366354800000, 12]]
As an experiment, I tried just plugging this data straight into a basic demo Fiddle, and it seems to render fine.
FIDDLE HERE.
So what am I doing incorrectly? Everything seems to be set up correctly, but it's not rendering. This is what I see:
Here are the relevant portions of my code. Yes, I know that mysql_* is deprecated...I'll change it.
$.get('../php/line-data.php', function(data) {
window.chart = new Highcharts.StockChart({
chart : {
renderTo : 'total_mentions',
margin: [20, 10, 10, 10],
spacingTop: 0,
spacingBottom: 1,
spacingLeft: 0,
spacingRight: 0
},
series : [{
name : 'Total Mentions',
data: data,
type:'line',
lineWidth:1,
shadow:false,
states: {
hover: {
lineWidth:1
}
},
id : 'dataseries',
tooltip : {
valueDecimals: 4,
borderColor:'#DA7925',
borderRadius: 0,
borderWidth: 1,
shadow: false
},
color:'#DA7925',
fillOpacity:0.2
}]
[more options...etc.]
No problems with this code. It's pulling the correct data and echoing how I expect it to.
<?php
$expAddress = "URL";
$expUser = "USERNAME";
$expPwd = "PASSWORD";
$database = "DB";
$db = mysql_connect($expAddress, $expUser, $expPwd);
mysql_select_db($database, $db);
$ok = mysql_query("
SELECT
DATE(created_at) AS create_date,
COUNT(id) AS total
FROM
tweets
WHERE
subject LIKE 'word1'
OR
subject LIKE 'word2'
GROUP BY
DATE(created_at)");
if (!$ok) {
echo "<li>Mysql Error: ".mysql_error()."</li>";
}
else {
while($row = mysql_fetch_assoc($ok)){
extract($row);
$date = strtotime($create_date);
$date *= 1000;
$data[] = "[$date, $total]";
}
$tmp = join($data,',');
echo "[".$tmp."]";
}
?>
Have you tried parsing your data (string) into a javascript object before setting it to the series[i].data?
series : [{
data: JSON.parse(data)
}]
What you are getting from php through $.get is basically string and NOT a javascript array of array of numbers, which is what you want. It may look like that, but it is as simple as "5"!=5, but parseInt("5")==5 same is the case with json objects, you need to parse the string into such an object before javascript or highcharts can interpret it correctly, highcharts could do it on your behalf, but it is not designed that way.
Try his fiddle to get an idea of the data types in picture
var data="[[1362639600000, 8],[1362726000000, 20],[1362985200000, 28],[1363071600000, 51],[1363158000000, 64],[1363244400000, 11],[1363330800000, 4],[1363503600000, 4],[1363590000000, 21],[1363676400000, 10],[1363762800000, 31],[1363849200000, 13],[1363935600000, 17],[1364194800000, 10],[1364454000000, 1],[1365058800000, 30],[1365145200000, 10],[1366009200000, 55],[1366182000000, 18],[1366268400000, 22],[1366354800000, 12]]"
console.log(typeof data); //string
var parsedData=JSON.parse(data);
console.log(typeof parsedData); //object
console.log(typeof parsedData[0]); //object [1362639600000, 8]
console.log(typeof parsedData[0][0]); //number 1362639600000
When you paste the console value directly in the fiddle, you are actually pasting it as a valid javascript array, try using your console value wrapped by " quotes " and see that the exact issue is reproduced!!
Demo # jsFiddle
An alternate approach could be using the $.getJSON() method instead. jQuery does the parsing for you before it calls your callback method
Your problem is in either the output from the PHP script or when you receive the data in your Javascript (quite obvious).
First, don't do JSON by hand use json_encode (http://php.net/manual/en/function.json-encode.php). It's easier and it will guarantee that strings will be escaped properly.
Secondly, inspect your data variable with a debugger. You could also post the exact content of the variable to the question.
But basically, as long as it is working in the fiddle and not in your program you have not yet reproduced the error in your code properly in the fiddle.
For instance, you could replace data in your callback with the data you have in your fiddle to see if the code runs.