JavaScript实现时间序列上采样 生成视觉一致的m元素序列算法
时间序列扩点简洁实现方案
核心思路
要实现长度为n的时间序列扩充到m(m>n)、绘图后视觉高度相似,最简洁可靠的方案是分段线性插值:
- 所有新生成的点全部落在原序列相邻两点的连线上,绘制折线图时和原图形完全重合,无视觉偏差
- 逻辑简单,无复杂计算,性能极高
- 自动对齐原序列首尾时间点,不会出现整体偏移
实现代码
function interpolateTimeSeries(original, targetLen) { // 目标长度小于等于原长度时,直接返回按时间排序的原数据副本 if (targetLen <= original.length) { return [...original].sort((a, b) => a.time - b.time) } // 原数据按时间升序排序,兼容乱序输入 const sorted = [...original].sort((a, b) => a.time - b.time) const timeStart = sorted[0].time const timeEnd = sorted.at(-1).time const timeStep = (timeEnd - timeStart) / (targetLen - 1) const result = [] let segIdx = 0 // 缓存当前分段索引,避免重复遍历 for (let i = 0; i < targetLen; i++) { const curTime = timeStart + i * timeStep // 定位当前时间点所属的原序列分段区间 while (segIdx < sorted.length - 2 && sorted[segIdx + 1].time < curTime) { segIdx++ } const pointA = sorted[segIdx] const pointB = sorted[segIdx + 1] // 首尾点直接取原值,规避浮点计算误差 if (i === 0) { result.push({ value: pointA.value, time: pointA.time }) continue } if (i === targetLen - 1) { result.push({ value: pointB.value, time: pointB.time }) continue } // 线性插值计算当前点数值 const ratio = (curTime - pointA.time) / (pointB.time - pointA.time) const curValue = pointA.value + ratio * (pointB.value - pointA.value) result.push({ value: curValue, time: curTime }) } return result }
使用示例
以给出的样例数据为例,将原长度为5的序列扩充为长度12的新序列:
const rawData = [ {value: 33209.203948532944, time: 1620178750}, {value: 33209.203948532944, time: 1620208647}, {value: 33610.0948868934, time: 1620219519}, {value: 34220.523450350825, time: 1620241262}, {value: 34220.523450350825, time: 1620242621} ] const expandedSeries = interpolateTimeSeries(rawData, 12) console.log(expandedSeries)
补充说明
如果需要绘制平滑曲线图而非折线图,可以将上述代码中的线性插值逻辑替换为三次样条插值,但线性插值已经完全满足“视觉表现高度相似”的要求,且没有过拟合、边缘震荡等插值常见问题,鲁棒性更强。
内容的提问来源于stack exchange,提问作者Jonathan Maim
相关产品推荐
相关产品推荐

