You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于最优点组自动计算含噪数组的平滑导数?

含噪数据的平滑导数计算:自动识别趋势点组

我有如下一组数据点:

import numpy as np
import matplotlib.pyplot as plt

x = np.array([1,1,1,1,2,3,4,4,4,4,4.5,5,5.5,6,6.5,7,7.5,8,8.1,8.2,8.3,8.4,8.5,8.5,8.5,8.5])
y = np.linspace(10,20,len(x))

plt.plot(x*0.1,y,'o', label='x')
plt.xlabel('x * 0.1')
plt.ylabel('y')

图1是该数据的可视化结果。我希望计算导数时,能自动找到具有共同增长趋势的最优点组,最终得到类似图2的结果——对无噪数据用numpy.diff做点到点求导可以实现这个效果,但在含随机噪声的真实数据中,numpy.diff会得到像图3那样的糟糕结果。请问能否针对含噪数据得到图2那样的平滑导数结果?也就是不用点到点求导,而是通过算法自动识别最优的点组来计算平滑导数。


解决方案

1. 分段线性拟合 + 断点检测

先检测数据中的趋势变化断点,再对每个分段做线性拟合,用拟合直线的斜率作为该段的导数,能精准匹配分段趋势:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error

# 模拟含噪数据
x_noisy = x + np.random.normal(0, 0.2, size=len(x))
y = np.linspace(10,20,len(x))

# 滑动窗口计算残差,识别趋势断点
window_size = 3
residuals = []
for i in range(len(x_noisy)-window_size):
    seg_x = x_noisy[i:i+window_size]
    seg_y = y[i:i+window_size]
    coeffs = np.polyfit(seg_y, seg_x, 1)
    pred_x = np.polyval(coeffs, seg_y)
    residuals.append(mean_squared_error(seg_x, pred_x))

# 基于残差阈值筛选断点
threshold = np.mean(residuals) + 2*np.std(residuals)
breakpoints = [i+window_size//2 for i, res in enumerate(residuals) if res > threshold]
breakpoints = [0] + breakpoints + [len(x_noisy)-1]

# 分段拟合并计算导数
derivatives = []
for i in range(len(breakpoints)-1):
    start, end = breakpoints[i], breakpoints[i+1]+1
    seg_y, seg_x = y[start:end], x_noisy[start:end]
    coeffs = np.polyfit(seg_y, seg_x, 1)
    slope = coeffs[0] * 0.1  # 对应x*0.1的导数
    derivatives.extend([slope]*(end-start))

# 可视化
plt.figure(figsize=(12,6))
plt.subplot(121)
plt.plot(x_noisy*0.1, y, 'o', label='含噪数据')
plt.xlabel('x * 0.1')
plt.ylabel('y')
plt.legend()

plt.subplot(122)
plt.plot(y, derivatives, '-', label='分段平滑导数')
plt.xlabel('y')
plt.ylabel('导数 (dx/dy * 0.1)')
plt.legend()
plt.tight_layout()
plt.show()

2. 滑动窗口加权平均导数

用滑动窗口对局部范围内的点计算导数并加权平均,快速抑制噪声,适合趋势变化平缓的数据:

def smooth_derivative(x, y, window_size=5):
    derivatives = np.zeros_like(x)
    half_win = window_size//2
    for i in range(len(x)):
        start = max(0, i-half_win)
        end = min(len(x), i+half_win+1)
        seg_x, seg_y = x[start:end], y[start:end]
        coeffs = np.polyfit(seg_y, seg_x, 1)
        derivatives[i] = coeffs[0] * 0.1
    return derivatives

# 应用到含噪数据
deriv_smooth = smooth_derivative(x_noisy, y, window_size=5)

# 可视化
plt.plot(y, deriv_smooth, '-', label='滑动窗口平滑导数')
plt.xlabel('y')
plt.ylabel('导数')
plt.legend()
plt.show()

3. 贝叶斯分段线性模型

通过贝叶斯方法自动推断分段数和断点位置,适合趋势复杂、需要精准自动化识别的场景:

import pymc3 as pm
import arviz as az

with pm.Model() as model:
    # 先验定义:断点数量、位置、分段斜率与截距
    k = pm.DiscreteUniform('k', lower=1, upper=5)
    changepoints = pm.Uniform('changepoints', lower=y.min(), upper=y.max(), shape=k)
    slopes = pm.Normal('slopes', mu=0, sd=1, shape=k+1)
    intercepts = pm.Normal('intercepts', mu=0, sd=10, shape=k+1)
    
    # 定义分段线性函数
    def linear_model(y_val):
        idx = pm.math.sum(changepoints < y_val)
        return slopes[idx] * y_val + intercepts[idx]
    
    # 似然函数
    pm.Normal('x_obs', mu=linear_model(y), sd=0.2, observed=x_noisy)
    
    # MCMC采样
    trace = pm.sample(2000, tune=1000, cores=2)

# 提取后验均值计算导数
post_slopes = az.summary(trace)['mean'].filter(regex='slopes')
changepoints_mean = np.sort(trace['changepoints'].mean(axis=0))
deriv_bayes = np.zeros_like(y)

for i in range(len(changepoints_mean)+1):
    if i == 0:
        mask = y <= changepoints_mean[i]
    elif i == len(changepoints_mean):
        mask = y > changepoints_mean[-1]
    else:
        mask = (y > changepoints_mean[i-1]) & (y <= changepoints_mean[i])
    deriv_bayes[mask] = post_slopes[f'slopes[{i}]'] * 0.1

# 可视化
plt.plot(y, deriv_bayes, '-', label='贝叶斯分段导数')
plt.xlabel('y')
plt.ylabel('导数')
plt.legend()
plt.show()

内容的提问来源于stack exchange,提问作者Lucas Oliveira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 03:00:03