如何缩放整数列表以适配800点波形图展示?
波形数据降采样(保留形态与极值)的实用方案
你遇到的问题本质是固定点数的时间序列降采样——既要把从几十到百万级的波形数据压缩到800个点,又不能丢失整体趋势和关键极值。步长取点容易跳过峰值,邻域平均会抹平波形细节,以下是几个针对性的解决方案:
1. LTTB降采样算法(首选)
这个算法专门为波形/时间序列数据设计,能在固定输出点数的前提下,最大程度保留数据的形态、峰值和谷值。核心逻辑是将数据划分为800个桶,每个桶内选择一个点,使得相邻三个桶的点构成的三角形面积最大——这样能自动优先保留那些对波形形态影响最大的极值点。
操作步骤:
- 如果原数据长度 ≤800:直接输出所有数据点,无需降采样。
- 如果原数据长度 >800:
- 将数据均匀划分为800个桶,最后一个桶可容纳剩余的少量数据。
- 第一个桶取第一个点,最后一个桶取最后一个点。
- 对中间的每个桶,计算该桶内每个点与前一个已选点、后一个桶中点组成的三角形面积,选择面积最大的点作为该桶的代表点。
伪代码示例:
def lttb_downsample(data, n_out=800): data_len = len(data) if data_len <= n_out: return data # 划分桶 bucket_size = data_len / n_out buckets = [] for i in range(n_out): start = int(i * bucket_size) end = int((i+1)*bucket_size) if i != n_out-1 else data_len buckets.append(data[start:end]) # 初始化结果(首尾点固定) result = [buckets[0][0], buckets[-1][-1]] # 处理中间桶 for i in range(1, n_out-1): prev_point = result[-1] next_bucket = buckets[i+1] # 简化取后一个桶的中点 next_mid = (next_bucket[0] + next_bucket[-1]) / 2 max_area = -1 best_point = None for point in buckets[i]: # 计算三角形面积(省略1/2系数不影响比较) area = abs((prev_point - point) * (next_mid - prev_point) - (prev_point - prev_point) * (point - prev_point)) if area > max_area: max_area = area best_point = point result.insert(-1, best_point) return result
优势:
- 完美保留峰值、谷值和整体趋势,不会像均值法那样抹平波形细节。
- 计算效率较高,处理180万级数据也能快速完成。
2. 分块极值优先采样
如果不想实现复杂的LTTB,这个简化方案也能满足需求:核心是每个采样块优先保留极值,再用中位数补充(中位数比均值更能抵抗异常值,保留形态)。
操作步骤:
- 计算块大小:
block_size = (len(data) + 800 - 1) // 800(向上取整),若原数据长度<800则block_size=1。 - 遍历每个块:
- 若块内数据量 ≤3:直接取所有点(总点数不会超过800)。
- 若块内数据量 >3:检查块内是否存在接近边界的极值(比如值≥190或≤10,对应你的0-200取值范围),如果有则取该极值点;如果没有则取块内的中位数。
伪代码示例:
def extreme_priority_downsample(data, n_out=800): data_len = len(data) if data_len <= n_out: return data block_size = (data_len + n_out - 1) // n_out result = [] for i in range(n_out): start = i * block_size end = min((i+1)*block_size, data_len) block = data[start:end] # 筛选接近极值的点 extremes = [] for x in block: if x >= 190 or x <= 10: extremes.append(x) if extremes: # 取最接近边界的极值 result.append(max(extremes) if max(extremes) >= 190 else min(extremes)) else: # 取中位数 sorted_block = sorted(block) mid_idx = len(sorted_block) // 2 result.append(sorted_block[mid_idx]) return result
优势:
- 实现简单,针对你的0-200取值范围做了针对性优化,确保峰值不丢失。
- 计算速度极快,适合超大规模数据。
3. 短数据补全(可选)
如果原数据长度远小于800(比如只有50个点),可以用线性插值补全到800个点,让波形在画布上更饱满,同时避免过度插值失真:
import numpy as np def interpolate_short_data(data, n_out=800): if len(data) >= n_out: return data x_old = np.linspace(0, 1, len(data)) x_new = np.linspace(0, 1, n_out) return np.interp(x_new, x_old, data).tolist()
内容的提问来源于stack exchange,提问作者rafael
相关产品推荐
相关产品推荐

