在Python中处理缺失值至关重要,因为缺失值可能导致数据分析结果出现偏差,影响模型的准确性和可靠性。如果不对缺失值进行处理,统计分析可能会产生误导性的结论,机器学习模型也可能因为数据的不完整性而无法有效学习数据中的模式。

e5746829a433d05cd9e1d404cb01e7f4.png

因此,选择合适的数据处理方法对于维持数据完整性并提升模型训练效率至关重要。

常见的数据缺失值处理方法包括:

1、删除含有缺失值的行或列:简单直接,但可能丢失重要信息。

2、填充缺失值:使用均值、中位数、众数等统计量填充。

3、插值法:利用已知数据点估计缺失值。

4、模型预测:使用模型预测缺失值。

5、多重插补:生成多个数据集,每个数据集都有不同的缺失值填充。

6、热卡填充:使用相似数据点的值填充。

7、最近邻法:找到最近的非缺失值点进行填充。

8、回归分析:利用其他变量预测缺失值。

9、聚类分析:将数据分为若干个簇,每个簇内部填充缺失值。

10、基于知识的填充:利用领域知识或业务规则填充。

从数据缺失值填充实现工具的角度,有基于pandas的,有基于numpy的,有基于sklearn的,有基于XGBoost的,有基于随机森林的。

本文无法全部实现,涵盖了一些常用工具和常用处理方法。

本文数据来自于Meteostat‌,它 是一个开源的 Python 库,旨在提供简单高效的接口来访问全球范围内的历史和实时气象数据。它支持全球成千上万的气象站,能够提供月度、每日和每小时的数据记录‌,当然在数据采集的过程中,也存在数据缺失的情况,甚至长达数月。我们就以此为例来进行数据缺失值算法探讨。

第一段代码,主要是基础库导入、定义了一个pandas缺失值比例函数、对pandas数据进行格式化展示

from meteostat import Daily, Hourly, Monthly, Stations
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer,KNNImputer
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error

# ---------------------------------------查看pandas缺失情况-----------------------------------------
def pandasnullinfo(df):
    missing = df.isna().sum()
    missing = pd.DataFrame(data={'feature_name': missing.index, 'null_value_num': missing.values})
    # 通过~取反,选取不包含数字0的行
    missing = missing[~missing['null_value_num'].isin([0])]
    # 缺失比例
    missing['null_value_ratio'] = missing['null_value_num'] / df.shape[0]
    return missing

# ---------------------------------------对pandas显示进行格式化-----------------------------------------
# 显示所有列
pd.set_option('display.max_columns', None)
# 显示所有行
pd.set_option('display.max_rows', None)
# 不换行显示
pd.set_option('display.width', 1000)
# 行列对齐显示,显示不混乱
pd.set_option('display.unicode.ambiguous_as_wide', True)
pd.set_option('display.unicode.east_asian_width', True)
# 显示精度
pd.set_option('display.precision', 4)
# 显示小数位数
pd.get_option("display.precision")

第二段代码,主要是获取某个气象站的数据,构造了等同的新列,按照一定的概率随机赋值为空,后续将新列数据和原始数据做对比。

# ---------------------------------------对获取气象的参数进行构造-----------------------------------------
startdate=datetime(2024,12,1)
enddate=datetime(2024,12,31)
citycode=59758

# ---------------------------------------通过meteostat获取气象信息-----------------------------------------
dailydata=Daily(citycode,startdate,enddate)
dailyinfo=dailydata.fetch()
# ---------------------------------------在pandas构造缺失值-----------------------------------------
dailyinfo['tavgtemp']=dailyinfo['tavg']
dailyinfo['tmintemp']=dailyinfo['tmin']
dailyinfo['tmaxtemp']=dailyinfo['tmax']
dailyinfo['prcptemp']=dailyinfo['prcp']
dailyinfo['wdirtemp']=dailyinfo['wdir']
dailyinfo['wspdtemp']=dailyinfo['wspd']
dailyinfo['prestemp']=dailyinfo['pres']
# 为'tavgtemp'等列随机赋空值,赋值的概率为20%
dailyinfo['tavgtemp'] = np.where(np.random.choice([True, False], size=len(dailyinfo), p=[0.8, 0.2]), dailyinfo['tavgtemp'], np.nan)
dailyinfo['tmintemp'] = np.where(np.random.choice([True, False], size=len(dailyinfo), p=[0.8, 0.2]), dailyinfo['tmintemp'], np.nan)
dailyinfo['tmaxtemp'] = np.where(np.random.choice([True, False], size=len(dailyinfo), p=[0.8, 0.2]), dailyinfo['tmaxtemp'], np.nan)
dailyinfo['prcptemp'] = np.where(np.random.choice([True, False], size=len(dailyinfo), p=[0.9, 0.1]), dailyinfo['prcptemp'], np.nan)
dailyinfo['wdirtemp'] = np.where(np.random.choice([True, False], size=len(dailyinfo), p=[0.9, 0.1]), dailyinfo['wdirtemp'], np.nan)
dailyinfo['wspdtemp'] = np.where(np.random.choice([True, False], size=len(dailyinfo), p=[0.85, 0.15]), dailyinfo['wspdtemp'], np.nan)
dailyinfo['prestemp'] = np.where(np.random.choice([True, False], size=len(dailyinfo), p=[0.85, 0.15]), dailyinfo['prestemp'], np.nan)
dailyinfo.drop(columns=['snow'], inplace=True)
dailyinfo.drop(columns=['wpgt'], inplace=True)
dailyinfo.drop(columns=['tsun'], inplace=True)
# tavg  平均气温,单位为°C Float64
# tmin  最低空气温度,单位为°C Float64
# tmax  最高空气温度,单位为°C Float64
# prcp  日降水总量,单位为mm Float64
# snow  日降雪量,以毫米为单位的雪深度 Float64
# wdir  以度(°)为单位的平均风向 Float64
# wspd  以km/h为单位的平均风速 Float64
# wsgt  以km/h为单位的每日最大风速 Float64
# pres  海平面平均气压,单位为hPa Float64
# tsun  每日日照时长,单位为h小时 Float64
# print(dailyinfo.describe())
#            tavg     tmin     tmax     prcp  snow     wdir     wspd  wpgt       pres  tsun  tavgtemp  tmintemp  tmaxtemp  prcptemp  wdirtemp  wspdtemp   prestemp
# count  31.000031.000031.000031.0000   0.031.000031.0000   0.0    31.0000   0.0   25.0000   21.0000   21.0000   26.0000   28.0000   29.0000    25.0000
# mean   19.551617.251622.3613   0.3194   NaN  61.451612.0226   NaN  1018.8613   NaN   19.3200   17.1810   22.5333    0.3808   61.9286   12.15521018.9320
# std     2.2717   2.4942   2.6361   1.0756   NaN  14.7983   2.1271   NaN     3.1995   NaN    2.3267    2.3377    2.6377    1.1679   15.5133    2.1340     3.3913
# min    15.800013.000017.9000   0.0000   NaN  26.0000   8.1000   NaN  1013.7000   NaN   15.8000   14.0000   17.9000    0.0000   26.0000    8.10001013.7000
# 25%    17.950014.700020.2000   0.0000   NaN  54.500010.6000   NaN  1015.6000   NaN   17.8000   14.8000   20.4000    0.0000   51.7500   10.70001015.0000
# 50%    19.000017.100021.8000   0.0000   NaN  65.000011.8000   NaN  1019.6000   NaN   18.3000   17.1000   22.4000    0.0000   66.0000   12.10001019.6000
# 75%    21.200019.550025.0500   0.0000   NaN  72.500013.7000   NaN  1020.5000   NaN   21.1000   18.6000   25.3000    0.0000   73.2500   14.00001020.9000
# max    23.800021.500026.6000   5.8000   NaN  87.000015.4000   NaN  1024.6000   NaN   23.8000   21.5000   26.6000    5.8000   87.0000   15.40001024.6000
# 统计每列NaN的数量
nancounts = pandasnullinfo(dailyinfo)
# print(nancounts)
#    feature_name  null_value_num  null_value_ratio
# 7      tavgtemp               9            0.2903
# 8      tmintemp               5            0.1613
# 9      tmaxtemp               5            0.1613
# 10     prcptemp               3            0.0968
# 11     wdirtemp               2            0.0645
# 12     wspdtemp               4            0.1290
# 13     prestemp               5            0.1613

第三段代码是基于pandas的fillna、ffill,bfill进行缺失值填充,fillna主要包括常量填充、平均值填充、中位数填充、众数填充,按照条件填充缺失值,这些相对比较简单,但误差率也高。

# ---------------------------------------通过pandas自带的函数处理缺失值-----------------------------------------
# 使用缺省值填充
dailyinfo_fillna = dailyinfo.fillna(0)
# 使用前一个值填充缺失值,当第一行是空值时,无法填充
dailyinfo_ffill = dailyinfo.ffill()
# 使用后一个值填充缺失值,当最后一行是空值时,无法填充
dailyinfo_bfill = dailyinfo.bfill()
# 使用列平均值填充缺失值
dailyinfo_mean = dailyinfo.fillna(dailyinfo.mean())
# 使用列的中位数填充缺失值
dailyinfo_median = dailyinfo.fillna(dailyinfo.median())
# 使用列的众数填充缺失值
dailyinfo_mode = dailyinfo.fillna(dailyinfo.mode().iloc[0])

# 根据条件填充缺失值
# dailyinfo['tavgtemp'] = dailyinfo['tavgtemp'].fillna(dailyinfo['tavg'])

第四段代码是基于pandas的interpolate即插值函数来填充,这里也提供了多种插值参数,需要注意的是插值函数无法保证所有缺失值都能填充,尤其是首行、尾行,因此需要对仍缺失的值按最简单的bfill,ffill进行填充。

# ---------------------------------------通过pandas自带的插值函数处理缺失值-----------------------------------------
# pandas.interpolate(method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', limit_area=None, downcast=None, **kwargs)
# 参数说明:
# method:指定插值方法,默认为线性插值,可选的方法有'linear'、'time'、'index'、'values'、'nearest'、'zero'、'slinear'、'quadratic'、'cubic'、'polynomial',具体使用哪种方法取决于数据的特点和需求。
#   线性插值法:method='linear',是最常用的插值方法,通过已知数据点之间的线性关系来估计未知的数据
#   基于时间的插值:method=‘time’,如果数据集中存在时间序列数据,可以尝试使用基于时间的插值方法。
#   二次插值:method=‘quadratic’,二次插值通过已知的数据点构建一个二次多项式来逼近未知的数值。
#   三次插值:method=‘cubic’,三次插值是一种常用的插值方法,通过已知的数据点构建一个三次多项式来逼近未知的数值。
#   三次样条插值:三次样条插值相比于简单的三次插值更加灵活和准确,因为它提供了更高的插值精度和更好的曲线平滑性。
#   拉格朗日插值:拉格朗日插值使用拉格朗日多项式逼近已知数据点,通过构造一个通过所有数据点的插值多项式来估计其他点的数值。
#   ‘index’, ‘values’: 使用索引的实际数值
#   ‘pad’:使用现有值填写NaN。
# axis:指定插值的轴,默认为0,表示按列进行插值操作。
# limit:指定连续缺失值的最大数量,超过该数量的缺失值将不会被插值,默认为None,表示不限制数量。
# inplace:指定是否在原始数据上进行插值操作,默认为False,表示生成新的插值后的数据副本。
# limit_direction:指定插值的方向,默认为'forward',表示向前插值,可选的值还有'backward'和'both'。
# limit_area:指定插值的区域,默认为None,表示不限制区域,可选的值有'inside'和'outside'。
# downcast:指定插值后数据的类型,默认为None,表示不进行类型转换。
# 。例如,如果你的数据是时间序列,并且你知道数据的自然增长趋势,那么使用time或index方法可能更合适;
#   如果你需要平滑的曲线拟合,可以考虑使用spline或polynomial方法。
#   对于简单的替换策略,如最近邻或零插值,可以直接使用nearest或zero方法。

# linear 线性插值,缺省linear,通过计算相邻点之间的线性回归来估计缺失值,其他首行和尾行需要借助ffbill和bfill来填充
dailyinfo_linear=dailyinfo.interpolate(method='linear')
dailyinfo_linear.ffill(inplace=True)
dailyinfo_linear.bfill(inplace=True)

# interpolate 基于时间的插值,这种方法假定数据的时间序列性质,并基于时间顺序进行插值。通常用于时间序列数据。
dailyinfo_time=dailyinfo.interpolate(method='time')
dailyinfo_time.ffill(inplace=True)
dailyinfo_time.bfill(inplace=True)
# values:基于周围的非缺失值进行插值。这是一种简单的替换方法,将缺失值替换为最近的非缺失值。
dailyinfo_values=dailyinfo.interpolate(method='values')
dailyinfo_values.ffill(inplace=True)
dailyinfo_values.bfill(inplace=True)
# nearest:最近邻插值。它将每个缺失值替换为最近的非缺失观测值
dailyinfo_nearest=dailyinfo.interpolate(method='nearest')
dailyinfo_nearest.ffill(inplace=True)
dailyinfo_nearest.bfill(inplace=True)
# slinear 线性插值,三次样条的线性插值
dailyinfo_slinear=dailyinfo.interpolate(method='slinear')
dailyinfo_slinear.ffill(inplace=True)
dailyinfo_slinear.bfill(inplace=True)
# quadratic 二次插值,三次样条的二次插值
dailyinfo_quadratic=dailyinfo.interpolate(method='quadratic')
dailyinfo_quadratic.ffill(inplace=True)
dailyinfo_quadratic.bfill(inplace=True)
# cubic 三次插值,三次样条的三次插值
dailyinfo_cubice=dailyinfo.interpolate(method='cubic')
dailyinfo_cubice.ffill(inplace=True)
dailyinfo_cubice.bfill(inplace=True)
# polynomial:多项式插值,可以通过指定order参数来指定多项式的阶数,例如order=2为二次多项式
dailyinfo_polynomial=dailyinfo.interpolate(method='polynomial', order=2)
dailyinfo_polynomial.ffill(inplace=True)
dailyinfo_polynomial.bfill(inplace=True)
# method='barycentric',适用于快速增长的时间序列
dailyinfo_barycentric=dailyinfo.interpolate(method='barycentric', order=2)
dailyinfo_barycentric.ffill(inplace=True)
dailyinfo_barycentric.bfill(inplace=True)

# method='pchip',适用于值接近累积分布函数
dailyinfo_pchip=dailyinfo.interpolate(method='pchip', order=2)
dailyinfo_pchip.ffill(inplace=True)
dailyinfo_pchip.bfill(inplace=True)
# spline:样条插值,可以通过order参数来控制样条的阶数。默认情况下,order=1对应于线性样条,order=2对应于二次样条,以此类推
dailyinfo_spline=dailyinfo.interpolate(method="spline", order=2)
dailyinfo_spline.ffill(inplace=True)
dailyinfo_spline.bfill(inplace=True)

第五段代码是基于sklearn的SimpleImputer和KNNImputer来进行填充,SimpleImputer其实和第三段中pandas的简单填充很类似。

# ---------------------------------------通过sklearn中带的SimpleImputer进行缺失值填充-----------------------------------------
dailyinfocopy= dailyinfo.copy()
dailyinfocopy_mean = SimpleImputer(missing_values=np.nan, strategy='mean').fit_transform(dailyinfocopy)
# 方法一,但时间序列就没有了
dailyinfocopy_mean_copy1 = pd.DataFrame(dailyinfocopy_mean, columns=dailyinfocopy.columns)
# print(dailyinfocopy_mean_copy)
#     tavg  tmin  tmax  prcp  wdir  wspd    pres  tavgtemp  tmintemp  tmaxtemp  prcptemp  wdirtemp  wspdtemp   prestemp
# 0   21.117.124.8   0.079.0   8.11013.9   19.7333   17.1000   24.8000    0.0000      79.0    8.10001013.9000
# 1   22.819.925.4   0.077.0   9.71013.7   22.8000   19.9000   25.4000    0.0000      77.0    9.70001018.9391
# 2   23.820.926.0   0.076.0   9.71014.4   23.8000   20.9000   26.0000    0.0000      76.0   12.18751018.9391
# 方法二,对控制列逐列填充,其他方法类似
dailyinfocopy_mean_copy2= dailyinfo.copy()
for nancolumnname in dailyinfocopy_mean_copy2.columns[dailyinfocopy_mean_copy2.isna().any()].tolist():
    dailyinfocopy_mean_copy2[nancolumnname] = SimpleImputer(missing_values=np.nan, strategy='mean').fit_transform(dailyinfocopy_mean_copy2[[nancolumnname]])

#             tavg  tmin  tmax  prcp  wdir  wspd    pres  tavgtemp  tmintemp  tmaxtemp  prcptemp  wdirtemp  wspdtemp   prestemp
# time
# 2024-12-0121.117.124.8   0.079.0   8.11013.9   19.7333   17.1000   24.8000    0.0000      79.0    8.10001013.9000
# 2024-12-0222.819.925.4   0.077.0   9.71013.7   22.8000   19.9000   25.4000    0.0000      77.0    9.70001018.9391
# 2024-12-0323.820.926.0   0.076.0   9.71014.4   23.8000   20.9000   26.0000    0.0000      76.0   12.18751018.9391
# 方法三,与方法二类似
dailyinfocopy_mean_copy3= dailyinfo.copy()
nancolumnnames=dailyinfocopy_mean_copy3.columns[dailyinfocopy_mean_copy3.isna().any()].tolist()
dailyinfocopy_mean_copy3[nancolumnnames] = SimpleImputer(missing_values=np.nan, strategy='mean').fit_transform(dailyinfocopy_mean_copy3[nancolumnnames])

#             tavg  tmin  tmax  prcp  wdir  wspd    pres  tavgtemp  tmintemp  tmaxtemp  prcptemp  wdirtemp  wspdtemp   prestemp
# time
# 2024-12-0121.117.124.8   0.079.0   8.11013.9   19.7333   17.1000   24.8000    0.0000      79.0    8.10001013.9000
# 2024-12-0222.819.925.4   0.077.0   9.71013.7   22.8000   19.9000   25.4000    0.0000      77.0    9.70001018.9391
# 2024-12-0323.820.926.0   0.076.0   9.71014.4   23.8000   20.9000   26.0000    0.0000      76.0   12.18751018.9391

# 中位数填补
dailyinfocopy_median= dailyinfo.copy()
# dailyinfocopy_median = SimpleImputer(missing_values=np.nan, strategy='median').fit_transform(dailyinfocopy)
nancolumnnames=dailyinfocopy_median.columns[dailyinfocopy_median.isna().any()].tolist()
dailyinfocopy_median[nancolumnnames] = SimpleImputer(missing_values=np.nan, strategy='median').fit_transform(dailyinfocopy_median[nancolumnnames])

# 频数填充
dailyinfocopy_most_frequent= dailyinfo.copy()
# dailyinfocopy_most_frequent = SimpleImputer(missing_values=np.nan, strategy='most_frequent').fit_transform(dailyinfocopy)
nancolumnnames=dailyinfocopy_most_frequent.columns[dailyinfocopy_most_frequent.isna().any()].tolist()
dailyinfocopy_most_frequent[nancolumnnames] = SimpleImputer(missing_values=np.nan, strategy='most_frequent').fit_transform(dailyinfocopy_most_frequent[nancolumnnames])

# 常量填充
dailyinfocopy_constant= dailyinfo.copy()
# dailyinfocopy_constant = SimpleImputer(missing_values=np.nan, strategy='constant',fill_value=100).fit_transform(dailyinfocopy)
nancolumnnames=dailyinfocopy_constant.columns[dailyinfocopy_constant.isna().any()].tolist()
dailyinfocopy_constant[nancolumnnames] = SimpleImputer(missing_values=np.nan, strategy='constant',fill_value=0).fit_transform(dailyinfocopy_constant[nancolumnnames])

# ---------------------------------------通过sklearn的KNN近邻法对缺省值进行处理-----------------------------------------
# 最近的3个邻居,使用的是3者的均值
dailyinfocopy_KNN= dailyinfo.copy()
# dailyinfocopy_KNN = KNNImputer(n_neighbors=3).fit_transform(dailyinfocopy)
nancolumnnames=dailyinfocopy_constant.columns[dailyinfocopy_KNN.isna().any()].tolist()
dailyinfocopy_KNN[nancolumnnames] = KNNImputer(n_neighbors=3).fit_transform(dailyinfocopy_KNN[nancolumnnames])
# SimpleImputer(missing_values=np.nan, strategy='median').fit_transform(dailyinfocopy_KNN[nancolumnnames])

第六段代码是基于随机森林来填充,一旦涉及时间列,所有互联网上看到的代码都是会报错的,在这里花了一些时间来处理这些问题。

# ---------------------------------------随机森林对缺省值进行处理-----------------------------------------
# 日期列在随机森林会报错,因此要重置索引后,删除该列
dailyinfocopy= dailyinfo.copy()
dailyinfocopy.reset_index(inplace=True)
del dailyinfocopy[dailyinfocopy.columns[0]]

# 创建一个副本,保存时间列
dailyinfocopywithtime= dailyinfo.copy()
dailyinfocopywithtime.reset_index(inplace=True)
timecolumn=dailyinfocopywithtime['time']

# 创建一个‘target’列,是未空列,作为预测列
dailyinfocopy['target']=1
target=dailyinfocopy['target']
# 获取其他列,不选取刚才加的target列
features = dailyinfocopy.iloc[:, 0:-1]
# 构造随机森林所需的数据集
X_full, y_full = features, target
n_samples = X_full.shape[0]  # 样本
n_features = X_full.shape[1]  # 特征
X_missing_reg = X_full.copy()

# 获取null列统计
X_df = X_missing_reg.isnull().sum()
# 得出列名 缺失值最少的列名 到 缺失值最多的列名
colname = X_df[~X_df.isin([0])].sort_values().index.values
# colname= ['wdirtemp''prcptemp''tmaxtemp''tavgtemp''wspdtemp''prestemp''tmintemp']
# 缺失值从小到大的特征顺序
sortindex = []
for i in colname:
    sortindex.append(X_missing_reg.columns.tolist().index(str(i)))
# sortindex= [11, 10, 9, 7, 12, 13, 8]

# 遍历所有的特征,从缺失最少的开始进行填补,每完成一次回归预测,就将预测值放到原本的特征矩阵中,再继续填补下一个特征
for i in sortindex:
    # 构建我们的新特征矩阵和新标签
    df = X_missing_reg  # 充当中间数据集
    fillc = df.iloc[:, i]  # 缺失值最少的特征列
    # 除了第 i 特征列,剩下的特征列+原有的完整标签 = 新的特征矩阵
    df = pd.concat([df.drop(df.columns[i], axis=1), pd.DataFrame(y_full)], axis=1)
    # 在新特征矩阵中,对含有缺失值的列,进行0的填补 ,没循环一次,用0填充的列越来越少
    df_0 = SimpleImputer(missing_values=np.nan, strategy='constant', fill_value=0).fit_transform(df)
    # 找出训练集和测试集
    # 标签
    Ytrain = fillc[fillc.notnull()]  # 没有缺失的部分,就是 Y_train
    Ytest = fillc[fillc.isnull()]  # 不是需要Ytest的值,而是Ytest的索引
    # 特征矩阵
    Xtrain = df_0[Ytrain.index, :]
    Xtest = df_0[Ytest.index, :]  # 有缺失值的特征情况
    rfc = RandomForestRegressor(n_estimators=100)  # 实例化
    rfc = rfc.fit(Xtrain, Ytrain)  # 训练
    Ypredict = rfc.predict(Xtest)  # 预测结果,就是要填补缺失值的值
    # 将填补好的特征返回到我们的原始的特征矩阵中
    X_missing_reg.loc[X_missing_reg.iloc[:, i].isnull(), X_missing_reg.columns[i]] = Ypredict

X_missing_reg['time']=timecolumn
X_missing_reg.set_index('time', inplace=True)
dailyinfoRandomForestRegressor=X_missing_reg
#             tavg  tmin  tmax  prcp  wdir  wspd    pres  tavgtemp  tmintemp  tmaxtemp  prcptemp  wdirtemp  wspdtemp  prestemp
# time
# 2024-12-0121.117.124.8   0.079.0   8.11013.9    21.100    17.100    24.800     0.000     79.00     8.1001013.900
# 2024-12-0222.819.925.4   0.077.0   9.71013.7    22.800    20.287    25.406     0.000     77.00     9.7001013.700
# 2024-12-0323.820.926.0   0.076.0   9.71014.4    23.800    20.900    26.000     0.000     76.00     9.7001014.400
# 2024-12-0423.421.526.6   1.570.011.61014.8    23.400    21.500    26.600     1.500     70.00    11.6001014.800

第七段代码是将之前若干副本中的插入值和实际值进行均方误差计算,最后通过可视化方式呈现出准确率,这里抛掉了一些常量值填充,误差太大,影响最后误差率呈现效果。

metricdata = (
              # ('dailyinfo_fillna',dailyinfo_fillna),
              # ('dailyinfo_ffill',dailyinfo_ffill),
              # ('dailyinfo_bfill',dailyinfo_bfill),
              ('dailyinfo_mean',dailyinfo_mean),
              ('dailyinfo_median',dailyinfo_median),
              ('dailyinfo_mode',dailyinfo_mode),
              ('dailyinfo_linear',dailyinfo_linear),
              ('dailyinfo_time',dailyinfo_time),
              ('dailyinfo_values',dailyinfo_values),
              ('dailyinfo_nearest',dailyinfo_nearest),
              ('dailyinfo_slinear',dailyinfo_slinear),
              ('dailyinfo_quadratic',dailyinfo_quadratic),
              ('dailyinfo_cubice',dailyinfo_cubice),
              ('dailyinfo_polynomial',dailyinfo_polynomial),
              # ('dailyinfo_barycentric',dailyinfo_barycentric),
              ('dailyinfo_pchip',dailyinfo_pchip),
              ('dailyinfo_spline',dailyinfo_spline),
              ('dailyinfocopy_mean_copy1',dailyinfocopy_mean_copy1),
              ('dailyinfocopy_mean_copy2',dailyinfocopy_mean_copy2),
              ('dailyinfocopy_mean_copy3',dailyinfocopy_mean_copy3),
              ('dailyinfocopy_median',dailyinfocopy_median),
              ('dailyinfocopy_most_frequent',dailyinfocopy_most_frequent),
              # ('dailyinfocopy_constant',dailyinfocopy_constant),
              ('dailyinfocopy_KNN',dailyinfocopy_KNN),
              ('dailyinfoRandomForestRegressor',dailyinfoRandomForestRegressor))

# 创建一个空字典,记录各缺失值算法的准确率
targetnames=['wdirtemp','prcptemp' ,'tmaxtemp', 'tavgtemp' ,'wspdtemp' ,'prestemp' ,'tmintemp']
resourcenames=['wdir' ,'prcp', 'tmax' ,'tavg', 'wspd', 'pres', 'tmin']
accuracydict={}
for logicname, logicpandas in metricdata:
    accuracy=0
    for targetname,resourcename in zip(resourcenames,targetnames):
        accuracy += mean_squared_error(logicpandas[resourcename], logicpandas[targetname])
    accuracydict[logicname]=accuracy
    # print(logicname,':',accuracy)

# accuracydict
# dailyinfo_mean : 36.19240932807212
# dailyinfo_median : 24.932661290322585
# dailyinfo_mode : 54.81419354838708
# dailyinfo_linear : 24.311348566308247
# dailyinfo_time : 24.311348566308247
# dailyinfo_values : 24.311348566308247
# dailyinfo_nearest : 36.24580645161289
# dailyinfo_slinear : 24.31134856630825
# dailyinfo_quadratic : 18.373887784634906
# dailyinfo_cubice : 17.624795757494642
# dailyinfo_polynomial : 18.373887784634906
# dailyinfo_pchip : 23.852328514247336
# dailyinfo_spline : 71.26528378383206
# dailyinfocopy_mean_copy1 : 36.19240932807212
# dailyinfocopy_mean_copy2 : 36.19240932807212
# dailyinfocopy_mean_copy3 : 36.19240932807212
# dailyinfocopy_median : 24.932661290322585
# dailyinfocopy_most_frequent : 54.81419354838708
# dailyinfocopy_KNN : 22.760931899641594
# dailyinfoRandomForestRegressor : 1.619480741935444

plt.figure(figsize=(12, 6))
cmap = plt.cm.viridis  # 使用viridis色图
colors = [cmap(i / len(accuracydict)) for i in range(len(accuracydict))]  # 为每个条形生成颜色
plt.barh(accuracydict.keys() , accuracydict.values() , color=colors)
plt.show()

我们可以看一下最后的效果,随机森林是最高的,有些意外的是近邻法反而比较低,插值法比平均数、众数还要稍差一些,当然这只是一次的比较而已,也可能时间数据太少的原因。

f7966a4fbd0f7b49a3dbd8dcf71c49e9.png

把时间startdate=datetime(2023,1,1)后,再执行各类方法的准确率又发生了变化。

b4834f2fa367e80cd71c3e5c6241a442.png

总的来说,随机森林的效果是最佳的,随着数据量的增加插值法的效果要好于均数、众数、中位数等等。

最后欢迎关注公众号:python与大数据分析

图片

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐