机器学习时间序列缺失数据填充:从线性回归到决策树

用机器学习填充时间序列缺失数据,对比线性回归和决策树方法,提供实践建议。

原文标题:使用机器学习技术进行时间序列缺失数据填充:基础方法与入门案例

原文作者:数据派THU

冷月清谈:

本文介绍了如何使用机器学习方法填充时间序列数据中的缺失值,并通过模拟能源生产数据集进行了案例分析。文章首先解释了时间序列数据缺失的挑战以及传统方法的局限性,然后详细介绍了线性回归和决策树两种机器学习方法的实现步骤和评估指标。

线性回归方法实现简单,计算效率高,但对于非线性模式的捕捉能力有限。通过对补充后的数据进行统计特征分析、自相关性分析和时间序列分解分析,发现线性回归方法在保持数据基本特征和趋势方面表现良好,但在处理数据的变异性和极值方面存在局限性。

为了解决这个问题,文章进一步介绍了决策树回归方法。决策树的非线性特性使其能够更好地捕捉数据中的复杂模式。通过与线性回归方法进行对比,发现决策树方法在保持数据的局部变化特征、极值和时间依赖性方面表现更出色。

最后,文章总结了两种方法的优劣势和适用场景,并提出了实践建议。对于简单的时间序列,可以优先考虑线性回归方法;而在处理复杂模式的数据时,建议使用决策树方法。

怜星夜思:

1、文章中提到的线性回归和决策树方法都是比较基础的模型,在实际应用中,有没有其他更高级的机器学习模型可以用于时间序列缺失值填充?
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()


图1:模拟能源生产数据可视化。蓝线表示能源生产数据,散点表示缺失值的位置

从上图中,我们可以清晰地观察到以下特征:

  1. 数据展现出明显的周期性波动,这反映了能源生产的昼夜变化规律
  2. 缺失值(散点标记)随机分布在整个时间序列中
  3. 能源生产量在白天和夜间有显著的水平差异

这个数据集为我们研究不同补充方法的效果提供了理想的测试基础。在接下来的分析中,我们将详细探讨如何运用机器学习方法来补充这些缺失值。

机器学习在时间序列补充中的应用基础


机器学习方法的优势


在时间序列数据分析中,机器学习方法相比传统补充方法具有独特优势。传统方法通常基于简单的统计假设,而机器学习方法则能够自适应地学习数据中的复杂模式和多维依赖关系。

非线性关系处理:在能源生产等实际场景中,变量之间往往存在复杂的非线性关系。机器学习模型能够自动捕捉这些非线性模式,而无需预先指定关系形式。

多维特征利用:当数据集包含多个相关变量时,机器学习模型可以同时考虑多个特征的影响,从而提供更准确的估计。

大规模缺失处理:对于连续时间段的缺失,机器学习可以通过学习数据的长期模式来提供更可靠的补充值。

异常模式识别:在处理非随机缺失时,机器学习方法表现出较强的鲁棒性,能够识别并适应异常模式。

线性回归补充方法实现


我们首先探讨线性回归这一基础但高效的补充方法。以下是详细的实现步骤:

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()


图2:线性回归补充效果可视化。绿线表示补充后的数据,红线表示原始数据

补充效果的多维度评估


为了全面评估补充效果,我们需要从多个维度进行分析。以下是详细的评估框架:

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)


这个统计分析揭示了以下关键发现:

  1. 数据完整性:补充后的数据集从7648个观测值增加到8497个,实现了完整覆盖。
  2. 中心趋势:补充后数据的均值(185.07)与原始数据基本一致,表明保持了数据的整体水平。
  3. 离散程度:补充数据的标准差(120.31)略低于原始数据(126.82),表明发生了一定程度的平滑。
  4. 分布特征:虽然最大值和最小值保持不变,但中位数的变化反映出分布形态有所改变。

通过这些初步分析,我们可以看到线性回归方法在保持数据基本特征方面表现良好,但也存在一定的局限性,特别是在处理数据的变异性方面。在下一部分中,我们将进一步探讨更多高级评估指标,以及决策树回归等其他补充方法的表现。

时间序列补充效果的深入评估


在时间序列分析中,仅依靠基本的统计指标是不够的。我们需要特别关注数据的时序特性,包括自相关性、趋势和季节性模式。让我们逐步深入这些关键评估维度。

自相关性分析


自相关性反映了时间序列中相邻观测值之间的依赖关系。保持适当的自相关结构对于确保补充数据的时序特性至关重要。以下是详细的分析过程:

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’])


图3:自相关函数对比分析。左图显示原始数据的自相关性,右图显示补充后数据的自相关性

从自相关分析中,我们可以观察到几个重要特征:

  1. 短期相关性:补充数据在短期滞后期(1-5个lag)的自相关系数与原始数据非常接近,表明短期时间依赖关系得到了良好保持。
  2. 周期性特征:两个图中都清晰显示出规律的波动模式,这反映了数据中的日周期特性被很好地保留。
  3. 相关强度:补充数据的自相关系数整体略低于原始数据,这是线性回归补充过程中不可避免的平滑效应导致的。

时间序列分解分析


为了更深入地理解补充效果,我们使用STL(Seasonal-Trend decomposition using Loess)方法将时间序列分解为趋势、季节性和残差组件:

# 执行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()

图4:趋势组件比较。蓝线表示原始数据趋势,绿虚线表示补充数据趋势。
图5:季节性组件比较。展示了原始数据和补充数据的周期性模式。

通过分解分析我们发现:

趋势组件特征:

  • 补充数据很好地保持了原始数据的长期趋势方向
  • 趋势线的平滑程度增加,这是线性回归方法的特性导致的
  • 关键的趋势转折点得到了准确保持

季节性组件特征:

  • 日内周期的基本模式被准确捕获
  • 补充数据的季节性振幅略有减小,表明极值被部分平滑
  • 周期性的时间点(如日出、日落时段)的变化模式得到保持

这些分析结果提示我们,虽然线性回归方法在保持数据的基本时序特性方面表现不错,但在处理极值和突变点方面可能存在局限,所以我们选用一些更好的模型如决策树回归器,来改善这些方面的表现。

决策树回归方法的应用与评估


在观察到线性回归方法的局限性后,我们引入决策树回归器作为一种更灵活的补充方法。决策树的非线性特性使其能够更好地捕捉数据中的复杂模式。

决策树回归器的实现


让我们首先看看如何使用决策树进行缺失值补充:

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()


图6:决策树补充结果可视化。橙线表示决策树补充的数据,红线表示原始数据。

从图中可以直观地看到,决策树方法在保持数据特征方面展现出了以下优势:

  1. 更好地保持了数据的局部变化特征
  2. 对极值的处理更为准确
  3. 补充结果展现出更自然的波动性

补充效果对比分析


让我们通过各项指标来系统比较两种方法的表现:


# 统计指标比较
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)
  • 两种方法都很好地保持了数据的整体范围(最小值和最大值)
  • 决策树在四分位数的保持上表现更好,特别是在中位数方面

自相关性分析:

图7:两种方法的自相关分析对比。

决策树方法在时间依赖性的保持方面表现出明显优势:

  • 更准确地保持了短期相关性强度
  • 更好地捕捉了周期性模式
  • 自相关结构的衰减特征更接近原始数据

  1. 趋势和季节性分解:

图8:趋势组件比较

图9:季节性组件比较

从分解结果可以看出:

  • 决策树方法在保持趋势的细节特征方面表现更好
  • 季节性模式的振幅和相位都得到了更准确的保持
  • 整体而言,决策树补充的数据展现出更自然的时间序列特性


方法优劣势总结与应用建议


基于以上分析,我们可以得出以下结论:

线性回归方法的特点:

  • 优势:计算效率高,实现简单
  • 劣势:对非线性模式的捕捉能力有限
  • 适用场景:数据呈现明显的线性趋势,且波动较为规律

决策树方法的特点:

  • 优势:能更好地处理非线性关系,保持数据的局部特征
  • 劣势:计算复杂度较高,需要更多的参数调优
  • 适用场景:数据具有复杂的非线性模式,需要保持精细的局部特征

实践建议:

  • 对于简单的时间序列,可以优先考虑线性回归方法
  • 在处理复杂模式的数据时,建议使用决策树方法
  • 可以根据具体应用场景的需求(如计算资源限制、精度要求等)来选择合适的方法

本文展示了机器学习方法在时间序列缺失值补充中的有效性,并提供了方法选择的实践指导。这些方法和评估框架可以推广到其他类似的时间序列分析场景中。

编辑:王菁



关于我们

数据派THU作为数据科学类公众号,背靠清华大学大数据研究中心,分享前沿数据科学与大数据技术创新研究动态、持续传播数据科学知识,努力建设数据人才聚集平台、打造中国大数据最强集团军。



新浪微博:@数据派THU

微信视频号:数据派THU

今日头条:数据派THU

还可以考虑使用一些基于模型的方法来检测和处理异常值,例如孤立森林、One-Class SVM等。这些方法可以识别出与正常数据模式不符的异常值,并将其去除或进行特殊处理。

引用一下问题:除了预测精度,还有哪些指标可以用来评估时间序列缺失值填充的效果? 可以考虑时间序列的特性,例如自相关性、互相关性等。填充后的数据应该保持原始数据的这些特性。

当然有,比如深度学习模型RNN、LSTM和GRU等,它们能够更好地捕捉时间序列的长程依赖关系,对于复杂的非线性模式也有更强的学习能力。此外,一些基于变分自编码器(VAE)的模型也能用于时间序列缺失值填充,它们可以学习数据的潜在分布,生成更合理的填充值。

除了深度学习模型,一些集成学习方法,例如梯度提升树(GBDT)和随机森林,也可以用于时间序列缺失值填充。它们可以结合多个弱学习器的预测结果,提高填充的准确性和鲁棒性。

处理噪声和异常值的方法有很多,选择哪种方法取决于具体的应用场景。例如,如果噪声的频率已知,可以使用带通滤波器去除噪声。如果异常值是由于测量误差引起的,可以考虑直接删除这些异常值。

噪声和异常值确实会影响模型的性能。对于噪声,可以考虑使用一些滤波方法进行预处理,例如卡尔曼滤波、小波变换等。对于异常值,可以尝试使用一些稳健的回归方法,例如Huber回归、RANSAC等,减少异常值的影响。

还有,高斯过程回归也是一种不错的选择,它可以对不确定性进行建模,提供更可靠的填充结果,尤其是在数据量较少的情况下。不过,高斯过程的计算复杂度相对较高,在大规模数据集上应用可能会受到限制。

除了自相关性和互相关性,还可以考虑一些频域特征,例如功率谱密度等。如果原始数据在频域上有一些特定的模式,填充后的数据也应该保持这些模式。

从更实际的角度出发,可以考虑填充后的数据对下游任务的影响。例如,如果填充后的数据用于预测,可以评估填充对预测精度的影响。如果填充后的数据用于分类,可以评估填充对分类精度的影响。