javascript - 如何根据下拉菜单中的选择呈现条形图?
问题描述
我正在尝试在 d3.js 中创建交互式条形图。此数据是一个 .csv 文件,其中存储每个 BIB(即运动员)的所有跑步。我已经使用d3.group()按 BIB 分组,但我不知道如何在下拉菜单中选择 BIB,以便显示该特定运动员的跑步。我是 d3.js 的新手,所以我感谢所有输入和帮助。现在,该图如下所示:
如您所见,我可以在控制台中记录特定的 BIB,但我不明白如何从这里为选定的 BIB 呈现仅显示其运行的图表。我想我很接近
这是存储我所有代码的 index.html:
const data = d3.range(20).map(i => ({
BIB: Math.floor(i / 5) + 1,
RATIO: -1 + Math.random() * 2,
RUN: [1, 2, 3, 4, 5][i % 5],
NAME: ['RASTAD', 'SANDENGEN', 'NYBORG', 'ANDERSEN'][Math.floor(i / 5)]
}));
// 1. This is the DropDownMenu
const dropdown = (selection, props) => {
const {
options,
onOptionClicked
} = props
let select = selection.selectAll("select").data([null]);
select.enter().append("select").merge(select)
.on("change", function() {
console.log(this.value)
})
let option = select.selectAll("option").data(options);
option.enter().append("option")
.merge(option)
.attr("value", d => d)
.text(d => d)
}
// 2. This is the SVG element
const height = 300
const width = 700
const margin = {
top: 20,
right: 20,
bottom: 20,
left: 50
}
// 3. THis is where I define the margin convention
const innerWidth = width - margin.left - margin.right
const innerHeight = height - margin.top - margin.bottom
const svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
let selectBib;
let onSelectBibClicked = BIB => {
selectBib = BIB;
render();
}
// 4. This is the render function that creates the bar chart
const render = () => {
d3.select("body").call(dropdown, {
options: groupBySkiers,
onOptionClicked: onSelectBibClicked,
});
const yScale = d3.scaleLinear()
.domain([-3, 3])
.range([innerHeight, 0]);
const xScale = d3.scaleBand()
.domain(data.map(d => d.RUN))
.range([0, innerWidth])
.padding(0.1)
const yAxis = d3.axisLeft(yScale)
const xAxis = d3.axisTop(xScale)
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
g.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr('v', d => d.RATIO)
.attr("x", d => xScale(d.RUN))
.attr("width", xScale.bandwidth())
.attr("height", d => d.RATIO < 0 ? yScale(d.RATIO) - yScale(0) : yScale(0) - yScale(d.RATIO))
.attr("y", d => d.RATIO < 0 ? yScale(0) : yScale(d.RATIO))
.attr("fill", d => d.RATIO < 0 ? '#2ec1ac' : 'red')
.append("title")
.text(function(d) {
return d.RATIO
})
yAxis(g.append("g").attr("class", "axis"))
xAxis(g.append("g").attr("class", "axis").attr("transform", `translate(0,${yScale(-3)})`))
}
groupBySkiers = d3.group(data, d => d.BIB)
render()
label {
font-size: larger;
font-family: sans-serif;
}
#startnummer {
font-family: sans-serif;
font-size: larger;
}
.axis {
font-size: larger;
font-family: sans-serif;
}
rect:hover {
opacity: 0.6;
}
<script src="https://d3js.org/d3.v6.min.js"></script>
<label for="bibnumber">Startnummer :</label>
<select name="STARTNUMMER" id="startnummer">
<option value="volvo">1</option>
</select>
原始 .csv 文件可以从这里下载
解决方案
我对你的图表做了一些改进。首先,我给下拉列表提供了更好的标签,并将值更改为每个运动员的 BIB。
我还将代码拆分为一个render
和一个update
函数,因此一次性代码可以从常规代码中分离出来。
您不需要d3.group
,因为您不需要值,只需要唯一的 BIB 编号。因此,您可以将其更改为使用 ES6 Set、对象或独特的函数,如下所示:
// This works because `indexOf` returns the first match.
// Any other matches have a different index `i` than the first match
const uniqueValues = arrayWithDuplicates.filter((d, i, arr) => arr.indexOf(d) === i));
selectBib
最后,如果您只使用一次,则无需存储变量。仅当您想在不是由下拉更改触发的过程中查找值时才需要。
const data = d3.range(20).map(i => ({
BIB: Math.floor(i / 5) + 1,
RATIO: -1 + Math.random() * 2,
RUN: [1, 2, 3, 4, 5][i % 5],
NAME: ['RASTAD', 'SANDENGEN', 'NYBORG', 'ANDERSEN'][Math.floor(i / 5)]
}));
// 1. This is the DropDownMenu
const dropdown = (options) => {
let select = d3.select('select')
.on("change", function() {
const bib = Number(d3.select(this).property('value'));
update(bib)
})
let option = select.selectAll("option").data(Object.entries(options));
option.enter()
.append("option")
.merge(option)
.attr("value", ([bib, name]) => bib)
.text(([bib, name]) => `${bib} (${name})`)
}
// 2. This is the SVG element
const height = 300
const width = 700
const margin = {
top: 20,
right: 20,
bottom: 20,
left: 50
}
// 3. THis is where I define the margin convention
const innerWidth = width - margin.left - margin.right
const innerHeight = height - margin.top - margin.bottom
let g, xScale, yScale
// 4. This is the render function that creates the bar chart
const render = () => {
const svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
dropdown(uniqueSkiers);
yScale = d3.scaleLinear()
.domain([-3, 3])
.range([innerHeight, 0]);
xScale = d3.scaleBand()
.domain(data.map(d => d.RUN))
.range([0, innerWidth])
.padding(0.1)
const yAxis = d3.axisLeft(yScale)
const xAxis = d3.axisTop(xScale)
g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
g.append("g")
.attr("class", "axis")
.call(yAxis)
g.append("g")
.attr("class", "axis")
.attr("transform", `translate(0,${yScale(-3)})`)
.call(xAxis)
update(data[0].BIB)
}
const update = bib => {
const filteredData = data.filter(d => d.BIB === bib);
const rect = g.selectAll("rect")
.data(filteredData, d => d.RUN);
rect.exit().remove();
rect
.enter()
.append("rect")
.merge(rect)
.attr('v', d => d.RATIO)
.attr("x", d => xScale(d.RUN))
.attr("width", xScale.bandwidth())
.attr("height", d => d.RATIO < 0 ? yScale(d.RATIO) - yScale(0) : yScale(0) - yScale(d.RATIO))
.attr("y", d => d.RATIO < 0 ? yScale(0) : yScale(d.RATIO))
.attr("fill", d => d.RATIO < 0 ? '#2ec1ac' : 'red')
.append("title")
.text(function(d) {
return d.RATIO
})
}
const uniqueSkiers = data.reduce((obj, {
BIB,
NAME
}) => {
obj[BIB] = NAME;
return obj;
}, {});
render()
label {
font-size: larger;
font-family: sans-serif;
}
#startnummer {
font-family: sans-serif;
font-size: larger;
}
.axis {
font-size: larger;
font-family: sans-serif;
}
rect:hover {
opacity: 0.6;
}
<script src="https://d3js.org/d3.v6.min.js"></script>
<label for="bibnumber">Startnummer :</label>
<select name="STARTNUMMER" id="startnummer">
<option value="volvo">1</option>
</select>
推荐阅读
- python - 检验假设的 P 值显示 NaN
- python - 使用存储在另一列中的值每行选择一列数据框并修改它
- swift - NSTableView:根据用户对 NSTableView 中单元格的编辑更新数据模型
- php - Symfony 5 - OneToMany 没有出现在我的 mysql 数据库中
- reactjs - 使用代码拆分反应错误(参数类型函数()不可分配给参数类型)
- python - 在 ggplot 中创建 3D 条形图
- java - 为什么Eclipse中会出现“选择无法启动且最近没有启动”的提示?
- python - 使用 Pip 与 Python 3.9 一起安装后导入模块时出错
- python - 派斯帕克;将一列列表拆分为多列
- batch-file - 使用批处理按名称匹配在文件夹之间移动文件