Highcharter (R wrapper for displaying Highcharts plots in R) does automatically adjust axis labels depending on the values, e.g. 405 will show as 405 whereas 3'000'000 will show as 3M.
I want to reproduce this for the tooltips shown.
This article outlines how to do this in Highcharts directly. However, I am not able to reproduce this using Highcharter.
Related:
highcharts tooltip format millions billions
JS Fiddle: http://jsfiddle.net/BlackLabel/ynCKW/104/
What am I missing?
chart_data <- tibble(
date_var = c(seq(from = as.Date('2019-09-10'), to = as.Date('2019-09-15'), by = 1))
, value = c(2304, 50000, 678900, 98457, 124684, 249547)
)
hchart(chart_data, type = 'line', hcaes(x = date_var, y = value)) %>%
hc_tooltip(formatter = JS(
"function() {
var axis = this.series.yAxis;
return axis.defaultLabelFormatter.call({
axis: axis,
value: this.y
});
}"
))
IMPORTANTLY: It seems to work for some values but not all of them. For instance, with above data I get correct tooltip labels for the 2nd and the 3rd date but all else show the original number.
In the related SO question you provided a link to, the assumption was to short numbers that have zeros (0) at the end. Just use the previous code from #Paweł Fus' answer:
formatter: function() {
var ret = '',
multi,
axis = this.series.yAxis,
numericSymbols = ['k', 'M', 'G', 'T', 'P', 'E'],
i = numericSymbols.length;
while (i-- && ret === '') {
multi = Math.pow(1000, i + 1);
if (axis.tickInterval >= multi && numericSymbols[i] !== null) {
ret = Highcharts.numberFormat(this.y / multi, -1) + numericSymbols[i];
}
}
return ret;
}
jsFiddle: https://jsfiddle.net/BlackLabel/yrb7gzap
Best regards!
Related
I came up with this Fiddle : https://jsfiddle.net/2s09hqLu/ which has stacked rounded chart as I wanted.But problem is when a value is 0 in data array.It doesnt make it rounded.I always want it to be rounded how can I do that?
data: [20, 5, 0, 15, 12, 13]
As you can see here on the yellow its flat not rounded.How can I solve this problem?
I have updated your Fiddle only line number 118:
https://jsfiddle.net/r7hvn2ox/
From:
if (rounded){
To:
if (rounded || this._chart.data.datasets[1].data[this._index] == 0) {
I know its fix for 2 datasets: [{}, {}] bars, but it will work perfectly in any of the values of datasets and also for this example.
Thanks everyone for answers.I tried below and worked perfectly for any amount of datasets.
var lastVisible;
var datasetsLength = this._chart.data.datasets.length;
this._chart.data.datasets.map((e,index)=>{
lastVisible=datasetsLength-1;
//to find the depth of datasets and get non-zero value
for(var i=lastVisible;i>0;i--){
if(!this._chart.getDatasetMeta(i).hidden){
if(this._chart.data.datasets[i].data[this._index] != 0){
lastVisible = i;
break;
}
}
}
})
You could add an additional expression to rounded variable if its rendering specific item. This is not the best approach.
var rounded = this._datasetIndex === lastVisible || this._index === 2;
Alternatively you could compare values in each dataset
var cond = false;
if(this._datasetIndex === 0) {
var datasets = this._chart.data.datasets;
var dataset = datasets[this._datasetIndex];
var currentData = dataset.data[this._index];
var nextData = datasets[this._datasetIndex + 1].data[this._index]
cond = currentData !== 0 && nextData === 0;
}
var rounded = this._datasetIndex === lastVisible || cond;
Very similar to the question stated in the problem blow, I want to automatically show big numbers using k, M etc. (1200 => 1.2k).
highcharts tooltip format millions billions
This seems to work for standard Highcharts plots but not for Highstock plots. My guess is that this is due to Highstock not having yAxis?
See, e.g., http://jsfiddle.net/1zhga6bm/1/
Would anyone know how to adjust the code below so that it works in Highstock?
var chart = new Highcharts.StockChart({
chart: {
renderTo: 'container'
},
series: [{
name: 'USD to EUR',
data: [10000, 1000000]
}],
tooltip: {
valueSuffix: '',
formatter: function () {
var ret = '',
multi,
axis = this.series.yAxis,
numericSymbols = ['k', 'M', 'G', 'T', 'P', 'E'],
i = numericSymbols.length;
while (i-- && ret === '') {
multi = Math.pow(1000, i + 1);
if (axis.tickInterval >= multi && numericSymbols[i] !== null) {
ret = Highcharts.numberFormat(this.y / multi, -1) + numericSymbols[i];
}
}
return ret;
}
}
});
Adjusted JSfiddle from below comment.
Many thanks!
I have below Dygraph using the dygraphs package
library(dygraphs)
library(htmlwidgets)
library(zoo)
valueFormatter = "function formatValue(v) {
var suffixes = ['', 'K', 'M', 'G', 'T'];
if (v < 1000) return v;
var magnitude = Math.ceil(String(Math.floor(v)).length / 4-1);
if (magnitude > suffixes.length - 1)
magnitude = suffixes.length - 1;
return String(Math.round(v / Math.pow(10, magnitude * 3), 2)) +suffixes[magnitude]
}"
Data = zoo(matrix(c(10000, 1000000, 100000000, 50000), nc = 1), as.Date(c('2015-01-05', '2016-01-05', '2017-01-05', '2018-01-05'))); colnames(Data) = 'x'
dygraph(Data, main = "") %>% dySeries(c("x")) %>%
dyAxis("y", axisLabelFormatter = JS(valueFormatter),
valueFormatter = JS(valueFormatter),
rangePad = 20)
However in the label of Y-axis I want to bring Thousand seperator for the tick values e.g. instead of 30000K I want to have 3,0000K. Is there any way to achieve this.
Convert your number 30000K into thousand separated value 30,000K.
Regex to place comma after each 3rd digit
var regex = /\B(?=(\d{3})+(?!\d))/g;
var number = "200";
var thousandSeprator = "20000";
console.log(number.replace(regex,',')); // 200
console.log(thousandSeprator.replace(regex,',')); // 20,000
Updated valueFormatter function
valueFormatter = "function formatValue(v) {
var suffixes = ['', 'K', 'M', 'G', 'T'];
var regex = /\B(?=(\d{3})+(?!\d))/g;
if (v < 1000) return v.toString().replace(regex,',');
var magnitude = Math.ceil(String(Math.floor(v)).length / 4-1);
if (magnitude > suffixes.length - 1)
magnitude = suffixes.length - 1;
return v.toString().replace(regex,',') +suffixes[magnitude]
}"
I'm new to dc.js so I am probably missing out on something, I have time series data that I am trying to display on 3 time dimensions: by month, by day and by hour - basically to display machine (manufacturing) efficiency across time.
So I made a barchart for the months and hours,and a linechart for the days. code below:
var cfdata = crossfilter(dataArray);
var avgadd = function(p,v) {
p.count++;
p.sum += v.efficiency;
p.avg = p.sum/p.count;
return p;
},
avgremove = function(p,v) {
p.count--;
p.sum -= v.efficiency;
p.avg = p.sum/p.count;
return p;
},
avginit = function(){
return {
count: 0,
sum: 0,
avg: 0
}
};
var parseDate = d3.time.format('%a %b %d %Y %H:%M:%S GMT%Z (UTC)').parse;
dataArray.forEach(function(d) {
d.date = parseDate(d.date);
d.hour = d3.time.hour(d.date).getHours();
d.day = d3.time.day(d.date);
d.month = d3.time.month(d.date);
// d.year = d3.time.year(d.date).getFullYear();
});
// dimensions: efficiency by hour
var hourDim = cfdata.dimension(function(d){return d.hour});
var hourlyEff = hourDim.group().reduce(avgadd, avgremove, avginit);
// dimensions: efficiency by day
var dayDim = cfdata.dimension(function(d){return d.day});
var minDay = dayDim.bottom(1)[0].date;
var maxDay = dayDim.top(1)[0].date;
var dailyEff = dayDim.group().reduce(avgadd, avgremove, avginit);
// dimensions: efficieny by month and year
var monthDim = cfdata.dimension(function(d){return d.month});
var minMonth = monthDim.bottom(1)[0].date;
var maxMonth = monthDim.top(1)[0].date;
var monthlyEff = monthDim.group().reduce(avgadd, avgremove, avginit);
dc.barChart(".month-eff-chart")
.height(180)
.width(900)
.dimension(monthDim)
.group(monthlyEff)
.valueAccessor(function(p){return p.value.avg})
.centerBar(true)
.x(d3.time.scale().domain([minMonth, maxMonth]))
.xUnits(d3.time.months)
.xAxis().ticks(d3.time.month, 1).tickFormat(d3.time.format("%b %y"));
dc.lineChart(".day-eff-chart")
.height(180)
.width(900)
.dimension(dayDim)
.group(dailyEff)
.valueAccessor(function(p){return p.value.avg})
.elasticX(true)
.x(d3.time.scale())
.xUnits(d3.time.days)
.xAxis().ticks(d3.time.week, 1).tickFormat(d3.time.format("%W/%y"));
dc.barChart(".hour-eff-chart")
.height(180)
.width(900)
.dimension(hourDim)
.group(hourlyEff)
.valueAccessor(function(p){return p.value.avg})
.x(d3.scale.linear().domain([0,23]));
dc.renderAll();
so when I open the page and filter across any of the barcharts, the other barchart will filter just fine, but the linechart will just become empty - the lines will completely disappear. when I change the linechart to a barchart, it works - filters just fine.
I must be missing something here.
also, suggestions on how to display time series data in a more meaningful way is also welcome. Thanks!
EDIT: JsFiddle: http://jsfiddle.net/shuzf2vm/2/
The reason that this doesn't work is that your averages will produce NaNs when the count is zero. You need to protect your divisions like this:
var avgadd = function(p,v) {
p.count++;
p.sum += v.efficiency;
p.avg = p.count ? p.sum/p.count : 0;
return p;
},
avgremove = function(p,v) {
p.count--;
p.sum -= v.efficiency;
p.avg = p.count ? p.sum/p.count : 0;
return p;
},
Working fork: http://jsfiddle.net/gordonwoodhull/hsqa43uk/3/
The reason it behaves differently for bar charts versus line charts is interesting. Both protect the output to the drawing functions and detect NaNs. But the bar chart protects each bar individually, while the line chart outputs a single multi-segment line and throws the whole line out if any point is bad.
(It might be more helpful to use d3.svg.line.defined here, if anyone cares to file a PR.)
I am using stacked line graph but I am facing issue with hover tooltips. There are some values which are 0. I just want to ignore tooltip on 0 value points because they override points with greater than 0 value.
I have tried eliminating 0 value points from data array but by doing that graph does not render correctly.
Please have a look at this:
When using the flot.tooltip plugin, you can set the content property to a function which returns a string for the tooltip or false for the case where you don't want to show a tooltip (see the documentation), something like this:
tooltipOpts: {
content: function (label, x, y, datapoint) {
if (y == 0) {
return false;
}
else {
// change this to get your desired format
return (new Date(x)).toString() + label + x;
}
},
When generating the tooltip manually with the plothover event, check the value before showing the tooltip, something like this:
$("#placeholder").bind("plothover", function (event, pos, item) {
// check if value is zero
if (item && item.datapoint[1] != 0) {
var x = item.datapoint[0].toFixed(2),
y = item.datapoint[1].toFixed(2);
// change this to get your desired format
$("#tooltip").html(x + item.series.label + y)
.css({top: item.pageY + 5, left: item.pageX + 5}).fadeIn(200);
} else {
$("#tooltip").hide();
}
});
I have analyzed the code base and here are the changes which I have purposed.
https://github.com/flot/flot/pull/1447/files
## -607,7 +607,8 ## Licensed under the MIT license.
clickable: false,
hoverable: false,
autoHighlight: true, // highlight in case mouse is near
- mouseActiveRadius: 10 // how far the mouse can be away to activate an item
+ mouseActiveRadius: 10, // how far the mouse can be away to activate an item
+ ignoreZeroValuePoints: false
},
interaction: {
redrawOverlayInterval: 1000/60 // time between updates, -1 means in same flow
## -2873,8 +2874,11 ## Licensed under the MIT license.
// use <= to ensure last point takes precedence
// (last generally means on top of)
if (dist < smallestDistance) {
- smallestDistance = dist;
- item = [i, j / ps];
+ jps = j / ps;
+ if(!options.grid.ignoreZeroValuePoints || series[i].data[series[i].datapoints.points.slice(jps * series[i].datapoints.pointsize, (jps + 1) * series[i].datapoints.pointsize)[0]][1] != 0){
+ smallestDistance = dist;
+ item = [i, jps];
+ }
}
}
}