Shorten number labels in Charts.js - javascript

I'm making a simple line chart for a client using Chart.js, but the values shown are all above millions, making the labels take up a lot of space in the chart, as below:
I would like to shorten the labels to show an M instead of six 0s, for instance.
I've looked into the documentation and and have not found anything of such.

You could override the ticks.callback method as documented here: https://www.chartjs.org/docs/latest/axes/labelling.html#creating-custom-tick-formats
For example, to abbreviate the y-axis zeros to simply 'M':
var chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
scales: {
yAxes: [{
ticks: {
// Abbreviate the millions
callback: function(value, index, values) {
return value / 1e6 + 'M';
}
}
}]
}
}
});
My fiddle: https://jsfiddle.net/robhirstio/hsvxbjkg/17/

Adding commarize feature for k, M, B and T
function commarize(min) {
min = min || 1e3;
// Alter numbers larger than 1k
if (this >= min) {
var units = ["k", "M", "B", "T"];
var order = Math.floor(Math.log(this) / Math.log(1000));
var unitname = units[(order - 1)];
var num = Math.floor(this / 1000 ** order);
// output number remainder + unitname
return num + unitname
}
// return formatted original number
return this.toLocaleString()
}
In chart JS you could use config property ticks into yAxes
var chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
scales: {
yAxes: [{
ticks: {
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return String(value).commarize();
}
}
}]
}
}
});
Chart JS Reference https://www.chartjs.org/docs/latest/axes/labelling.html
Commarize reference https://gist.github.com/MartinMuzatko/1060fe584d17c7b9ca6e

Support 'K', 'M', 'B':
This is my solution, to be generic when you use the same options object for multiple charts, that possibly contain lower numbers or negative numbers.
formatNumbers(value) {
if (value >= 1000000000 || value <= -1000000000 ) {
return value / 1e9 + 'B';
} else if (value >= 1000000 || value <= -1000000) {
return value / 1e6 + 'M';
} else if (value >= 1000 || value <= -1000) {
return value / 1e3 + 'K';
}
return value;
}
My fiddle:
https://jsfiddle.net/epsilontal/v0qnsbwk/45/
Example:

Related

Highcharts negative logarithmic scale solution stopped working

I generate graph code for a graph with a logarithmic scale with negative values. The result is in the fiddle below.
I made the function required to handle the negative values like here and it worked perfectly. But since some days it does not function anymore. So I adjusted the code according to this article but it still does not work. See my JSFidddle:
(function (H) {
H.addEvent(H.Axis, 'afterInit', function () {
const logarithmic = this.logarithmic;
if (logarithmic && this.options.custom.allowNegativeLog) {
// Avoid errors on negative numbers on a log axis
this.positiveValuesOnly = false;
// Override the converter functions
logarithmic.log2lin = num => {
const isNegative = num < 0;
let adjustedNum = Math.abs(num);
if (adjustedNum < 10) {
adjustedNum += (10 - adjustedNum) / 10;
}
const result = Math.log(adjustedNum) / Math.LN10;
return isNegative ? -result : result;
};
logarithmic.lin2log = num => {
const isNegative = num < 0;
let result = Math.pow(10, Math.abs(num));
if (result < 10) {
result = (10 * (result - 1)) / (10 - 1);
}
return isNegative ? -result : result;
};
}
});
}(Highcharts));
I get an error 10 but the examples in that description don't go anywhere.
What is going on and how do I repair?
Apparently Highcharts made a change to the Axis definition.
The Y-axis was defined as:
yAxis:
{
type: 'logarithmic',
allowNegativeLog: true,
title:
{
text: 'Regen Spreiding ( mm)'
},
min: 0.0,
max: 25.40
},
And it is now required to be (at least it is working with this modification):
type: 'logarithmic',
custom:
{
allowNegativeLog: true,
},
NOTE: this is together with the modified function H which now starts with :
H.addEvent(H.Axis, 'afterInit', function () {
If you landed here like I did, checkout this JSFiddle by Highcharts with working demo.

Comparison between date codes

I have an array of values as such:
var array_1 = ["1W", "2W", "3W","1M", "2M", "3M", "6M","9M","1Y"]
W stands for weeks, M for months, Y for years. How do I do a string comparison such that a comparison between
"1Y" > "9M"
will return true
You could take the same base, like days for every information and take the letter for an equivalent of days and return the product.
function getDays(string) {
return string.slice(0, -1) * { W: 7, M: 30, Y: 365 }[string.slice(-1)];
}
var array = ["1W", "2W", "3W","1M", "2M", "3M", "6M","9M","1Y"]
console.log(array.map(getDays));
Here is a simple decoder that is easy to expand upon.
In essence it filters the numeric value and then returns it multiplied by some constant based upon the what time symbol it finds in the string (W, M, ...).
function decodeDateCode(dateCode) {
var numeric = parseInt(dateCode.replace(/\D/igm, ''), 10);
if (dateCode.indexOf("D") >= 0) {
return numeric;
}
if (dateCode.indexOf("W") >= 0) {
return numeric * 7;
}
if (dateCode.indexOf("M") >= 0) {
return numeric * (365 / 12);
}
if (dateCode.indexOf("Y") >= 0) {
return numeric * 365;
}
}
//test
var dateCodes = ["1W", "2W", "3W", "1M", "2M", "3M", "6M", "9M", "1Y", "50W"];
//Decode entire list
console.log("Decoded list:", dateCodes.map(decodeDateCode));
//Sort entire list in descending order
console.log("Sorted descending list:", dateCodes.sort(function(a, b) {
return decodeDateCode(b) - decodeDateCode(a);
}));
//Make simple comparison
console.log("Simple comparison:", decodeDateCode("1Y") > decodeDateCode("9M"));

Jquery not looping function correctly

I'm new to develop jquery plugin,I have stuck with my function on my function call my plugging repeat same value on elements.I do expect replace all values of my page,respectively
( function ($) {
$.fn.siPrifixx = function (value, options) {
// This is the easiest way to have default options.
var settings = $.extend({
// These are the defaults.
maxDigits: 8,
seperator: true,
decimal: 1,
popUp: true,
index: "tool tip message"
}, options);
console.log(settings.index);
$(this).addClass('tooltip', 'test');
$(this).tooltipster({
theme: 'tooltipster-default',
functionInit: function () {
return value
}
})
// $('.tooltip').prop(settings.index, value);
var number = value;
if (typeof value === 'string') {
var parts = value.split(",");
number = (parseInt(parts.join("")));
}
if (typeof number !== 'undefined' && !isNaN(number)) {
// if the number is alreadey comma seperated convert to number
var n = settings.decimal
// 2 decimal places => 100, 3 => 1000, etc
var decPlace = Math.pow(10, n);
// Enumerate number abbreviations
var abbrev = ["K", "M", "B", "T"];
// Go through the array backwards, so we do the largest first
for (var i = abbrev.length - 1; i >= 0; i--) {
// Convert array index to "1000", "1000000", etc
var size = Math.pow(10, (i + 1) * 3);
// If the number is bigger or equal do the abbreviation
if (size <= number) {
// Here, we multiply by decPlaces, round, and then divide by decPlaces.
// This gives us nice rounding to a particular decimal place.
number = Math.round(number * decPlace / size) / decPlace;
// Handle special case where we round up to the next abbreviation
if ((number == 1000) && (i < abbrev.length - 1)) {
number = 1;
i++;
}
// Add the letter for the abbreviation
number += abbrev[i];
// We are done... stop
break;
}
}
$(this).html(number)
console.log(number)
// return number;
} else {
$(this).html(number)
console.log(number)
// return value;
}
};
}(jQuery));
I'm calling function on a loop like this.
$.each($(plugin.element).find('.widget-data'), function(index, value)
{
var index = $(this).data('index');
var value = data.stats[index];
$('.widget-data').siPrifixx(value,{
decimal:2,
index:index
});
What's the wrong with my code?
When you call $('.widget-data').siPrifixx, you're still addressing all elements with the widget-data class. Since you're already iterating that set, you shouldn't targets all elements in every iteration. Instead call $(this).siPrifixx(...);
Can you try with following code:
$(plugin.element).find('.widget-data').each(function(index)
{
var index_current = $(this).data('index');
var value = data.stats[index_current];
$(this).siPrifixx(value,{
decimal:2,
index:index_current
});
});

Highcharts: display peak values

I would like to display difference between 2 points on my Highcharts graph. Only when it's a major "peak" difference.
Here's a "static" example :
$(function () {
$('#container').highcharts({
chart: {
type: 'areaspline'
},
series: [{
data: [560,500,476,453,356,{
y: 590,
dataLabels: {
enabled: true,
formatter: function () {return '+243';}
}
},497,478,465,465,465,410,398]
}]
});
});
http://jsfiddle.net/vr1a61wy/
How can I compare all point values with the previous ?
For example, something like that (not "real code)
if(prevValue > 200) {
displayDataLabel();
}
function displayDataLabel() {
peak = this.datalabel - prev.datalabel;
return peak;
}
The goal is to have a clean json file only with values and peaks calculated by javascript.
Is it possible ?
You can use the plotOptions.series.dataLabels.formatter to do this comparison. To do the comparison you will utilize this.x, this.y and this.series to look up the previous point.
An example formatter function would be (JSFiddle):
formatter: function() {
if(this.x > 0 && this.y - this.series.data[this.x-1].y >= PEAKDIFF) {
return "+" + (this.y - this.series.data[this.x-1].y);
}
return null;
}
Where PEAKDIFF is the value you want the difference to be before showing the label. If you want large negative drops to show as well you'd do the same with Math.abs.

Adding color to the max value in a dynamically generated chart?

The jsfiddle link is here http://jsfiddle.net/MzQE8/110/
The problem here I feel is in my JavaScript.
I input values to the series object in a HighChart from an array. On the array I am trying to find index and the value of the maximum element and then I am saving the maximum array element back with this modification
yArr[index] = {
y: value,
color: '#aaff99'
};
So that It appears as a diferent color from the rest of the points on the graph which is a dynamic one. That is its sliding one.
Here is my code
$(function () {
$(document).ready(function () {
Highcharts.setOptions({
global: {
useUTC: false
}
});
var chart;
$('#container').highcharts({
chart: {
type: 'spline',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function () {
// set up the updating of the chart each second
var series = this.series[0];
//As this graph is generated due to random values. I am creating an Array with random values.
var yArr = [];
yArr[0] = Math.random();
yArr[1] = Math.random();
yArr[2] = Math.random();
yArr[3] = Math.random();
setInterval(function () {
console.log(yArr.length);
var x = (new Date()).getTime(), // current time
y = Math.random();
var index = findIndexOfGreatest(yArr);
var value = yArr[index];
yArr[index] = {
y: value,
color: '#aaff99'
};
series.addPoint([x, yArr.shift()], true, true);
yArr.push(Math.random());
}, 1000);
}
}
},
title: {
text: 'Live random data'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 450
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
plotOptions: {
series: {
lineWidth: 1
}
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' + Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' + Highcharts.numberFormat(this.y, 2);
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'Random Data',
data: (function () {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -19; i <= 0; i++) {
data.push({
x: time + i * 1000,
y: yArr.shift()
});
}
return data;
})(),
color: 'red'
}]
});
});
function findIndexOfGreatest(array) {
var greatest;
var indexOfGreatest;
for (var i = 0; i < array.length; i++) {
if (!greatest || array[i] > greatest) {
greatest = array[i];
indexOfGreatest = i;
}
}
return indexOfGreatest;
}
});
I feel my idea is correct but there are big holes in my implementation. I guess.
Thanks
See demo: http://jsfiddle.net/MzQE8/350/
All y-values are stored in series.yData, so you don't have to create another array for that. Now just update point which is the highest one, and add new points. Something like above demo, code:
events: {
load: function () {
// set up the updating of the chart each second
var series = this.series[0],
index = series.yData.indexOf(series.yData.max());
// mark first max points
this.series[0].prevMax = this.series[0].data[index];
this.series[0].prevMax.update({
color: '#aaff99'
});
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.random(),
color = null,
index, max;
if (series.prevMax && series.prevMax.update) {
// remove previously colored point
if (y > series.prevMax.y) {
series.prevMax.update({
color: null
}, false);
color = '#aaff99';
// store max, which is last point
series.prevMax = series.data[series.yData.length];
}
} else {
max = series.yData.max();
index = series.yData.indexOf(max);
if(y > max) {
color = '#aaff99';
series.prevMax = series.data[series.yData.length];
} else {
series.prevMax = series.data[index];
series.prevMax.update({
color: '#aaff99'
}, false)
}
}
// add new point
series.addPoint({
x: x,
y: y,
color: color
}, true, true);
}, 1000);
}
}
im not to sure what your seeking but if i understand correctly you want the bullet marks to be a different color to differentiate from the others. i wont include source code because what you have included in your js fiddle is pretty advanced stuff and you will be able to figure this out no problem.
calculate (all total values added together / 256 + i) * 255 (or something similar)
the i represents the bullet increment in a loop. please for give the algorithm i haven't slept in 32 hours and i know its def. suggested you check it.
Oh.. kay here is the updated solution working out on paper
http://i966.photobucket.com/albums/ae147/Richard_Grant/Untitled-1_zps7766a939.png
I PUT A LOT OF WORK INTO THIS! so please!!! use it well -.-'
What is happening here is i drew out a graph with values going from 0 - 200 and i labeled the graph results 1 - 4, i also made this the x coordinates cause im lazy.
In the workflow RES = means results or X, Val = Value or Y
These are used to calculate RGB values and i solved the first 3 results. the first result tangent should always be 0 because any number divisible by 0 is 0, and any number multiplied by 0 is 0.
When i say tangent i mean the angle of the point by the 200, 0 value (invisible point). in this formula the angle would not be perfected because the x and y on the graph are not equal, one is 200 max and the other is 4 max. if i wanted this to be accurate i would have turned the tangent into a percent and multiplied it by 255. but i didn't.
I feel i have given you the necessary tools to complete your request on your own now :) if you do not understand how i did this or found this algorithm, just fall back to random colors.
Thanks John doe, your question helped me. This is the function i used to change the color of the maximum value of my y axis:
function () {
var maximum = chart.yAxis[0].dataMax;
var index = series.yData.indexOf(maximum);
chart.series[0].data[index].update({ color: '#aaff99' });
}
It may help other people too.

Categories