用机器学习填充时间序列缺失数据,对比线性回归和决策树方法,提供实践建议。
原文标题:使用机器学习技术进行时间序列缺失数据填充:基础方法与入门案例
原文作者:数据派THU
冷月清谈:
线性回归方法实现简单,计算效率高,但对于非线性模式的捕捉能力有限。通过对补充后的数据进行统计特征分析、自相关性分析和时间序列分解分析,发现线性回归方法在保持数据基本特征和趋势方面表现良好,但在处理数据的变异性和极值方面存在局限性。
为了解决这个问题,文章进一步介绍了决策树回归方法。决策树的非线性特性使其能够更好地捕捉数据中的复杂模式。通过与线性回归方法进行对比,发现决策树方法在保持数据的局部变化特征、极值和时间依赖性方面表现更出色。
最后,文章总结了两种方法的优劣势和适用场景,并提出了实践建议。对于简单的时间序列,可以优先考虑线性回归方法;而在处理复杂模式的数据时,建议使用决策树方法。
怜星夜思:
2、文章中使用模拟数据进行实验,实际应用中,数据往往更加复杂,例如存在噪声、异常值等,这些因素会如何影响模型的性能,应该如何处理?
3、文章中主要评估了模型的预测精度,除了预测精度,还有哪些指标可以用来评估时间序列缺失值填充的效果?
原文内容
来源:Deephub Imba本文约5000字,建议阅读5分钟
本文将通过实际案例,详细探讨如何运用机器学习技术来解决时间序列的缺失值问题。
-
存在复杂的非线性模式
-
包含多层次的趋势变化
-
数据波动性较大
数据说明
-
时间范围:2023年1月1日至2023年3月1日
-
采样频率:10分钟
-
数据特点:包含真实的昼夜能源生产周期
-
缺失设置:随机选择10%的数据点作为缺失值
import pandas as pd import numpy as np from datetime import datetime import matplotlib.pyplot as plt
生成模拟能源生产数据
start_date = datetime(2023, 1, 1)
end_date = datetime(2023, 3, 1)
datetime_index = pd.date_range(start=start_date, end=end_date, freq=‘10T’)创建具有昼夜周期的能源生产值
np.random.seed(42) # 设置随机种子以确保可重复性
base_energy =
for dt in datetime_index:
hour = dt.hour
if 6 <= hour <= 18: # 白天时段:较高的能源生产
energy = np.random.normal(loc=300, scale=30)
else: # 夜间时段:较低的能源生产
energy = np.random.normal(loc=50, scale=15)
base_energy.append(energy)energy_production = pd.Series(base_energy)
随机引入缺失值
num_missing = int(0.1 * len(energy_production))
missing_indices = np.random.choice(len(energy_production), num_missing, replace=False)
energy_production.iloc[missing_indices] = np.nan创建数据框架
mock_energy_data_with_missing = pd.DataFrame({
‘Datetime’: datetime_index,
‘Energy_Production’: energy_production
})添加时间索引便于后续分析
data_with_index = mock_energy_data_with_missing.reset_index()
data_with_index[‘Time_Index’] = np.arange(len(data_with_index))数据可视化
plt.figure(figsize=(14, 7))
plt.plot(mock_energy_data_with_missing[‘Datetime’],
mock_energy_data_with_missing[‘Energy_Production’],
label=‘Energy Production (With Missing)’, color=‘blue’, alpha=0.7)
plt.scatter(mock_energy_data_with_missing[‘Datetime’],
mock_energy_data_with_missing[‘Energy_Production’],
c=mock_energy_data_with_missing[‘Energy_Production’].isna(),
cmap=‘coolwarm’,
label=‘Missing Values’, s=10)
plt.title(‘模拟能源生产数据集(10分钟间隔采样)’)
plt.xlabel(‘时间’)
plt.ylabel(‘能源生产量’)
plt.legend([‘能源生产(含缺失值)’, ‘缺失值’])
plt.grid(True)
plt.show()
-
数据展现出明显的周期性波动,这反映了能源生产的昼夜变化规律
-
缺失值(散点标记)随机分布在整个时间序列中
-
能源生产量在白天和夜间有显著的水平差异
机器学习在时间序列补充中的应用基础
机器学习方法的优势
线性回归补充方法实现
import pandas as pd import numpy as np from datetime import datetime import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression
第一步:数据预处理
将时间索引作为特征,能源生产量作为目标变量
features = data_with_index[[‘Time_Index’]]
target = data_with_index[‘Energy_Production’]第二步:分离完整数据和缺失数据
non_missing_data = data_with_index.dropna(subset=[‘Energy_Production’])
missing_data = data_with_index[data_with_index[‘Energy_Production’].isna()]第三步:模型训练
使用完整数据训练线性回归模型
regressor = LinearRegression()
regressor.fit(non_missing_data[[‘Time_Index’]], non_missing_data[‘Energy_Production’])第四步:缺失值预测
predicted_values = regressor.predict(missing_data[[‘Time_Index’]])
第五步:将预测值填充到原始数据集
filled_data = data_with_index.copy()
filled_data.loc[filled_data[‘Energy_Production’].isna(), ‘Energy_Production’] = predicted_values
filled_data = filled_data[[‘Datetime’, ‘Energy_Production’]]第六步:结果可视化(展示2023年1月数据)
start_month = datetime(2023, 1, 1)
end_month = datetime(2023, 1, 31)
original_month_data = mock_energy_data_with_missing[
(mock_energy_data_with_missing[‘Datetime’] >= start_month) &
(mock_energy_data_with_missing[‘Datetime’] <= end_month)
]
imputed_month_data = filled_data[
(filled_data[‘Datetime’] >= start_month) &
(filled_data[‘Datetime’] <= end_month)
]
plt.figure(figsize=(14, 7))
plt.plot(imputed_month_data[‘Datetime’], imputed_month_data[‘Energy_Production’],
label=‘补充后数据’, color=‘green’, alpha=0.8)
plt.plot(original_month_data[‘Datetime’], original_month_data[‘Energy_Production’],
label=‘原始数据(含缺失)’, color=‘red’, alpha=0.9)
plt.title(‘原始数据与线性回归补充数据对比(2023年1月)’)
plt.xlabel(‘时间’)
plt.ylabel(‘能源生产量’)
plt.legend()
plt.grid(True)
plt.show()
补充效果的多维度评估
from statsmodels.tsa.seasonal import seasonal_decompose
统计特征分析
original_stats = mock_energy_data_with_missing[‘Energy_Production’].describe()
imputed_stats = filled_data[‘Energy_Production’].describe()创建比较表
stats_comparison = pd.DataFrame({
‘Metric’: original_stats.index,
‘Original Data’: original_stats.values,
‘Imputed Data (Linear Regression)’: imputed_stats.values
})输出统计比较结果
print(“数据统计特征对比:”)
print(stats_comparison)
-
数据完整性:补充后的数据集从7648个观测值增加到8497个,实现了完整覆盖。
-
中心趋势:补充后数据的均值(185.07)与原始数据基本一致,表明保持了数据的整体水平。
-
离散程度:补充数据的标准差(120.31)略低于原始数据(126.82),表明发生了一定程度的平滑。
-
分布特征:虽然最大值和最小值保持不变,但中位数的变化反映出分布形态有所改变。
时间序列补充效果的深入评估
自相关性分析
import statsmodels.api as sm
def plot_acf_comparison(original_series, imputed_series, lags=50):
“”"
绘制并比较原始数据和补充数据的自相关函数
参数:
original_series: 原始时间序列
imputed_series: 补充后的时间序列
lags: 滞后阶数
“”"
plt.figure(figsize=(14, 5))分析原始数据的自相关性
plt.subplot(1, 2, 1)
sm.graphics.tsa.plot_acf(original_series.dropna(), lags=lags,
ax=plt.gca(), title=“原始数据的自相关函数”)
plt.grid(True)分析补充数据的自相关性
plt.subplot(1, 2, 2)
sm.graphics.tsa.plot_acf(imputed_series, lags=lags,
ax=plt.gca(), title=“补充数据的自相关函数”)
plt.grid(True)plt.tight_layout()
plt.show()执行自相关分析
plot_acf_comparison(mock_energy_data_with_missing[‘Energy_Production’],
filled_data[‘Energy_Production’])
-
短期相关性:补充数据在短期滞后期(1-5个lag)的自相关系数与原始数据非常接近,表明短期时间依赖关系得到了良好保持。
-
周期性特征:两个图中都清晰显示出规律的波动模式,这反映了数据中的日周期特性被很好地保留。
-
相关强度:补充数据的自相关系数整体略低于原始数据,这是线性回归补充过程中不可避免的平滑效应导致的。
时间序列分解分析
# 执行STL分解 original_series = mock_energy_data_with_missing['Energy_Production'] imputed_series = filled_data['Energy_Production']
考虑每日144个观测值的周期(10分钟采样间隔)
original_decompose = seasonal_decompose(original_series.interpolate(),
model=‘additive’, period=144)
imputed_decompose = seasonal_decompose(imputed_series.interpolate(),
model=‘additive’, period=144)绘制趋势比较
plt.figure(figsize=(14, 5))
plt.plot(original_decompose.trend, label=‘原始趋势’, color=‘blue’)
plt.plot(imputed_decompose.trend, label=‘补充数据趋势’,
color=‘green’, linestyle=‘–’)
plt.title(‘趋势组件比较:原始数据 vs 线性回归补充’)
plt.legend()
plt.grid(True)
plt.show()绘制季节性比较
plt.figure(figsize=(14, 5))
plt.plot(original_decompose.seasonal, label=‘原始季节性’, color=‘blue’)
plt.plot(imputed_decompose.seasonal, label=‘补充数据季节性’,
color=‘green’, linestyle=‘–’)
plt.xlim(0, 4000)
plt.title(‘季节性组件比较:原始数据 vs 线性回归补充’)
plt.legend()
plt.grid(True)
plt.show()
-
补充数据很好地保持了原始数据的长期趋势方向
-
趋势线的平滑程度增加,这是线性回归方法的特性导致的
-
关键的趋势转折点得到了准确保持
-
日内周期的基本模式被准确捕获
-
补充数据的季节性振幅略有减小,表明极值被部分平滑
-
周期性的时间点(如日出、日落时段)的变化模式得到保持
决策树回归方法的应用与评估
决策树回归器的实现
from sklearn.tree import DecisionTreeRegressor
配置并训练决策树模型
max_depth=5用于防止过拟合,同时保持足够的模型复杂度
tree_regressor = DecisionTreeRegressor(max_depth=5, random_state=42)
tree_regressor.fit(non_missing_data[[‘Time_Index’]],
non_missing_data[‘Energy_Production’])使用训练好的模型预测缺失值
tree_predicted_values = tree_regressor.predict(missing_data[[‘Time_Index’]])
将预测值填充到原始数据集
tree_filled_data = data_with_index.copy()
tree_filled_data.loc[tree_filled_data[‘Energy_Production’].isna(),
‘Energy_Production’] = tree_predicted_values
tree_filled_data = tree_filled_data[[‘Datetime’, ‘Energy_Production’]]可视化决策树补充结果
plt.figure(figsize=(14, 7))
plt.plot(tree_imputed_month_data[‘Datetime’],
tree_imputed_month_data[‘Energy_Production’],
label=‘决策树补充数据’, color=‘orange’, alpha=0.8)
plt.plot(original_month_data[‘Datetime’],
original_month_data[‘Energy_Production’],
label=‘原始数据(含缺失)’, color=‘red’, alpha=0.9)
plt.title(‘原始数据与决策树补充数据对比(2023年1月)’)
plt.xlabel(‘时间’)
plt.ylabel(‘能源生产量’)
plt.legend()
plt.grid(True)
plt.show()
-
更好地保持了数据的局部变化特征
-
对极值的处理更为准确
-
补充结果展现出更自然的波动性
补充效果对比分析
# 统计指标比较
stats_comparison['决策树补充数据'] = tree_filled_data['Energy_Production'].describe()
核心统计指标对比:
Metric Original Data Linear Regression Decision Tree
count 7648.000000 8497.000000 8497.000000
mean 185.073509 185.073842 184.979184
std 126.816229 120.313162 120.633636
min -7.549833 -7.549833 -7.549833
25% 51.793304 54.186258 53.797479
50% 256.996772 185.197681 185.545605
75% 302.217789 298.324435 298.531049
max 415.581945 415.581945 415.581945
-
决策树补充的数据在标准差方面(120.63)比线性回归(120.31)更接近原始数据(126.82)
-
两种方法都很好地保持了数据的整体范围(最小值和最大值)
-
决策树在四分位数的保持上表现更好,特别是在中位数方面
-
更准确地保持了短期相关性强度
-
更好地捕捉了周期性模式
-
自相关结构的衰减特征更接近原始数据
-
趋势和季节性分解:
-
-
决策树方法在保持趋势的细节特征方面表现更好
-
季节性模式的振幅和相位都得到了更准确的保持
-
整体而言,决策树补充的数据展现出更自然的时间序列特性
方法优劣势总结与应用建议
-
优势:计算效率高,实现简单
-
劣势:对非线性模式的捕捉能力有限
-
适用场景:数据呈现明显的线性趋势,且波动较为规律
-
优势:能更好地处理非线性关系,保持数据的局部特征
-
劣势:计算复杂度较高,需要更多的参数调优
-
适用场景:数据具有复杂的非线性模式,需要保持精细的局部特征
-
对于简单的时间序列,可以优先考虑线性回归方法
-
在处理复杂模式的数据时,建议使用决策树方法
-
可以根据具体应用场景的需求(如计算资源限制、精度要求等)来选择合适的方法