使用先前有效值的趋势在 Pandas Dataframe 中填充 NaN
Fill in NaN in Pandas Dataframe using trend of previous valid values
我试图通过分组来填补数据中的空白,然后使用先前数据点的趋势来预测缺失值是什么。
df
Group Week Value
B 1 5
B 2 6
B 3 NaN
B 4 NaN
B 5 NaN
B 6 8
B 7 8
B 8 7
B 9 6
B 10 NaN
图形看起来像这样:
Initial df plot
一旦所需的功能发生,数据框将如下所示:
Group Week Value
B 1 5
B 2 6
B 3 7
B 4 8
B 5 9
B 6 8
B 7 8
B 8 7
B 9 6
B 10 5.5
找到这些 NaN 值的先前点的趋势在此处以图形方式显示:
NaN values calculated
本例中的前三个 NaN 值是通过简单地绘制值 5
和 6
得到的,找到线性方程 (y = mx + c) 并将 x 拟合为周计算 y。将对所有 NaN 值执行相同的过程
我试过插值 (df = df.groupby('Group').apply(lambda group: group.interpolate(method='index'))
但这显然会查看下一个有效数据点并将其包含在计算中,我试图避免这种情况
可能值得注意的是,我使用的数据框有 200,000 行和 4,000 个组!
您可以创建子组系列 g
并将 method="spline"
和 order=1
传递给 interpolate
:
g = df['Value'].mask(df['Value'].notnull(), df['Value'].isnull().cumsum()).ffill()
df['Value'] = (df.groupby(['Group', g])['Value']
.apply(lambda x: x.interpolate(method="spline", order=1)))
df
Out[1]:
Group Week Value
0 B 1 5.0
1 B 2 6.0
2 B 3 7.0
3 B 4 8.0
4 B 5 9.0
5 B 6 8.0
6 B 7 8.0
7 B 8 7.0
8 B 9 6.0
9 B 10 5.5
获得 g
的中间步骤如下所示。
g = df['Value'].mask(df['Value'].notnull(), df['Value'].isnull().cumsum()).ffill()
g
Out[1]:
0 0.0
1 0.0
2 0.0
3 0.0
4 0.0
5 3.0
6 3.0
7 3.0
8 3.0
9 3.0
这些数字基本上只是创建子组。我的方法是实现这一目标的一种方法。
根据您的评论,我创建了一个 mask
m 来计算大小为 1 的组。然后,我使用 fillna()
:
组合单独的方法
df = pd.DataFrame({'Group': {0: 'A',
1: 'B',
2: 'B',
3: 'B',
4: 'B',
5: 'B',
6: 'B',
7: 'B',
8: 'B',
9: 'B'},
'Week': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10},
'Value': {0: 5.0,
1: 6.0,
2: np.nan,
3: np.nan,
4: np.nan,
5: 8.0,
6: 8.0,
7: 7.0,
8: 6.0,
9: np.nan}})
g = df['Value'].iloc[1:].mask(df['Value'].notnull(), df['Value'].isnull().cumsum()).ffill()
m = df.groupby(['Group', g])['Value'].transform('count') > 1
v1 = (df[m].groupby(['Group', g])['Value']
.apply(lambda x: x.interpolate(method="spline", order=1)))
v2 = (df.groupby(['Group', g])['Value']
.apply(lambda x: x.interpolate(method="index")))
df['Value'] = df['Value'].fillna(v1).fillna(v2)
df
我试图通过分组来填补数据中的空白,然后使用先前数据点的趋势来预测缺失值是什么。
df
Group Week Value
B 1 5
B 2 6
B 3 NaN
B 4 NaN
B 5 NaN
B 6 8
B 7 8
B 8 7
B 9 6
B 10 NaN
图形看起来像这样: Initial df plot
一旦所需的功能发生,数据框将如下所示:
Group Week Value
B 1 5
B 2 6
B 3 7
B 4 8
B 5 9
B 6 8
B 7 8
B 8 7
B 9 6
B 10 5.5
找到这些 NaN 值的先前点的趋势在此处以图形方式显示: NaN values calculated
本例中的前三个 NaN 值是通过简单地绘制值 5
和 6
得到的,找到线性方程 (y = mx + c) 并将 x 拟合为周计算 y。将对所有 NaN 值执行相同的过程
我试过插值 (df = df.groupby('Group').apply(lambda group: group.interpolate(method='index'))
但这显然会查看下一个有效数据点并将其包含在计算中,我试图避免这种情况
可能值得注意的是,我使用的数据框有 200,000 行和 4,000 个组!
您可以创建子组系列 g
并将 method="spline"
和 order=1
传递给 interpolate
:
g = df['Value'].mask(df['Value'].notnull(), df['Value'].isnull().cumsum()).ffill()
df['Value'] = (df.groupby(['Group', g])['Value']
.apply(lambda x: x.interpolate(method="spline", order=1)))
df
Out[1]:
Group Week Value
0 B 1 5.0
1 B 2 6.0
2 B 3 7.0
3 B 4 8.0
4 B 5 9.0
5 B 6 8.0
6 B 7 8.0
7 B 8 7.0
8 B 9 6.0
9 B 10 5.5
获得 g
的中间步骤如下所示。
g = df['Value'].mask(df['Value'].notnull(), df['Value'].isnull().cumsum()).ffill()
g
Out[1]:
0 0.0
1 0.0
2 0.0
3 0.0
4 0.0
5 3.0
6 3.0
7 3.0
8 3.0
9 3.0
这些数字基本上只是创建子组。我的方法是实现这一目标的一种方法。
根据您的评论,我创建了一个 mask
m 来计算大小为 1 的组。然后,我使用 fillna()
:
df = pd.DataFrame({'Group': {0: 'A',
1: 'B',
2: 'B',
3: 'B',
4: 'B',
5: 'B',
6: 'B',
7: 'B',
8: 'B',
9: 'B'},
'Week': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10},
'Value': {0: 5.0,
1: 6.0,
2: np.nan,
3: np.nan,
4: np.nan,
5: 8.0,
6: 8.0,
7: 7.0,
8: 6.0,
9: np.nan}})
g = df['Value'].iloc[1:].mask(df['Value'].notnull(), df['Value'].isnull().cumsum()).ffill()
m = df.groupby(['Group', g])['Value'].transform('count') > 1
v1 = (df[m].groupby(['Group', g])['Value']
.apply(lambda x: x.interpolate(method="spline", order=1)))
v2 = (df.groupby(['Group', g])['Value']
.apply(lambda x: x.interpolate(method="index")))
df['Value'] = df['Value'].fillna(v1).fillna(v2)
df