英文:
How do i sort the segments within each column for a stacked bar chart so that the largest value for each category is always at the top?
问题
我正在尝试使用AmCharts 5创建一个柱状图,其中每个列的分段按从最小到最大的顺序排序,以便最大的分段始终位于列的顶部。
我的图表代码目前如下:
// 获取具有指定ID的图表div
var chartDiv = document.getElementById(selector);
const data = JSON.parse(chartDiv.getAttribute('data-chart'));
const fontSize = 12;
const markerSize = 12;
var root = am5.Root.new(selector);
var chart = root.container.children.push(
am5xy.XYChart.new(root, {
panY: false,
layout: root.verticalLayout,
dy: 10,
})
);
// 创建X轴
let xAxis = chart.xAxes.push(
am5xy.CategoryAxis.new(root, {
categoryField: "week_ending_date",
renderer: am5xy.AxisRendererX.new(root, {})
})
);
let xRenderer = xAxis.get("renderer");
xRenderer.labels.template.setAll({
fontSize: fontSize
});
xAxis.data.setAll(data);
// 创建Y轴
var yAxis = chart.yAxes.push(
am5xy.ValueAxis.new(root, {
renderer: am5xy.AxisRendererY.new(root, {}),
})
);
let yRenderer = yAxis.get("renderer");
yRenderer.labels.template.setAll({
fontSize: fontSize
});
// 添加系列
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
function makeSeries(name, fieldName, colorHex) {
var series = chart.series.push(am5xy.ColumnSeries.new(root, {
name: name,
stacked: true,
xAxis: xAxis,
yAxis: yAxis,
baseAxis: xAxis,
valueYField: fieldName,
categoryXField: "week_ending_date",
fill: colorHex,
}));
series.columns.template.setAll({
tooltipText: "{name} - {valueY}",
tooltipY: am5.percent(90)
});
series.data.setAll(data);
series.bullets.push(function() {
return am5.Bullet.new(root, {
sprite: am5.Label.new(root, {
text: "{valueX}",
fill: root.interfaceColors.get("alternativeText"),
centerY: am5.p50,
centerX: am5.p50,
populateText: true,
fontSize: 12,
})
});
});
legend.data.push(series);
}
// 为数据中的每个站点添加系列
// 获取所有站点名称
let sites = Object.keys(data[0])
// 移除第一个值 (week_ending_date)
sites.shift()
sites.forEach((site, index) => {
makeSeries(site, site, COLOR_SCHEME[index]);
})
而我的数据如下:
[
{
"week_ending_date": "2022-10-02",
"site A": 100,
"site B": 150,
"site C": 200,
},
{
"week_ending_date": "2022-10-09",
"site A": 400,
"site B": 150,
"site C": 50,
},
// ...
所以在我的第一列中,我希望site C出现在顶部,而在第二列中,我希望site A出现在顶部。
有人知道是否可能以及如何实现吗?
提前感谢。
英文:
I am trying to create a bar chart using AmCharts 5 where the segments for each column are sorted from smallest to largest, so that the largest segment is always placed at the top of the column.
My chart code so far looks like this:
// Get chart div by id
var chartDiv = document.getElementById(selector);
const data = JSON.parse(chartDiv.getAttribute('data-chart'));
const fontSize = 12;
const markerSize = 12;
var root = am5.Root.new(selector);
var chart = root.container.children.push(
am5xy.XYChart.new(root, {
panY: false,
layout: root.verticalLayout,
dy: 10,
})
);
// Create X-axis
let xAxis = chart.xAxes.push(
am5xy.CategoryAxis.new(root, {
categoryField: "week_ending_date",
renderer: am5xy.AxisRendererX.new(root, {})
})
);
let xRenderer = xAxis.get("renderer");
xRenderer.labels.template.setAll({
fontSize: fontSize
});
xAxis.data.setAll(data);
// Create Y-Axis
var yAxis = chart.yAxes.push(
am5xy.ValueAxis.new(root, {
renderer: am5xy.AxisRendererY.new(root, {}),
})
);
let yRenderer = yAxis.get("renderer");
yRenderer.labels.template.setAll({
fontSize: fontSize
});
// Add series
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
function makeSeries(name, fieldName, colorHex) {
var series = chart.series.push(am5xy.ColumnSeries.new(root, {
name: name,
stacked: true,
xAxis: xAxis,
yAxis: yAxis,
baseAxis: xAxis,
valueYField: fieldName,
categoryXField: "week_ending_date",
fill: colorHex,
}));
series.columns.template.setAll({
tooltipText: "{name} - {valueY}",
tooltipY: am5.percent(90)
});
series.data.setAll(data);
series.bullets.push(function() {
return am5.Bullet.new(root, {
sprite: am5.Label.new(root, {
text: "{valueX}",
fill: root.interfaceColors.get("alternativeText"),
centerY: am5.p50,
centerX: am5.p50,
populateText: true,
fontSize: 12,
})
});
});
legend.data.push(series);
}
// Add series for each site in the data
// Get all site names
let sites = Object.keys(data[0])
// Remove first value (week_ending_date)
sites.shift()
sites.forEach((site, index) => {
makeSeries(site, site, COLOR_SCHEME[index]);
})
And my data looks like this:
[
{
"week_ending_date": "2022-10-02",
"site A": 100,
"site B": 150,
"site C": 200,
},
{
"week_ending_date": "2022-10-09",
"site A": 400,
"site B": 150,
"site C": 50,
},
...
So in my first column, I would want site C to appear at the top of it, and in the second column I want site A to be at the top.
Anyone know if this is possible and how to achieve it?
Thanks in advance.
I've tried searching through the documentation but I cant find any properties that would allow me to reorder segments.
答案1
得分: 1
以下是您要翻译的内容:
"可能的解决方案是预处理数据,将条目排序到数据结构中,并将列设置为浮动而不是堆叠。 这意味着为每个柱状图(即每个数据点)设置起始y值和结束y值;这允许我们按顺序放置排序的列:最短柱的末端将是第二短柱的起始,第二短柱的末端将是最长柱的起始。 这是基于您的代码的片段,实现了这种方法:
<!-- begin snippet: js hide: false console: false babel: false -->
<!-- language: lang-js -->
// var chartDiv = document.getElementById(selector);
// const data = JSON.parse(chartDiv.getAttribute('data-chart'));
const data = [
{
"week_ending_date": "2022-10-02",
"site A": 100,
"site B": 150,
"site C": 200,
},
{
"week_ending_date": "2022-10-09",
"site A": 400,
"site B": 150,
"site C": 50,
},
{
"week_ending_date": "2022-10-16",
"site A": 200,
"site B": 150,
"site C": 200,
},
{
"week_ending_date": "2022-10-23",
"site A": 120,
"site B": 150,
"site C": 200,
}];
const fontSize = 12;
// const markerSize = 12;
var root = am5.Root.new(selector);
var chart = root.container.children.push(
am5xy.XYChart.new(root, {
panY: false,
layout: root.verticalLayout,
dy: 10,
})
);
// Create X-axis
let xAxis = chart.xAxes.push(
am5xy.CategoryAxis.new(root, {
categoryField: "week_ending_date",
renderer: am5xy.AxisRendererX.new(root, {})
})
);
let xRenderer = xAxis.get("renderer");
xRenderer.labels.template.setAll({
fontSize: fontSize
});
xAxis.data.setAll(data);
// Create Y-Axis
var yAxis = chart.yAxes.push(
am5xy.ValueAxis.new(root, {
renderer: am5xy.AxisRendererY.new(root, {}),
})
);
let yRenderer = yAxis.get("renderer");
yRenderer.labels.template.setAll({
fontSize: fontSize
});
// Add series
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
function makeSeries(name, fieldName, colorHex) {
var series = chart.series.push(am5xy.ColumnSeries.new(root, {
name: name,
//stacked: false, // default
clustered: false,
xAxis: xAxis,
yAxis: yAxis,
baseAxis: xAxis,
trueValue: data,
valueYField: fieldName+'_end',
openValueYField: fieldName+'_start',
categoryXField: "week_ending_date",
fill: colorHex,
}));
series.columns.template.setAll({
tooltipText: "{name} - {"+fieldName+"}",
tooltipY: am5.percent(90),
});
series.data.setAll(data);
series.bullets.push(function() {
return am5.Bullet.new(root, {
sprite: am5.Label.new(root, {
text: "{valueX}",
fill: root.interfaceColors.get("alternativeText"),
centerY: am5.p50,
centerX: am5.p50,
populateText: true,
fontSize: 12,
})
});
});
const legend = chart.children.push(am5.Legend.new(root, {}));
legend.data.push(series);
}
// Get all site names
let sites = Object.keys(data[0])
// Remove first value (week_ending_date)
sites.shift()
// preprocess data
const dataEntriesSorted = data.map(
o => Object.entries(o).filter(([_, v]) => typeof v === "number").
sort(([_key1, v1], [_key2, v2]) => v1 - v2)
);
dataEntriesSorted.forEach(function(entries, i){
let sum = 0;
entries.forEach(function([key, val]){
data[i][key+'_start'] = sum; // the start of the bar
data[i][key+'_end'] = val + sum; // the end of the bar
sum += data[i][key];
})
});
// Add series for each site in the data
sites.forEach((site, index) => {
makeSeries(site, site, chart.get("colors").next());
});
<!-- language: lang-html -->
<div id="selector" style="width: 400px;height: 400px"></div>
<script src="https://cdn.amcharts.com/lib/5/index.js"></script>
<script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
<!-- end snippet -->
英文:
A possible solution is to preprocess the data, sort the entries in a data structure and make the columns floating rather then stacked.
This means setting a starting y value and an ending y value for each bar (that is for each data point); this allows us to place sorted columns in order: the end of the shortest will be the start of the second shortest and the end of the second shortest will be the start of the longest bar.
Here's a snippet based on your code that implements this method:
<!-- begin snippet: js hide: false console: false babel: false -->
<!-- language: lang-js -->
//var chartDiv = document.getElementById(selector);
//const data = JSON.parse(chartDiv.getAttribute('data-chart'));
const data = [
{
"week_ending_date": "2022-10-02",
"site A": 100,
"site B": 150,
"site C": 200,
},
{
"week_ending_date": "2022-10-09",
"site A": 400,
"site B": 150,
"site C": 50,
},
{
"week_ending_date": "2022-10-16",
"site A": 200,
"site B": 150,
"site C": 200,
},
{
"week_ending_date": "2022-10-23",
"site A": 120,
"site B": 150,
"site C": 200,
}];
const fontSize = 12;
//const markerSize = 12;
var root = am5.Root.new(selector);
var chart = root.container.children.push(
am5xy.XYChart.new(root, {
panY: false,
layout: root.verticalLayout,
dy: 10,
})
);
// Create X-axis
let xAxis = chart.xAxes.push(
am5xy.CategoryAxis.new(root, {
categoryField: "week_ending_date",
renderer: am5xy.AxisRendererX.new(root, {})
})
);
let xRenderer = xAxis.get("renderer");
xRenderer.labels.template.setAll({
fontSize: fontSize
});
xAxis.data.setAll(data);
// Create Y-Axis
var yAxis = chart.yAxes.push(
am5xy.ValueAxis.new(root, {
renderer: am5xy.AxisRendererY.new(root, {}),
})
);
let yRenderer = yAxis.get("renderer");
yRenderer.labels.template.setAll({
fontSize: fontSize
});
// Add series
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
function makeSeries(name, fieldName, colorHex) {
var series = chart.series.push(am5xy.ColumnSeries.new(root, {
name: name,
//stacked: false, // default
clustered: false,
xAxis: xAxis,
yAxis: yAxis,
baseAxis: xAxis,
trueValue: data,
valueYField: fieldName+'_end',
openValueYField: fieldName+'_start',
categoryXField: "week_ending_date",
fill: colorHex,
}));
series.columns.template.setAll({
tooltipText: "{name} - {"+fieldName+"}",
tooltipY: am5.percent(90),
});
series.data.setAll(data);
series.bullets.push(function() {
return am5.Bullet.new(root, {
sprite: am5.Label.new(root, {
text: "{valueX}",
fill: root.interfaceColors.get("alternativeText"),
centerY: am5.p50,
centerX: am5.p50,
populateText: true,
fontSize: 12,
})
});
});
const legend = chart.children.push(am5.Legend.new(root, {}));
legend.data.push(series);
}
// Get all site names
let sites = Object.keys(data[0])
// Remove first value (week_ending_date)
sites.shift()
// preprocess data
const dataEntriesSorted = data.map(
o => Object.entries(o).filter(([_, v]) => typeof v === "number").
sort(([_key1, v1], [_key2, v2]) => v1 - v2)
);
dataEntriesSorted.forEach(function(entries, i){
let sum = 0;
entries.forEach(function([key, val]){
data[i][key+'_start'] = sum; // the start of the bar
data[i][key+'_end'] = val + sum; // the end of the bar
sum += data[i][key];
})
});
// Add series for each site in the data
sites.forEach((site, index) => {
makeSeries(site, site, chart.get("colors").next());
});
<!-- language: lang-html -->
<div id="selector" style="width: 400px;height: 400px"></div>
<script src="https://cdn.amcharts.com/lib/5/index.js"></script>
<script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论