如何解决如何使用汇总为 d3 堆叠条形图准备数据
我是 JavaScript 和 D3.js 的新手,我正在尝试创建一个 Angular 应用程序,它带有堆叠条形图,将每天的不同测试结果“OK”、“NOK”和“Aborted”的数量可视化为一个堆叠y 轴为 bar,x 轴为日期。
我的 data.csv 如下所示:
Date;Result
20-05-2021 17:54:02;Aborted
20-05-2021 17:55:24;OK
21-05-2021 21:48:45;NOK
22-05-2021 17:55:24;OK
22-05-2021 17:54:02;Aborted
22-05-2021 17:55:24;OK
因为我需要计算每天的结果,所以我首先使用 timeParse 将日期解析为正确的格式,然后使用 timeDay.floor 方法摆脱时间:
let jsonObj = await d3.dsv(";","/assets/data.csv",function (d) {
let time = timeParse("%d-%m-%Y %-H:%M:%s")(d['Date'])
let date = timeDay.floor(new Date(time));
return {
Date: date,Result: d.Result
};
})
如果我理解正确的话,这会给我一个带有 用于每个测试结果。
现在总结我使用汇总的同一天的测试结果的计数:
let data_count = rollup(jsonObj,v => v.length,d => d.Date,d => d.Result)
现在我有一个嵌套的 Map,以日期(每天只有一个)为键,将值作为测试结果,每个值都带有当天的总和。
我有几个关于如何继续的例子,不需要使用汇总方法,例如here 并尝试使其适应我的地图:
let processed_data = data_count.map( d => {
let y0 = 0;
let total = 0;
return {
date: d.key,//call second mapping function on every test-result
values: (d.valules).map( d => {
let return_object = {
result: d.key,count: d.values,y0: y0,y1: y0 + d.values
};
//calculate the updated y0 for each new test-result on a given date
y0 = y0 + d.values;
//add the total for a given test-result to the sum total for that test-result
total = total + d.values;
return return_object;
}),total: total
};
});
Property 'map' does not exist on type 'InternMap<Date,InternMap<string,number>>'.ts(2339)
我知道地图功能不能在地图上使用,我猜。 我还尝试将这部分重写为单独的函数以不使用 map 函数,但它也不起作用。也许我有一些语法错误或什么,但我得到:
TypeError: Cannot read property 'key' of undefined
我可能需要对地图的值使用 get() 方法,但不确定如何实现它。
现在我不知道应该如何继续为堆积条形图准备数据。在 bl.ocks.org 的这个示例中,CSV 看起来不同。我正在考虑以某种方式操纵我的数据以适应该示例的形状:
Date;OK;NOK;Aborted
20-05-2021 17:54:02;1;0;1
21-05-2021 21:48:45;0;1;0
22-05-2021 17:55:24;2;0;1
但是我不知道如何去做。 欢迎任何有关如何准备我的数据的帮助。也许我不应该使用 rollup 方法,但我觉得它非常适合我的需求。
解决方法
如果我们的想法是您最终希望利用一些现有的代码示例,因此需要这样的数据:
Date;OK;NOK;Aborted
20-05-2021 17:54:02;1;0;1
21-05-2021 21:48:45;0;1;0
22-05-2021 17:55:24;2;0;1
有几件事情需要考虑:
-
您正在将数据从密集转换为稀疏,因为您需要为例如创建零数据点
NOK
上的20-05-2021
,因为原始数据中不存在该数据点。 -
您需要不同的
Result
值作为转换数据中的行标题。您可以通过以下方式获得:const columns = [...new Set(data.map(d => d.Result))];
-
您发现不能在
Array.protoype.map
对象上使用Map
,因此您只需要考虑其他选项(下面列出的两个)来迭代Map
对象例如使用Map.prototype.entries()
或Map.prototype.forEach
。
要使用 d3.rollup
实现这种重新整形的数据:
-
将
d3.rollup
语句包装在Array.from(...)
中,这样您就可以获得Map.prototype.entries()
,您可以将其传递给reduce
函数。 -
在
reduce
函数中,您可以访问外部[key,value]
的Map
对,其中value
本身就是一个Map
(由d3.rollup
) -
然后迭代
columns
(Result
的不同之处)以评估您是否需要获取内部Map
中的值(当天的总和 {{ 1}}) 或插入Result
,因为那天没有发生0
(每点 (1))。在示例中,这一行:
Result
意思是:对于一个列标题,如果内部
resultColumns.map(col => row[col] = innerMap.has(col) ? innerMap.get(col) : 0);
Map
那个列标题作为键,那么has
这个值键,否则为零。
工作示例:
get
// your data setup
const csv = `Date;Result
20-05-2021 17:54:02;Aborted
20-05-2021 17:55:24;OK
21-05-2021 21:48:45;NOK
22-05-2021 17:55:24;OK
22-05-2021 17:54:02;Aborted
22-05-2021 17:55:24;OK`;
// your data processing
const data = d3.dsvFormat(";").parse(csv,d => {
const time = d3.timeParse("%d-%m-%Y %-H:%M:%S")(d.Date);
const date = d3.timeDay.floor(new Date(time));
return {
Date: date,Result: d.Result
}
});
// distinct Results for column headers per point (2)
const resultColumns = [...new Set(data.map(d => d.Result))];
// cast nested Maps to array of objects
const data_wide = Array.from( // get the Map.entries()
d3.rollup(
data,v => v.length,d => d.Date,d => d.Result
)
).reduce((accumlator,[dateKey,innerMap]) => {
// create a 'row' with a Date property
let row = {Date: dateKey}
// add further properties to the 'row' based on existence of keys in the inner Map per point (1)
resultColumns.map(col => row[col] = innerMap.has(col) ? innerMap.get(col) : 0);
// store and return the accumulated result
accumlator.push(row);
return accumlator;
},[]);
console.log(data_wide);
.as-console-wrapper { max-height: 100% !important; top: 0; }
如果您更喜欢更程序化(也许更易读)的方式来获得相同的结果,您可以避免使用 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
并使用 Array.from(...)
的 forEach
方法(这是不同的从 Array.prototype.forEach
) 迭代外部 Map
,然后执行类似的操作来评估是否需要创建零数据点:
Map
// your data setup
const csv = `Date;Result
20-05-2021 17:54:02;Aborted
20-05-2021 17:55:24;OK
21-05-2021 21:48:45;NOK
22-05-2021 17:55:24;OK
22-05-2021 17:54:02;Aborted
22-05-2021 17:55:24;OK`;
// your data processing
const data = d3.dsvFormat(";").parse(csv,Result: d.Result
}
});
// distinct Results for column headers per point (2)
const resultColumns = [...new Set(data.map(d => d.Result))];
// rollup the data
const rolled = d3.rollup(
data,d => d.Result
);
// create an output array
const data_wide = [];
// populate the output array
rolled.forEach((innerMap,dateKey) => {
// create a 'row' with the date property
const row = {Date: dateKey}
// add further properties to the 'row' based on existence of keys in the inner Map per point (1)
resultColumns.map(col => row[col] = innerMap.has(col) ? innerMap.get(col) : 0);
// store the result
data_wide.push(row);
});
console.log(data_wide);
.as-console-wrapper { max-height: 100% !important; top: 0; }
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。