提问人:MicrowavableFrenchfries 提问时间:11/15/2023 更新时间:11/17/2023 访问量:93
获取分组条形图 y 域的 d3.max 值
Getting d3.max value for grouped bar chart y domain
问:
我创建了一个分组条形图,并尝试将 y 域设置为最大条形值。这样做,而不是必须根据数据更改的静态数字。我对如何使用 CSV 文件中的多列感到困惑。我正在尝试使它,以便您不必使用特定的列名,因为在其他 CSV 文件中可能有所不同。现在,列名称为 cat1、cat2 和 cat3。我正在使用 D3 v7d3.max()
数据
name,cat1,cat2,cat3
Item 1,50,102,302
Item 2,79,140,330
Item 3,200,180,120
Item 4,104,80,83
Item 5,90,320,130
Item 6,85,114,130
我有点了解如何使用,但绝对不是在这种情况下。d3.max()
这是我目前使用它的方式。你会看到我在 y 域中有一个集合。我现在只是在尝试控制台日志最大值。我意识到我没有正确地做到这一点,但不确定该怎么做。350
第3天
const data = await d3.csv(src);
console.log(d3.max(data, d => {
return d;
}));
// returns {name: 'Item 1', cat1: '50', cat2: '102', cat3: '302'}
// Would like to use the max value
// rather than 350
y.domain([350, 0])
.range([0, height]);
谢谢你的帮助。
答:
0赞
Akash Ram
11/15/2023
#1
let data = [{
name: 'Item 1',
cat1: 50,
cat2: 102,
cat3: 302
},
{
name: 'Item 2',
cat1: 79,
cat2: 140,
cat3: 330
},
{
name: 'Item 3',
cat1: 200,
cat2: 180,
cat3: 120
},
{
name: 'Item 4',
cat1: 104,
cat2: 80,
cat3: 83
},
{
name: 'Item 5',
cat1: 90,
cat2: 320,
cat3: 130
},
{
name: 'Item 6',
cat1: 85,
cat2: 114,
cat3: 130
}
]
let max = d3.max(data, ((d)=> (d.cat1,d.cat2,d.cat3)));
let result = data.find((d) => d.cat1 == max || d.cat2 == max || d.cat3 == max);
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
评论
0赞
MicrowavableFrenchfries
11/16/2023
感谢您的回复。您是说 CSV 数据需要转换为对象数组,还是需要导入为 JSON?是否必须在 d3.max 和 data.find 中指定键名?
0赞
Jeremy Caney
11/16/2023
感谢您对 Stack Overflow 社区的贡献。这可能是一个正确的答案,但提供代码的额外解释,以便开发人员能够理解你的推理,这将是非常有用的。这对于不熟悉语法或难以理解概念的新开发人员特别有用。为了社区的利益,您能否编辑您的答案以包含其他详细信息?
0赞
Akash Ram
11/16/2023
在 D3.js 中使用 d3.csv 方法时,将自动分析生成的数据并将其转换为 JSON 格式。因此,一旦使用 d3.csv(),就可以像处理 JSON 格式一样处理数据。@MicrowavableFrenchfries
0赞
MicrowavableFrenchfries
11/16/2023
@AkashRam 感谢您澄清这一点。它仍然没有回答我的问题。有没有办法在不指定其列名的情况下找到这些键的最大值?例如,不同 csv 文件中的列名可能不相同。不仅如此,我怎样才能在 y 域中使用该最大值并使其适合 svg 的高度?我已经在我的代码笔中使用了你的代码片段,你会看到条形图延伸到svg高度之外。请参阅第 73-79 行 codepen.io/MicrowavableFrenchfries/pen/KKJyBmG/...
0赞
Akash Ram
11/17/2023
我根据您的要求提供新的解决方案。请检查it.@MicrowavableFrenchfries
0赞
Akash Ram
11/17/2023
#2
const margin = { top: 20, right: 50, bottom: 50, left: 70 };
const wrap = d3.select('#chart-wrap');
let wrapWidth = parseInt(wrap.style('width'));
let width = wrapWidth - margin.left - margin.right;
const wrapHeight = parseInt(wrap.style('height'));
const height = wrapHeight - margin.top - margin.bottom;
let subgroup;
const y = d3.scaleLinear();
const x0 = d3.scaleBand();
const x1 = d3.scaleBand();
const src = 'https://assets.codepen.io/1329727/data-multi-demo.csv';
const colors = d3.scaleOrdinal()
.range(['#5626C4', '#E60576', '#2CCCC3', '#FACD3D', '#181818']);
let tooltipChart;
// Tooltip
const tooltipMouseMove = (key, value, loc) => {
tooltipChart
.html(() => {
return (
`<div class="chart-tooltip-wrap">
<p><strong>${key}</strong></p>
<p>${value}</p>
</div>
`
);
})
.style('visibility', 'visible')
.style('left', `${d3.pointer(event)[0] + loc}px`)
.style('top', `${d3.pointer(event)[1] + 20}px`)
}
const tooltipMouseOut = () => {
tooltipChart
.style('visibility', 'hidden');
}
const svg = wrap.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
// SVG aria tags
svg.append('title')
.attr('id','chart-title')
.html('Group, verical bar chart');
svg.append('desc')
.attr('id','chart-desc')
.html('Displays grouped bar charts for different items.');
svg.attr('aria-labelledby','chart-title chart-desc');
tooltipChart = wrap.append('div')
.attr('class','chart-tooltip')
.style('visibility', 'hidden');
const group = svg.append('g')
.attr('class', 'group')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
async function createGroupBars() {
const data = await d3.csv(src);
// Keys and group keys
const keys = data.columns.slice(1);
const groupKey = data.columns[0];
const values = [];
data.forEach((el)=> {
for(let i = 0; i < keys.length; i++) {
values.push(+el[keys[i]])
}
})
// Bar width
const barWidth = (width / groupKey.length);
const isSame = (data,keys) => {
let output = false;
for(let i=0; i<keys.length;i++) {
if(data[keys[i]] == max){
output = true;
break;
}
}
return output;
}
// Getting the maximum value from the keys
let max = d3.max(values);
let result = data.find((d) => {
return isSame(d,keys);
});
// Scales
y.domain([max, 0])
.range([0, height]);
x0.domain(data.map(d => { return d[groupKey]; }))
.range([0, width])
.paddingInner(0.3);
if (width < 400) {
x0.paddingInner(0.15);
}
x1.domain(keys)
.range([0, x0.bandwidth()]);
// X Axis
const xAxis = group.append('g')
.attr('class', 'x-axis')
.attr('transform', `translate(0, ${height})`)
.call(
d3.axisBottom(x0)
.tickSize(7)
);
xAxis.call(g => g.select('.domain').remove());
// X axis labels
xAxis.selectAll('text')
.attr('transform', 'translate(-10, 0) rotate(-45)')
.style('text-anchor', 'end');
// Y Axis
group.append('g')
.attr('class', 'y-axis')
.call(
d3.axisLeft(y)
.tickSizeOuter(0)
.tickSize(-width)
);
d3.selectAll('.y-axis text')
.attr('x', -10)
// Subgroups of rectangles
subgroup = group.selectAll('.subgroup')
.data(data)
.join('g')
.attr('class', 'subgroup')
.attr('transform', (d) => {
return `translate(${x0(d[groupKey])}, 0)`;
})
.attr('aria-label', d => {
return `Values for ${d.name}`
})
// Rectangles
subgroup
.selectAll('rect')
.data(d => {
return keys.map(key => {
return { key: key, value: d[key] }
});
})
.join('rect')
.attr('class', 'rect')
.attr('y', d => y(d.value))
.attr('x', d => { return x1(d.key); })
.attr('height', (d) => { return height - y(d.value); })
.attr('width', x1.bandwidth())
.attr('fill', (d, i) => { return colors(d.key); })
.attr('aria-label', d => {
return `${d.key} bar`;
})
.on('mousemove', function (event, d, i) {
// Get parent's translate x value
const parent = d3.select(this.parentNode).attr('transform').slice(10);
const loc = parseFloat(parent);
// call tooltip function
tooltipMouseMove(d.key, d.value, loc);
})
.on('mouseout', () => {
tooltipMouseOut();
});
// Rectangle labels
subgroup.selectAll('.bar-labels')
.data(d => {
return keys.map(key => {
return { key: key, value: d[key] }
});
})
.join('text')
.attr('class', 'bar-labels')
.attr('y', d => { return y(d.value) - 3 })
.attr('x', d => { return x1(d.key) + 12; })
.attr('text-anchor', 'middle')
.style('fill', '#181818')
.text(d => { return d.value });
// Legend
const createLegend = (parent, cat) => {
parent.append('div')
.attr('class', 'legend')
.selectAll('div')
.data(data.columns.slice(1))
.enter()
.append('div')
.attr('class', 'legend-group')
.html((d, i) => {
return(`
<div class="legend-box" style="background-color: ${colors(cat[i])};"></div>
<p class="legend-label">${cat[i]}</p>
`);
});
}
createLegend(wrap, Object.keys(data[0]).slice(1));
// Resize
const resize = () => {
wrapWidth = parseInt(wrap.style('width'));
width = wrapWidth - margin.left - margin.right;
x0.range([0, width])
.paddingInner(0.3);
if (width < 400) {
x0.paddingInner(0.15);
}
x1.range([0, x0.bandwidth()]);
svg.attr('width', width + margin.left + margin.right);
subgroup.selectAll('.rect')
.attr('x', d => { return x1(d.key); })
.attr('width', x1.bandwidth());
subgroup.selectAll('.bar-labels')
.attr('x', d => { return x1(d.key) + 12; })
group.select('.x-axis')
.attr('transform', `translate(0, ${height})`)
.call(
d3.axisBottom(x0)
);
group.select('.y-axis')
.call(
d3.axisLeft(y)
.tickSizeOuter(0)
.tickSize(-width)
);
subgroup.attr('transform', (d) => {
return `translate(${x0(d[groupKey])}, 0)`;
});
}
d3.select(window).on('resize', resize);
}
createGroupBars();
body {
background-color: #f7f4e9;
font-family: sans-serif;
}
.chart-section {
margin: 2rem auto;
padding: 1rem;
width: calc(100% - 2rem);
max-width: 700px;
}
.chart-section #chart-wrap {
height: 400px;
width: 100%;
position: relative;
}
.chart-section #chart-wrap .chart-tooltip {
margin-left: 10px;
position: absolute;
z-index: 10;
}
.chart-section #chart-wrap .chart-tooltip .chart-tooltip-wrap {
background-color: #181818;
border-radius: 10px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
display: block;
padding: 0.875rem;
}
.chart-section #chart-wrap .chart-tooltip .chart-tooltip-wrap p {
color: #fff;
font-size: 0.875rem;
line-height: 1.75;
margin: 0;
}
.chart-section #chart-wrap svg {
margin: auto;
}
.chart-section #chart-wrap svg .y-axis .domain {
display: none;
}
.chart-section #chart-wrap svg .y-axis .tick line {
stroke: #aaa;
stroke-dasharray: 3, 3;
}
.chart-section #chart-wrap svg .y-axis .tick:last-child line {
stroke: #555;
stroke-dasharray: 0;
}
.chart-section #chart-wrap svg .bar-labels {
font-size: 0.875rem;
display: block;
}
@media (max-width: 700px) {
.chart-section #chart-wrap svg .bar-labels {
display: none;
}
}
.chart-section #chart-wrap svg .tick line {
stroke: #555;
}
.chart-section #chart-wrap svg .tick text {
font-size: 0.875rem;
}
.chart-section #chart-wrap .legend {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
margin: 2rem auto;
width: 100%;
}
.chart-section #chart-wrap .legend .legend-group {
align-items: center;
display: flex;
flex-basis: 100px;
flex-direction: row;
gap: 8px;
justify-content: flex-start;
}
.chart-section #chart-wrap .legend .legend-group .legend-box {
height: 20px;
margin: 0;
width: 20px;
}
.chart-section #chart-wrap .legend .legend-group .legend-label {
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
<section class="chart-section">
<div id="chart-wrap"></div>
</section>
代码说明
我修改了容器中 SVG 的代码
// increase bottom value
const margin = { top: 20, right: 50, bottom: 20, left: 70 };
// change zero translate y value to ${margin.top}
const group = svg.append('g')
.attr('class', 'group')
.attr('transform', `translate(${margin.left}, ${margin.top})`)
并从 CSV 文件中获取动态最大值
const values = [];
data.forEach((el)=> {
for(let i = 0; i < keys.length; i++) {
values.push(+el[keys[i]])
}
})
// Bar width
const barWidth = (width / groupKey.length);
const isSame = (data,keys) => {
let output = false;
for(let i=0; i<keys.length;i++) {
if(data[keys[i]] == max){
output = true;
break;
}
}
return output;
}
// Getting the maximum value from the keys
let max = d3.max(values);
let result = data.find((d) => {
return isSame(d,keys);
});
注意:我不关注代码的时间复杂度;我只是想解决问题。
评论
0赞
MicrowavableFrenchfries
11/18/2023
这太完美了。正是我想要的。感谢您的帮助!
0赞
Akash Ram
11/18/2023
如果它能帮助你解决你的问题,请通过投票给答案来将帮助传播给其他人。@MicrowavableFrenchfries
评论