提问人:louis 提问时间:11/14/2023 最后编辑:louis 更新时间:11/14/2023 访问量:27
从许多对象的最新记录(Javascript 和 D3)中获取运行总和
Get Running Sum From Many Object's Most Recent Record (Javascript & D3)
问:
(注:底部更新了部分答案)
我希望创建一个 D3 折线图,其中包含许多对象的运行总和,这些对象的值会随时间而变化。
数据的结构是这样的......
const data = [
{"id": 1, "object": "A", "date": "2019-10-01", "value": 324},
{"id": 2, "object": "A", "date": "2019-10-06", "value": 123},
{"id": 3, "object": "A", "date": "2019-10-12", "value": 37},
{"id": 4, "object": "B", "date": "2019-09-24", "value": 16},
{"id": 5, "object": "B", "date": "2019-10-16", "value": 17},
{"id": 6, "object": "B", "date": "2019-10-24", "value": 14},
{"id": 7, "object": "B", "date": "2019-10-25", "value": 44},
{"id": 8, "object": "C", "date": "2019-10-02", "value": 62},
{"id": 9, "object": "C", "date": "2019-10-06", "value": 74},
{"id": 10, "object": "C", "date": "2019-10-16", "value": 64},
];
对于某个日期,例如 2019 年 10 月 12 日,我无法将该日期 (ID 3) 的更改与其他对象的最新值进行聚合。例如,2019-10-12 处的数据点应为 ID 3、ID 4 和 ID 9 的值之和。
下面的代码仅显示当天的分组值,不包括来自其他对象的聚合最新值。例如,对于 2019-10-16,它将 ID 5 和 ID 10 相加,但不包括 ID 3(它应该包括 ID 3)。
const svg = DOM.svg(500 , 500);
const parse = d3.timeParse("%Y-%m-%d");
data.forEach(d => {d.date = parse(d.date)});
const groupedData = d3.rollups(data, v => d3.sum(v, d => d.value), d => d.date)
const dateMinMax = d3.extent(groupedData, d => d[0]) // d[0] is date
const xScale = d3.scaleTime().domain(dateMinMax).range([0, 500])
const valueMinMax = d3.extent(groupedData, d => d[1]) // d[1] is value
const yScale = d3.scaleLinear().domain(valueMinMax).range([0, 500])
d3.select(svg).selectAll('circle').data(groupedData).enter()
.append('circle')
.attr('fill', 'green')
.attr('cy', d => yScale(d[1]))
.attr('cx', d => xScale(d[0]))
.attr("r", 5);
return svg
--------我试过了......--------
我制作了一个包含所有唯一日期的数组,然后用它来创建一个对象数组,其中包含当天发生的所有更改的curr_date和地图。我开始在注释掉的行上工作,我的想法是创建一个循环,该循环将查看从最近到最旧的先前日期,并将 ID 附加到映射中尚不存在的对象的curr_date“更改”映射中,但我觉得我走错了路, 因为循环遍历所有以前的日期似乎效率不高。
const parse = d3.timeParse("%Y-%m-%d");
rawData.forEach(d => {
d.date = parse(d.date)
});
const sortedData = rawData.sort((a, b) => d3.ascending(a.date, b.date));
const allActiveDates = sortedData.map(d => d.date.getTime())
const allActiveDatesNoDuplicates = [...new Set(allActiveDates)].map(d => new Date(d)) // remove duplicates.
const sortedDataLookup = d3.index(sortedData, d => d.date, d => d.id)
const changes = allActiveDatesNoDuplicates.map((curr_date, index, array) => {
//return array.slice(0,index).reverse().forEach()
return {curr_date, changes: sortedDataLookup.get(curr_date)}
});
return changes
我的另一个想法是做一个 ,所以我现在每行都有一个开始日期和一个结束日期。那我就可以做...(伪代码如下)... .但是,不要觉得添加 SQL 的开销也是正确的方法。LEAD(date) OVER (Partition by Object)
cumsum(value) by startdate - cumsum(value) by enddate
有什么想法吗?谢谢!
-------------------更新-------------------
下面的代码有效。它返回一个对象数组。每个对象都有一个日期,以及截至该日期的所有最新对象记录的数组。因此,我认为这很难变成我正在寻找的 d3 视觉效果。
但是,我觉得这个解决方案不能很好地扩展,我仍然希望得到指导。我不认为它会很好地扩展,因为 availableRecords 会变得非常大,并且每个映射中都有更多映射似乎有很多循环。我觉得这是一个相对简单的问题,我过于复杂了。
{
const rawData = [
{"id": 1, "object": "A", "date": "2019-10-01", "value": 324},
{"id": 2, "object": "A", "date": "2019-10-06", "value": 123},
{"id": 3, "object": "A", "date": "2019-10-12", "value": 37},
{"id": 4, "object": "B", "date": "2019-09-24", "value": 16},
{"id": 5, "object": "B", "date": "2019-10-16", "value": 17},
{"id": 6, "object": "B", "date": "2019-10-24", "value": 14},
{"id": 7, "object": "B", "date": "2019-10-25", "value": 44},
{"id": 8, "object": "C", "date": "2019-10-02", "value": 62},
{"id": 9, "object": "C", "date": "2019-10-06", "value": 74},
{"id": 10, "object": "C", "date": "2019-10-16", "value": 64},
];
const parse = d3.timeParse("%Y-%m-%d");
rawData.forEach(d => {
d.date = parse(d.date)
});
const sortedData = rawData.sort((a, b) => d3.ascending(a.date, b.date));
const days = d3.groups(sortedData, d => d.date) // 2D Array (date array[object array])
const changes = days.map((day, index, all_days) => {
const availableRecords = all_days.slice(0,index+1)//.reverse()
const availableRecordsFlat = availableRecords.map(d => d[1]).flat(1)
const availableObjects = [...new Set(availableRecordsFlat.map(d => d.object))]
const availableObjectsRecentRecord = availableObjects.map(d => {
const records = availableRecordsFlat.filter(r => r.object == d).sort((a, b) => d3.descending(a.date, b.date));
const recentRecord = records[0]
return {d, recentRecord}
});
const date = day[0]
return {date: date, currentRecords: availableObjectsRecentRecord}
});
return changes
}
答: 暂无答案
上一个:D3:如何“旋转”背景纹理和投影
评论