I have been recently experimenting with ChartJS and have come into a problem regarding sorting the bars in descending order, from lowest to highest. I have tried to debug but found no luck.
Are you sure the "beforeupdate" is called when you click the "Sort Data" button, because it seems you use the wrong variable.
barChart → barGraph
UPDATED
The old one link
UPDATED
Here is the link
// Get the data from each datasets.
var dataArray = [];
$.each(chart.data.datasets, function() {
dataArray.push(this.data);
});
// Get the index after sorted.
let dataIndexes = dataArray.map((d, i) => i);
dataIndexes.sort((a, b) => {
return dataArray[a] - dataArray[b];
});
// create after sorted datasets.
var tempDatasets = [];
$.each(dataIndexes, function() {
tempDatasets.push(chart.data.datasets[this]);
});
// apply it
chart.data.datasets = tempDatasets;
What data is returned using Ajax?
data = [{
sample : "ALPINE",
electric : 100,
mpg: 200,
urban: 300,
vmt: 400,
airtravel: 500,
renewable: 100,
conservation : 200,
heating: 300,
water: 400,
cefficiency: 500,
shiftc: 100,
healthyd: 200
}];
Related
So I have this code that I'm using for a class, but whenever I run it, nothing shows in the browser and instead under the Console I get an error that says that data.sort is not a function.
Here is the code in its entirety.
// Use d3 to read the JSON file.
// The data from the JSON file is arbitrarily named importedData as the argument.
d3.json("data/data.json").then((importedData) => {
// console.log(importedData);
var data = importedData;
// Sort the data array by using the greekSearchResults value.
data.sort(function(a, b) {
return parseFloat(b.greekSearchResults) - parseFloat(a.greekSearchResults);
});
// Slice the first 10 objects for plotting.
data = data.slice(0, 10);
// Reverse the array because of the Plotly defaults.
data = data.reverse();
// Trace1 for the Greek data.
var trace1 = {
x: data.map(row => row.greekSearchResults),
y: data.map(row => row.greekName),
text: data.map(row => row.greekName),
name: "Greek",
type: "bar",
orientation: "h"
};
// Data
var chartData = [trace1];
// Apply the group bar mode to the layout.
var layout = {
title: "Greek gods search results",
margin: {
l: 100,
r: 100,
t: 100,
b: 100
}
};
// Render the plot to the div tag with the id of "plot".
Plotly.newPlot("plot", chartData, layout);
});
sort is an array method. my guess is that the importedData variable isn't an array, hence you get that error. if you wish to use it, make sure to create an array from the data response.
sort - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
I am using a WebSocket connection to update a candlestick chart with live data.
Creating the initial candlestick chart is relatively easy:
var candleDiv = document.getElementById('candle-chart');
var data = {
x: x, //Each of these is a single dimension array of the same length
open: open,
close: close,
high: high,
low: low,
type: 'candlestick',
};
var layout = {
datarevision: candleCount,
dragmode: 'zoom',
showlegend: false,
xaxis: {
type: 'date',
range: [x[x.length - 26], x[x.length - 1]], //Only show the last 25 entries so it's not zoomed out too far.
rangeslider: {
visible: false
},
yaxis: {
autorange: true,
}
}
}
data.xaxis = 'x';
data.yaxis = 'y';
data = [data];
Plotly.plot(candleDiv, data, layout);
However, the documentation for the restyle method doesn't talk much to the update of data. More about how the data is displayed. After much tinkering, I found a reasonable workaround of updating the data variable directly:
candleDiv.data[0].open[candleDiv.data[0].open.length - 1] = updatedOpenValue;
candleDiv.data[0].close[candleDiv.data[0].close.length - 1] = updatedCloseValue;
candleDiv.data[0].high[candleDiv.data[0].high.length - 1] = updatedHighValue;
candleDiv.data[0].low[candleDiv.data[0].low.length - 1] = updatedLowValue;
Plotly.restyle(candleDiv, 'data[0]', candleDiv.data[0], [0]);
This works, except that it appears to draw the new candle on the old candle. This becomes particularly distracting when the stick changes from a green (increasing) stick to a red (decreasing) stick.
Is there a correct syntax to achieve what I am attempting to do such that I don't get display issues?
I checked out this link from this post but I couldn't get the method used to work in the context of a candlestick chart.
You may want to look at the Plotly.react method instead of Plotly.restyle: https://plot.ly/javascript/plotlyjs-function-reference/#plotlyreact
I know that I can set depth of all bars in Highcharts using depth property in column property of plotOptions likes the following code:
plotOptions: {
column : {
depth: 30
}
}
Or
# in R
hc_plotOptions(column = list(
depth = 30
)
The questions is how can I set different depth for each bar group in a bar chart (not one depth for all)? Solution can be in R (Highcharter) or in JS?
In core code the depth property is always taken from the series object options. Every group consists of the points with the same x values.
These 2 solutions came to my mind:
1. Modify the core code so that depth values are taken from points' configuration instead:
(function(H) {
(...)
H.seriesTypes.column.prototype.translate3dShapes = function() {
(...)
point.shapeType = 'cuboid';
shapeArgs.z = z;
shapeArgs.depth = point.options.depth; // changed from: shapeArgs.depth = depth;
shapeArgs.insidePlotArea = true;
(...)
};
})(Highcharts);
Series options:
series: [{
data: [{y: 5, depth: 50}, {y: 2, depth: 100}]
}, {
data: [{y: 13, depth: 50}, {y: 1, depth: 100}]
}]
Live demo: http://jsfiddle.net/kkulig/3pkon2Lp/
Docs page about overwriting core functions: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts
2. Create a separate series for every point.
depth property can be applied to a series so the modification of the core wouldn't be necessary. Every series is shown in legend by default so series will have to be properly connected using linkedTo property (so that the user doesn't see as many series as points).
Points can be modified before passing them to the chart constructor or dynamically handled in chart.events.load.
Live demo: http://jsfiddle.net/kkulig/37sot3am/
load: function() {
var chart = this,
newSeries = [],
merge = Highcharts.merge,
depths = [10, 100]; // depth values for subsequent x values
for (var i = chart.series.length - 1; i >= 0; i--) {
var s = chart.series[i];
s.data.forEach(function(p, i) {
// merge point options
var pointOptions = [merge(p.options, {
// x value doesn't have to appear in options so it needs to be added manually
x: p.x
})];
// merge series options
var options = merge(s.options, {
data: pointOptions,
depth: depths[i]
});
// mimic original series structure in the legend
if (i) {
options.linkedTo = ":previous"
}
newSeries.push(options);
});
s.remove(true);
}
newSeries.forEach((s) => chart.addSeries(s));
}
API reference:
https://api.highcharts.com/highcharts/plotOptions.series.linkedTo
I'm trying to set max value axis using dataprovider, since I'm dynamically loading data in my bar chart I need to see the "progress" on one of the bars compared to the one that is supposed to be the total.
How do I achieve this?
I' tried with:
"valueAxes": [
{
"id": "ValueAxis-1",
"stackType": "regular",
"maximum": myDataProviderAttribute
}
But no luck.
Any suggestion will be much apreciated.
I've submited a ticket to AmCharts support and got this feedback:
You can set max before the chart is initialized based on the data you have, inside addInitHandler. Here is an example for a simple column chart:
AmCharts.addInitHandler(function(chart) {
// find data maximum:
var min = chart.dataProvider[0].visits;
for (var i in chart.dataProvider) {
if (chart.dataProvider[i].visits > max) {
max = chart.dataProvider[i].visits;
}
}
// set axes max based on value above:
chart.valueAxes[0].maximum = max + 100;
chart.valueAxes[0].strictMinMax = true;
});
You may need to use strictMinMax as above to enforce the value:
https://docs.amcharts.com/3/javascriptcharts/ValueAxis#strictMinMax
Example of setting minimum inside addInitHandler:
https://codepen.io/team/amcharts/pen/b4be8cb4e3c073909860720e0909a876?editors=1010
If you refresh the data, or use live data, then before you animate or validate the chart to show the updated data, you should recalculate the max and, if you want the axis to change then use chart.valueAxes[0].maximum = max; where max is something you calculate based on data input.
Here is an example:
function loop() {
var data = generateChartData();
chart.valueAxes[0].maximum = max;
// refresh data:
chart.animateData(data, {
duration: 1000,
complete: function () {
setTimeout(loop, 2000);
}
});
}
The lines above were used in this example:
https://codepen.io/team/amcharts/pen/d0d5d03cfdcc2cc256e28ec52ad8b95c/?editors=1010
I'm trying to graph out metrics that don't have any relation to one another, so instead of plotting out the actual values, I've calculated an alternate set of values that are scaled between 0-1 like a percentage.
For example: [1, 2, 5] => [0.2, 0.4, 1]
So now I have 2 sets of data - the original and scaled versions. I have the scaled version plotting on to my graph just fine, but I want the tooltip to show the original value to the user. See what I mean?
I checked out http://c3js.org/samples/tooltip_format.html, which shows you can set tooltip as a function when you initially generate the C3 object. But I want to change the tooltip later on after I recalculate my original/scaled values and re-load() the graph.
All attempts I've made to explicitly change myGraph.tooltip.format.value = function (...) {...} after initially setting myGraph = C3.generate({...}) have been unsuccessful.
Any ideas how I can accomplish this without having to regenerate the graph from scratch every time?
You need to override internal.getTooltipContent
var data = ['data1', 30000, 20000, 10000, 40000, 15000, 250000];
// simple fake data
var fakeData = data.map(function (d, i) {
return i ? (d / 100) : d;
})
var chart = c3.generate({
data: {
columns: [
fakeData,
['data2', 100, 200, 100, 40, 150, 250]
],
}
});
// do code to take over mars and plant potatoes
// save the original
var originalGetTooltipContent = chart.internal.getTooltipContent;
chart.internal.getTooltipContent = function (data, defaultTitleFormat, defaultValueFormat, color) {
// we modified the first series, so let's change that alone back
var originalValue = {
id: data[0].id,
index: data[0].index,
name: data[0].name,
// unfaked
value: data[0].value * 100,
x: data[0].x
};
var originalValues = data.map(function (d, i) {
return i ? d : originalValue;
})
return originalGetTooltipContent.call(this, originalValues, defaultTitleFormat, defaultValueFormat, color)
}
I assume you are already doing something about the scaled y axis label?
Fiddle - http://jsfiddle.net/puf248en/
Thanks potatopeelings,
I did turn out solving this one by simply loading the form data in all at once, and then programmatically show/hide certain metrics. So that allowed me to use all the generate() options as intended.
Did try out your solution, and it seemed to do the trick till I found the simpler option. Thanks!