如何为numpy数组生成的5条折线分别添加趋势线并解决linregress报错
问题原因
scipy.stats.linregress 不支持仅传入单组因变量计算趋势,必须同时传入**自变量(时间序列)和因变量(对应深度的湿度序列)**两个参数。你只传了单维的湿度Series,函数尝试读取其第二维度时就会抛出索引越界错误。
另外要注意:你之前对df_plot做了异常值替换为NaN的处理,计算回归的时候要提前过滤掉NaN值,否则也会出现计算错误。
完整解决方案
1. 预处理滑动平均数据
和你原有预处理逻辑对齐,用除以孔隙度、过滤异常值后的df_plot计算,保证趋势线和已画折线的数值匹配:
import numpy as np from scipy.stats import linregress import plotly.graph_objects as go # 将时间转为数值类型用于线性回归计算,这里用序列位置做自变量,也可以根据需求转成时间戳/年份数值 t_numeric = np.arange(len(t)) # 计算滑动平均,加min_periods避免前半段全为NaN window = 17520 df_ma = df_plot.rolling(window=window, min_periods=window//2).mean()
2. 批量计算所有深度的趋势线
trend_lines = {} depth_cols = ['Moisture litter layer','Moisture 3 cm','Moisture 20 cm','Moisture 50 cm','Moisture 100 cm'] for col in depth_cols: # 过滤当前列的NaN值,匹配对应时间 valid_mask = ~df_ma[col].isna() y_valid = df_ma[col][valid_mask] x_valid = t_numeric[valid_mask] # 计算线性回归参数 slope, intercept, r_val, p_val, std_err = linregress(x_valid, y_valid) # 生成完整趋势线序列 trend_vals = intercept + slope * t_numeric trend_lines[col] = trend_vals
3. 将趋势线添加到已有图表
你之前生成的f4是plotly Figure对象,直接添加trace即可:
# 用虚线区分趋势线和原始折线,可自定义样式 line_styles = [dict(dash='dash', width=1.5) for _ in range(len(depth_cols))] for idx, col in enumerate(depth_cols): f4.add_trace( go.Scatter( x = t, y = trend_lines[col], name = f'{col} 趋势线', mode = 'lines', line = line_styles[idx], legendgroup = col # 和原折线归为同一图例组,点击可同时隐藏 ) ) f4.show()
注意事项
- 如果不需要滑动平均后的趋势,直接把第二步的
df_ma换成df_plot,即可得到原始数据的线性趋势 - 窗口大小17520对应小时频率的2年数据,若觉得前半段空值过多,可自行调小窗口或调整
min_periods参数 - 若需要斜率有实际物理意义(如每年湿度变化量),可将
t_numeric替换为(pd.to_datetime(t) - pd.to_datetime(t[0])).days / 365.25,计算出的slope单位为m³/m³ 每年
内容的提问来源于stack exchange,提问作者Ellie98
相关产品推荐
相关产品推荐

