I am using Chart.js to draw graphs in typescript.
I want to get a dynamic weight and bring a minimum and a maximum. And with maxTicksLimit as 5, I want to keep 5 Ticks no matter what data comes in.
The decimal point of body weight is taken to the first decimal point.
ex) 50.3
I want to show the difference between minimum and maximum as much as possible.
please help me!!!
ex1) maximum weight: 74.5, minimum weight: 71
result
Y Axis maximum weight: 76 , Y Axis minimum weight: 71
ex1 result image
enter image description here
ex2) maximum weight: 76.9, minimum weight: 62
result
Y Axis maximum weight: 76 , Y Axis minimum weight: 61
ex2 result image
enter image description here
The beforeBuildTicks callback also works to create a dynamic tick step size. Here is the code:
{
maintainAspectRatio: false,
tooltips: {
mode: 'label',
position: 'nearest',
},
scales: {
xAxes:[
{...}
],
yAxes: [
{
position: 'left',
id: 'y-axis-0',
scaleLabel: {
display: true,
labelString: 'label string goes here',
fontSize: 16,
},
ticks: {
beginAtZero: true,
stepSize: .25,
},
beforeBuildTicks: function(axis) {
if (axis.max >= 17) {
axis.options.ticks.stepSize = 1;
} else if (axis.max >= 10) {
axis.options.ticks.stepSize = .5;
}
}
}
]
}
}
I solved this problem in the following way.
There was a callback function of afterBuildTicks in chart.js
※ chart.js documents link.
It becomes possible to customize the ticks.
//... middle code skip
const getChartMaxAndMin = (
maxValue,
minValue
): { max: number; min: number } => {
// Convert all decimal places to integers
let max = Math.ceil(maxValue);
let min = Math.floor(minValue);
// a multiple of 5
const MULTIPLES = 5;
const DIVIDED_REMAINING_VALUE_LIMIT = 3;
// Maximum to Minimum difference
const diff = max - min;
const diffDividedRemainingValue = diff % MULTIPLES;
const remainingValue =
MULTIPLES *
(diffDividedRemainingValue > DIVIDED_REMAINING_VALUE_LIMIT
? Math.floor(diff / MULTIPLES) + 2
: Math.floor(diff / MULTIPLES) + 1) -
diff;
if (remainingValue % 2 !== 0) {
max = max + Math.floor(remainingValue / 2) * 2;
min = min - Math.floor(remainingValue % 2);
return { max, min };
}
max = max + remainingValue / 2;
min = min - remainingValue / 2;
return { max, min };
};
const customizedAxesTicks = (axis) => {
const EQUAL_PARTS = 5; // set 5 parts
const max = axis.max;
const min = axis.min;
const steps = (max - min) / EQUAL_PARTS;
const ticks = [];
for (let i = min; i <= max; i += steps) {
ticks.push(i);
}
axis.ticks = ticks;
return;
};
const {max, min} = getChartMaxAndMin(68,57);
const chartOptions = {
//... code skip
scales: {
yAxes: [
{
//... code skip
ticks: {
max: max,
min: min
},
afterBuildTicks: customizedAxesTicks,
//... code skip
};
I would appreciate it if you could let me know if there is a better way😀
Related
Sorry, I'm begginer in js, I want to update sliderState after changing min, max of the slider.
I've functions which update min, max in slider, also I have the variable which is calculated from min, max.
I need update to this variable.
const sliderStates = [
{
name: "low",
tooltip: "Great, we're confident we can complete your project within <strong>24 hours</strong> of launch.",
range: _.range(state.minValue, state.maxValue / 3)
},
{
name: "med",
tooltip: "Looks good! We can complete a project of this size within <strong>48 hours</strong> of launch.",
range: _.range(state.maxValue / 3, (state.maxValue / 3) * 2)
},
{
name: "high",
tooltip: "With a project of this size we'd like to talk with you before setting a completion timeline.",
range: _.range((state.maxValue / 3) * 2, state.maxValue + 1)
},
];
//update min
connector.updateMin = function (min) {
let attributeMin = {
min: min,
};
$element.attr(attributeMin).rangeslider('update', true);
}
//update max
connector.updateMax = function (max) {
let attributeMax = {
max: max,
};
$element.attr(attributeMax).rangeslider('update', true);
}
I'm not entirely sure if a for loop is the right thought the process to handle what my issue is. I am creating a line graph to display the % growth between two years for 10 years. I am able to get the data showing on the graph as expect but I unable to think of another way to make it more efficient. My current code works but I would like to handle the calculations for the entire array of data together rather than handling it separately.
my current logic:
const numerator is the index of the year I would to display a percentage growth I am subtracting it from const denominator
I am removing the commas in the first two lines since the value are higher numbers such as 1,323,349
Finally, the third line perChange is the calculation for the actual percentage. I am using 2 indexes at a time and using the larger number of the two to subtract the smaller one for example. index 1 - index 0; index 3 - index 2, index 5 - index 4 etc...
This is my current code:
const numeratorOne = Number(caArray[1].DataValue.replace(/,/g, ''))
const denominatorOne = Number(caArray[0].DataValue.replace(/,/g, ''))
const perChangeOne = 100 * Math.abs((denominatorOne - numeratorOne) / ((denominatorOne + numeratorOne ) /2 ) )
const numeratorTwo = Number(caArray[3].DataValue.replace(/,/g, ''))
const denominatorTwo = Number(caArray[2].DataValue.replace(/,/g, ''))
const perChangeTwo = 100 * Math.abs((denominatorTwo - numeratorTwo) / ((denominatorTwo + numeratorTwo) / 2))
const numeratorThree = Number(caArray[5].DataValue.replace(/,/g, ''))
const denominatorThree = Number(caArray[4].DataValue.replace(/,/g, ''))
const perChangeThree = 100 * Math.abs((denominatorThree - numeratorThree) / ((denominatorThree + numeratorThree) / 2))
const numeratorFour = Number(caArray[7].DataValue.replace(/,/g, ''))
const denominatorFour = Number(caArray[8].DataValue.replace(/,/g, ''))
const perChangeFour = 100 * Math.abs((denominatorFour - numeratorFour) / ((denominatorFour + numeratorFour) / 2))
const numeratorFive = Number(caArray[9].DataValue.replace(/,/g, ''))
const denominatorFive = Number(caArray[8].DataValue.replace(/,/g, ''))
const perChangeFive = 100 * Math.abs((denominatorFive - numeratorFive) / ((denominatorFive + numeratorFive) / 2))
const numeratorSix = Number(caArray[11].DataValue.replace(/,/g, ''))
const denominatorSix = Number(caArray[10].DataValue.replace(/,/g, ''))
const perChangeSix = 100 * Math.abs((denominatorSix - numeratorSix) / ((denominatorSix + numeratorSix) / 2))
const numeratorSeven = Number(caArray[13].DataValue.replace(/,/g, ''))
const denominatorSeven = Number(caArray[12].DataValue.replace(/,/g, ''))
const perChangeSeven = 100 * Math.abs((denominatorSeven - numeratorSeven) / ((denominatorSeven + numeratorSeven) / 2))
const numeratorEight = Number(caArray[15].DataValue.replace(/,/g, ''))
const denominatorEight = Number(caArray[16].DataValue.replace(/,/g, ''))
const perChangeEight = 100 * Math.abs((denominatorEight - numeratorEight) / ((denominatorEight + numeratorEight) / 2))
const numeratorNine = Number(caArray[17].DataValue.replace(/,/g, ''))
const denominatorNine = Number(caArray[16].DataValue.replace(/,/g, ''))
const perChangeNine = 100 * Math.abs((denominatorNine - numeratorNine) / ((denominatorNine + numeratorNine) / 2))
Highcharts.chart('container', {
chart: {
type: 'line'
},
title: {
text: 'Gross State Product in California'
},
xAxis: {
categories: ['1998', '2000', '2002', '2004', '2006', '2008', '2010', '2012', '2014', '2016', '2018'],
title: {
text: 'Year'
},
},
yAxis: {
categories: ['2', '4', '6', '8', '10'],
title: {
text: 'Percentage Change (%)'
}
},
plotOptions: {
line: {
dataLabels: {
enabled: false
},
enableMouseTracking: false
}
},
series: [{
name: 'California',
data: [perChangeOne, perChangeTwo, perChangeThree, perChangeFour, perChangeFive, perChangeSix, perChangeSeven, perChangeEight, perChangeNine, 8.3, 3.9],
color: '#002F65'
}, {
name: 'US',
color: '#0B7070',
data: [3.9, 4.2, 5.7, 8.5, 1.9, 5.2, 7.0, 6.6, 4.2, 5.3, 10]
}]
});
}
The desired outcome is a way to get the same results without having to create 10 different variables as I will need to continue to add more data to this graph in the future.
I have attempted this:
for (let i = 0; i < caArray.length; i++) {
let removeComma = Number(caArray.DataValue.replace(/,/g, ''))
}
for (let i = 0; i < removeComma.length; i += 2) {
// math logic here which I do not know how to work out
}
Iterate over the caArray first, pushing to either a numerators or denominators array, depending on the index of the item you're iterating over. Then map the numerators array to get the associated denominator and calculate the change, and you'll have the array you can put into the series.data property:
const numerators = [];
const denominators = [];
caArray.forEach(({ DataValue }, i) => {
const arrToPushTo = i % 2 === 0 ? denominators : numerators;
arrToPushTo.push(Number(DataValue.replace(/,/g, '')));
});
const changes = numerators.map((numerator, i) => {
const denominator = denominators[i];
return 100 * Math.abs((denominator - numerator) / ((denominator + numerator) / 2));
});
series: [{
name: 'California',
data: [...changes, 8.3, 3.9],
const caArray = [
{ DataValue: '10' },
{ DataValue: '20' },
{ DataValue: '30' },
{ DataValue: '40' },
];
const numerators = [];
const denominators = [];
caArray.forEach(({ DataValue }, i) => {
const arrToPushTo = i % 2 === 0 ? denominators : numerators;
arrToPushTo.push(Number(DataValue.replace(/,/g, '')));
});
const changes = numerators.map((numerator, i) => {
const denominator = denominators[i];
return 100 * Math.abs((denominator - numerator) / ((denominator + numerator) / 2));
});
console.log(changes);
If i understood correctly you just need one loop. You need to iterate by two each and then you have all the data in your current iteration. Something like this.
const changes = []
for(let i = 0; i < caArray.length; i+=2) {
if (!caArray[i + 1]) {
break
}
const denominator = Number(caArray[i].DataValue.replace(/,/g, ''));
const numerator = Number(caArray[i + 1].DataValue.replace(/,/g, ''));
const perChange = 100 * Math.abs((denominator - numerator) / ((denominator + numerator ) /2 ) )
changes.push(perChange);
}
This should work.
How to set/supply value (default to 0) for blank data in hours as shown in attached image below.
The target result should have all graphs even for blank hours.
You can create a loop and add points to your data array with some default y value (50 in the example below) and x value with the same interval as tickInterval:
var data = [
[1561593600000, 102.5],
[1561658400000, 177.45],
[1561723200000, 115.5]
],
interval = 2 * 60 * 60 * 1000,
j = 1,
i = data[0][0] + interval;
for (j; j < data.length; j++) {
for (i; i < data[j][0]; i += interval) {
data.push([i, 50])
}
i += interval;
}
Highcharts.chart('container', {
chart: {
type: 'column'
},
series: [{
data: data.sort(function(a, b) {
return a[0] - b[0]
})
}],
xAxis: {
type: 'datetime',
tickInterval: interval
},
});
Live demo: http://jsfiddle.net/BlackLabel/m0zfeyd4/
I was playing around with the waterfall series of the jqxChart.
According to its API, the following piece of code defines the values of the axis, in this case it's the y-axis:
valueAxis:
{
title: {text: 'Population<br>'},
unitInterval: 1000000,
labels:
{
formatFunction: function (value) {
return value / 1000000 + ' M';
}
}
}
Is it possible to define the intervals not with absolute values, but with relative values. So that the interval are e.g. 10% and the overall value is 100%?
Simply doing unitInterval: '10%' doesn't work.
This is how it should look like:
Here is a fiddle.
I think you're looking for these options :
logarithmicScale: true,
logarithmicScaleBase: 1.10,
Example:
valueAxis:
{
title: {text: 'Population<br>'},
logarithmicScale: true,
logarithmicScaleBase: 1.10,
labels:
{
formatFunction: function (value) {
return value / 1000000 + ' M';
}
}
},
Edit:
var accuracy = 2;
var first = data[0].population;
var last = data[data.length - 2].population;
var unit = (100 / last);
// convert raw data to differences
for (var i = 0; i < data.length - 2; i++)
data[i].population = (data[i].population * unit).toFixed(accuracy);
I need to create a dynamic scales something like this
Range 1 = 0 to 100
Range 2 = 100 to 200
Range 3 = 200 to 300
Range 4 = 300 to 400
Range 5 = 400 to 500
Range 6 = 600 to 700
Range 7 = 700 to 800
Range 8 = 800 to 900
Range 9 = 900 to 1000
Here, ranges are 1 to 9 and minimum value is 0 and maximum value is 1000. These ranges, minimum and maximum values are dynamic.
So, I required a function to return the scales.
For example:-
function getScales(minRage, maxRange, minValue, maxValue){
var scales={};
..........
............
return scales;
}
//Output:
[
{
range :1
min :0,
max :100
},
{
range :2
min :100,
max :200
},
{
range :3
min :200,
max :300
},
....
....
{
range :9,
min :900,
max :1000
}
]
To get above result , I need to call the function like this getScales(1, 9, 0, 1000).
This is what my actual requirement: if I call getScales(1, 5, 4000, 418500);
Have a look at this:
function getScales(minRange, maxRange, min, max){
var scales = [], // Prepare some variables
ranges = maxRange+1 - minRange, // Amount of elements to be returned.
range = (max-min)/ranges; // Difference between min and max
for(var i = 0; i < ranges; i++){
scales.push({
range: i+minRange, // Current range number
min: min + range * i,
max: min + range * (i+1)
});
}
return scales;
}
You can call the function like this:
getScales(0, 9, 0, 1000);
Output:
[
{
"range": 0,
"min": 0,
"max": 100
},
{
"range": 1,
"min": 100,
"max": 200
},
.......
{
"range": 8,
"min": 800,
"max": 900
},
{
"range": 9,
"min": 900,
"max": 1000
}
]
To get rid of the floating point errors in the output, you can replace:
min: range * i,
max: range * (i+1)
With:
min: (range * i).toFixed(2),
max: (range * (i+1)).toFixed(2)
Replace the 2 with the desired amount of digits behind the decimal point.
Something like that:
var factory = function(start, end, minV, maxV){
var result = [],
st = (maxV - minV) / (end - start + 1),
buffer = minV;
for(var i = 0; i <= end - start; i++){
buffer = st * i + minV;
result.push({
range: i,
minValue: buffer.toFixed(2),
maxValue: (buffer + st).toFixed(2)
});
}
return result;
}
console.log(JSON.stringify(factory(0, 4, 253, 467)))
Some explanation: #start and #end describe number of ranges, #minV - first range starts with this value, #maxV - last range ends with this
Output
[{"range":0,"minValue":"253.00","maxValue":"295.80"},{"range":1,"minValue":"295.80","maxValue":"338.60"},{"range":2,"minValue":"338.60","maxValue":"381.40"},{"range":3,"minValue":"381.40","maxValue":"424.20"},{"range":4,"minValue":"424.20","maxValue":"467.00"}]
Play with demo