如何更优雅地将1分钟时间序列数组聚合为5分钟数据?
1分钟步长时间序列聚合为5分钟数据的简洁实现
作为编程初学者,我发现很多开发者都在讨论将1分钟步长的时间序列数据聚合为5分钟数据的问题。我需要把包含steps(步数)和created_at(时间戳)的1分钟数据数组转换为5分钟粒度的数据:对连续5分钟的steps求和,created_at取该5分钟时段的结束时间。目前我的实现方案较为繁琐,附上原数据、现有代码及期望结果,希望找到更简洁的单遍处理方法。
原数据
let array = [ { steps: 40, created_at: '2022-09-03T11:36:00.000Z' }, { steps: 13, created_at: '2022-09-03T11:37:00.000Z' }, { steps: 40, created_at: '2022-09-03T11:38:00.000Z' }, { steps: 40, created_at: '2022-09-03T11:39:00.000Z' }, { steps: 34, created_at: '2022-09-03T11:40:00.000Z' }, { steps: 86, created_at: '2022-09-03T11:41:00.000Z' }, { steps: 23, created_at: '2022-09-03T11:42:00.000Z' }, { steps: 78, created_at: '2022-09-03T11:43:00.000Z' }, { steps: 67, created_at: '2022-09-03T11:44:00.000Z' }, { steps: 80, created_at: '2022-09-03T11:45:00.000Z' }, { steps: 34, created_at: '2022-09-03T11:46:00.000Z' }, { steps: 64, created_at: '2022-09-03T11:47:00.000Z' }, { steps: 32, created_at: '2022-09-03T11:48:00.000Z' }, { steps: 78, created_at: '2022-09-03T11:49:00.000Z' }, { steps: 45, created_at: '2022-09-03T11:50:00.000Z' } ]
现有实现代码
const moment = require(`moment`); const newArray = array.map( (item)=> { const timestamp = moment(item.created_at).valueOf(); const timeStampgroup = Math.ceil((timestamp)/300000); return {...item, timeStampgroup: timeStampgroup} } ); const reducedArray = Array.from(newArray.reduce( (m, {timeStampgroup: timeStampgroup, steps}) => m.set(timeStampgroup, (m.get(timeStampgroup) || 0) + steps), new Map ), ([timeStampgroup, steps]) => ({timeStampgroup, steps})); const result = reducedArray.map(entry => ({steps : entry.steps, created_at : moment(entry.timeStampgroup*300000).toISOString()})); console.log(result);
期望结果
[ { steps: 167, created_at: '2022-09-03T11:40:00.000Z' }, { steps: 334, created_at: '2022-09-03T11:45:00.000Z' }, { steps: 253, created_at: '2022-09-03T11:50:00.000Z' } ]
简洁的单遍处理方案
方案1:基于时间分组(兼容数据缺失场景)
使用reduce单遍遍历数组,通过对象映射存储分组信息,最终转换为结果数组,无需多次遍历:
const moment = require('moment'); const result = Object.values(array.reduce((groups, item) => { // 计算当前数据所属5分钟时段的结束时间 const minute = moment(item.created_at).minute(); const endMinute = Math.ceil(minute / 5) * 5; const endTime = moment(item.created_at) .startOf('minute') .set('minute', endMinute) .toISOString(); // 更新分组的步数总和 if (!groups[endTime]) { groups[endTime] = { steps: 0, created_at: endTime }; } groups[endTime].steps += item.steps; return groups; }, {})); console.log(result);
方案2:基于索引分组(仅适用于连续无缺失的1分钟数据)
如果你的数据是严格连续的1分钟记录,没有缺失项,可以直接按每5个元素一组处理,代码更简洁:
const result = []; for (let i = 0; i < array.length; i += 5) { const group = array.slice(i, i + 5); const totalSteps = group.reduce((sum, item) => sum + item.steps, 0); // 取分组最后一个元素的created_at作为时段结束时间 result.push({ steps: totalSteps, created_at: group.at(-1).created_at }); } console.log(result);
这两个方案都只需要遍历数组一次(方案2的内部reduce是小范围遍历,整体复杂度更低),比原代码的三次遍历更高效简洁。
内容的提问来源于stack exchange,提问作者Lorenzo Sandini
相关产品推荐
相关产品推荐

