用 pandas 中训练数据的平均值填充测试数据中的 nan 值
Fill nan values in test data with mean values form train data in pandas
我正在尝试用基于多列或按列分组的训练数据的平均值填充测试数据中的 nan 值。以下是部分测试数据:
date_block_num shop_id item_id item_category_id target item_price avg_item_price sum_item_cnt_day avg_item_cnt_day shop_avg_item_price ... avg_item_cnt_day_lag_12 shop_avg_item_price_lag_12 shop_sum_item_cnt_day_lag_12 shop_avg_item_cnt_day_lag_12 category_avg_item_price_lag_12 category_sum_item_cnt_day_lag_12 category_avg_item_cnt_day_lag_12 shop_avg_item_price_per_category_lag_12 shop_sum_item_cnt_per_category_lag_12 shop_avg_item_cnt_per_category_lag_12
0 26.5 5 5037 19.0 0.928571 1788.897788 1934.764286 90.714286 1.937141 868.822366 ... 0.383736 619.341077 181.571429 0.029328 716.813821 779.214286 0.084066 716.052585 10.285714 0.056515
1 NaN 5 5320 NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 30.0 5 5233 19.0 1.428571 854.861715 842.778086 72.428571 1.685456 914.767445 ... 0.000000 597.460870 0.000000 0.000000 591.507516 0.000000 0.000000 591.790514 0.000000 0.000000
3 32.0 5 5232 23.0 0.333333 728.018465 790.297277 47.000000 1.100087 965.966832 ... 0.000000 597.460870 0.000000 0.000000 591.507516 0.000000 0.000000 591.790514 0.000000 0.000000
4 NaN 5 5268 NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
5 rows × 102 columns
所以我想用基于 item_id.
的列的平均值替换 nans
首先我知道我可以得到按item_id分组的火车数据列的平均值如下:
mt = train.groupby('item_id').apply(lambda x: np.mean(x))
然后 我看到我尝试对测试集中的每一列使用 fillna,如下所示:
for col in test.columns:
test[col] = test.groupby('item_id')[col].apply(lambda x: x.fillna...)
我不知道如何用火车的平均值替换而不是测试。怎么做?这是最好的方法还是有更好的方法?谢谢。
如果您的 train
和 test
数据框具有相同的形状(# 行,# 列)
我们可以使用pandas.DataFrame.combine_first
为此,但不与 groupby
中发生的聚合相结合,因为 combine_first
将 NaN
替换为另一个数据帧中相同位置的值。
这就是我们需要为此使用 pandas.DataFrame.transform
的原因,因为它使数据帧的 shape
保持不变:
# make two example dataframes
train = pd.DataFrame({'item_id':[5037, 5320, 5037, 5320],
'num1': [10, 8, 9, 5],
'num2': [3, 5, 1, 9]})
test = pd.DataFrame({'item_id':[5037, 5320, 5037, 5320],
'num1': [6, np.NaN, 3, 7],
'num2': [np.NaN, 4, np.NaN, 9]})
print(train, '\n')
print(test)
item_id num1 num2
0 5037 10 3
1 5320 8 5
2 5037 9 1
3 5320 5 9
item_id num1 num2
0 5037 6.0 NaN
1 5320 NaN 4.0
2 5037 3.0 NaN
3 5320 7.0 9.0
我们应用 groupby.transform
和 combine_first
train_means = train.groupby('item_id').transform('mean')
test.combine_first(train_means)
item_id num1 num2
0 5037 6.0 2.0
1 5320 6.5 4.0
2 5037 3.0 2.0
3 5320 7.0 9.0
如果您的 train
和 test
数据框的形状不同(# 行,# 列),
它变得有点复杂。
我们可以做到以下几点:
- 我们可以用
pandas.groupby.mean
得到每个 item_id
和值的平均值
- 之后我们
pandas.DataFrame.merge
将每个对应的item_id
的均值和train
数据帧的均值获取到我们的test
数据帧。
- 然后我们根据我们的列名进行口述,并有条件地用来自
train
数据集的同一列的值填充我们的 NaN
,其中应用了 groupby
.我们为此使用 np.where
。
train_grp = train.groupby('item_id').mean().reset_index()
print(train_grp)
item_id num1 num2
0 5037 9.5 2.0
1 5320 6.5 7.0
应用合并
test_merged = test.merge(train_grp, on='item_id', suffixes=['_test', '_train'])
print(test_merged)
item_id num1_test num2_test num1_train num2_train
0 5037 6.0 NaN 9.5 2.0
1 5037 3.0 NaN 9.5 2.0
2 5320 NaN 4.0 6.5 7.0
3 5320 7.0 9.0 6.5 7.0
创建对应列的字典
test_cols = [col for col in test_merged.columns if 'test' in col]
train_cols = [col for col in test_merged.columns if 'train' in col]
dict_cols =dict(zip(test_cols, train_cols))
print(dict_cols)
{'num1_test': 'num1_train', 'num2_test': 'num2_train'}
有条件替换Nan
for test, train in dict_cols.items():
test_merged[test] = np.where(test_merged[test].isnull(),
test_merged[train],
test_merged[test])
# Clean up dataframe
test_merged.drop(train_cols, axis=1, inplace=True)
test_merged.columns = test_merged.columns.str.replace('_test', '')
print(test_merged)
item_id num1 num2
0 5037 6.0 2.0
1 5037 3.0 2.0
2 5320 6.5 4.0
3 5320 7.0 9.0
说明
np.where
的工作原理如下:np.where(condition, value if true, value if false)
我正在尝试用基于多列或按列分组的训练数据的平均值填充测试数据中的 nan 值。以下是部分测试数据:
date_block_num shop_id item_id item_category_id target item_price avg_item_price sum_item_cnt_day avg_item_cnt_day shop_avg_item_price ... avg_item_cnt_day_lag_12 shop_avg_item_price_lag_12 shop_sum_item_cnt_day_lag_12 shop_avg_item_cnt_day_lag_12 category_avg_item_price_lag_12 category_sum_item_cnt_day_lag_12 category_avg_item_cnt_day_lag_12 shop_avg_item_price_per_category_lag_12 shop_sum_item_cnt_per_category_lag_12 shop_avg_item_cnt_per_category_lag_12
0 26.5 5 5037 19.0 0.928571 1788.897788 1934.764286 90.714286 1.937141 868.822366 ... 0.383736 619.341077 181.571429 0.029328 716.813821 779.214286 0.084066 716.052585 10.285714 0.056515
1 NaN 5 5320 NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 30.0 5 5233 19.0 1.428571 854.861715 842.778086 72.428571 1.685456 914.767445 ... 0.000000 597.460870 0.000000 0.000000 591.507516 0.000000 0.000000 591.790514 0.000000 0.000000
3 32.0 5 5232 23.0 0.333333 728.018465 790.297277 47.000000 1.100087 965.966832 ... 0.000000 597.460870 0.000000 0.000000 591.507516 0.000000 0.000000 591.790514 0.000000 0.000000
4 NaN 5 5268 NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
5 rows × 102 columns
所以我想用基于 item_id.
的列的平均值替换 nans首先我知道我可以得到按item_id分组的火车数据列的平均值如下:
mt = train.groupby('item_id').apply(lambda x: np.mean(x))
然后 我看到我尝试对测试集中的每一列使用 fillna,如下所示:
for col in test.columns:
test[col] = test.groupby('item_id')[col].apply(lambda x: x.fillna...)
我不知道如何用火车的平均值替换而不是测试。怎么做?这是最好的方法还是有更好的方法?谢谢。
如果您的 train
和 test
数据框具有相同的形状(# 行,# 列)
我们可以使用pandas.DataFrame.combine_first
为此,但不与 groupby
中发生的聚合相结合,因为 combine_first
将 NaN
替换为另一个数据帧中相同位置的值。
这就是我们需要为此使用 pandas.DataFrame.transform
的原因,因为它使数据帧的 shape
保持不变:
# make two example dataframes
train = pd.DataFrame({'item_id':[5037, 5320, 5037, 5320],
'num1': [10, 8, 9, 5],
'num2': [3, 5, 1, 9]})
test = pd.DataFrame({'item_id':[5037, 5320, 5037, 5320],
'num1': [6, np.NaN, 3, 7],
'num2': [np.NaN, 4, np.NaN, 9]})
print(train, '\n')
print(test)
item_id num1 num2
0 5037 10 3
1 5320 8 5
2 5037 9 1
3 5320 5 9
item_id num1 num2
0 5037 6.0 NaN
1 5320 NaN 4.0
2 5037 3.0 NaN
3 5320 7.0 9.0
我们应用 groupby.transform
和 combine_first
train_means = train.groupby('item_id').transform('mean')
test.combine_first(train_means)
item_id num1 num2
0 5037 6.0 2.0
1 5320 6.5 4.0
2 5037 3.0 2.0
3 5320 7.0 9.0
如果您的 train
和 test
数据框的形状不同(# 行,# 列),
它变得有点复杂。
我们可以做到以下几点:
- 我们可以用
pandas.groupby.mean
得到每个 - 之后我们
pandas.DataFrame.merge
将每个对应的item_id
的均值和train
数据帧的均值获取到我们的test
数据帧。 - 然后我们根据我们的列名进行口述,并有条件地用来自
train
数据集的同一列的值填充我们的NaN
,其中应用了groupby
.我们为此使用np.where
。
item_id
和值的平均值
train_grp = train.groupby('item_id').mean().reset_index()
print(train_grp)
item_id num1 num2
0 5037 9.5 2.0
1 5320 6.5 7.0
应用合并
test_merged = test.merge(train_grp, on='item_id', suffixes=['_test', '_train'])
print(test_merged)
item_id num1_test num2_test num1_train num2_train
0 5037 6.0 NaN 9.5 2.0
1 5037 3.0 NaN 9.5 2.0
2 5320 NaN 4.0 6.5 7.0
3 5320 7.0 9.0 6.5 7.0
创建对应列的字典
test_cols = [col for col in test_merged.columns if 'test' in col]
train_cols = [col for col in test_merged.columns if 'train' in col]
dict_cols =dict(zip(test_cols, train_cols))
print(dict_cols)
{'num1_test': 'num1_train', 'num2_test': 'num2_train'}
有条件替换Nan
for test, train in dict_cols.items():
test_merged[test] = np.where(test_merged[test].isnull(),
test_merged[train],
test_merged[test])
# Clean up dataframe
test_merged.drop(train_cols, axis=1, inplace=True)
test_merged.columns = test_merged.columns.str.replace('_test', '')
print(test_merged)
item_id num1 num2
0 5037 6.0 2.0
1 5037 3.0 2.0
2 5320 6.5 4.0
3 5320 7.0 9.0
说明
np.where
的工作原理如下:np.where(condition, value if true, value if false)