合并 pandas 数据帧使用太多内存
Merging pandas Data frames uses way too much memory
我正在研究 this Kaggle competition as the final project for the course I'm taking, and for that, I was trying to replicate this notebook,但他使用一个函数来获取滞后的功能,这对我来说占用了太多内存。这是他的代码:
def lag_feature(df, lags, col):
tmp = df[['date_block_num','shop_id','item_id',col]]
for i in lags:
shifted = tmp.copy()
shifted.columns = ['date_block_num','shop_id','item_id', col+'_lag_'+str(i)]
shifted['date_block_num'] += i
df = pd.merge(df, shifted, on=['date_block_num','shop_id','item_id'], how='left')
return df
在使用他的代码 运行 失败后,我做了一些细微的修改以尝试减少内存使用,我开始使用 google colab,因为它比我的笔记本电脑有更多的内存所以这里是我的代码:
def lag_feature(df, lags, col):
df = dd.from_pandas(df, chunksize=1000)
tmp = df[['date_block_num','shop_id','item_id',col]]
for i in lags:
shifted = tmp[tmp.date_block_num + i <= 34].copy()
shifted.columns = ['date_block_num','shop_id','item_id', col+'_lag_'+str(i)]
shifted['date_block_num'] += i
df = dd.merge(df, shifted, on=['date_block_num','shop_id','item_id'], how='left')
return df.compute()
但仍然使用太多内存,以至于我的代码使用了 google 为该函数调用提供的 10 Gb 内存
sales_train = lag_feature(sales_train, [1, 2, 3, 12, 20], 'item_cnt_month')
有什么方法可以减少内存使用量?只是为了展示,这是我的数据框:
Int64Index: 2829445 entries, 0 to 3134798
Data columns (total 8 columns):
date object
date_block_num int8
item_cnt_day float16
item_id int16
item_price float16
shop_id int8
item_cnt_month float16
item_category_id int8
dtypes: float16(3), int16(1), int8(3), object(1)
memory usage: 152.9+ MB
只是为了添加更多信息,'date_block_num' 列保留了一个数字,用于标识该功能发生在哪个月份,我想要做的是将上个月的一些数据放入该行。因此,如果我的延迟为 1,则意味着我想为我的数据框中的每个产品获取一个月前的数据,并将其添加到名称为 'feature_lag_1' 的另一列中。例如,使用此数据框:
date date_block_num item_cnt_day item_id item_price shop_id \
0 14.09.2013 8 1.0 2848 99.0 24
1 14.09.2013 8 1.0 2848 99.0 24
2 14.09.2013 8 1.0 2848 99.0 24
3 01.09.2013 8 1.0 2848 99.0 24
4 01.09.2013 8 1.0 2848 99.0 24
item_cnt_month item_category_id
0 2.0 30
1 2.0 30
2 2.0 30
3 2.0 30
4 2.0 30
这个函数调用:
sales_train = lag_feature(sales_train, [1], 'item_cnt_month')
我想要这个输出:
date date_block_num item_cnt_day item_id item_price shop_id \
0 14.09.2013 8 1.0 2848 99.0 24
1 14.09.2013 8 1.0 2848 99.0 24
2 14.09.2013 8 1.0 2848 99.0 24
3 01.09.2013 8 1.0 2848 99.0 24
4 01.09.2013 8 1.0 2848 99.0 24
item_cnt_month item_category_id item_cnt_month_lag_1
0 2.0 30 3.0
1 2.0 30 3.0
2 2.0 30 3.0
3 2.0 30 3.0
4 2.0 30 3.0
调用 df.compute()
会将您的完整结果转换为 Pandas Dataframe,因此如果您的结果不适合内存,那么 Dask 将不会在这里帮助您。
相反,更常见的是不调用计算,而是最终计算某种适合内存的聚合,或者,如果您需要完整的数据帧,则将其写入磁盘,如 df.to_parquet()
在Python 3.6+中可以重写该函数如下(需要先对dataframe进行预排序):
df = df.sort_values(['date_block_num']).reset_index(drop=True)
def lag_feature(df, lags, col):
key_columns = ['shop_id', 'item_id']
for lag in lags:
all_but_col = list(df.columns.difference([col]))
df[f'{col}_lag_{lag}'] = (df.set_index(all_but_col)
.groupby(level=key_columns)
.shift(lag)
.reset_index(drop=True))
return df
您面临的内存问题可能是由于具有同一数据帧的多个(子)副本。在pandas中没有必要这样做,正如其他人指出的那样,.shift
功能可以实现你需要的。
首先创建一个 pandas 数据框,它有两个商店,即 24 和 25。
df = pd.DataFrame({'shop_id':[24, 24, 24, 24, 24, 25, 25, 25, 25, 25],
'item_id': [2000, 2000, 2000, 3000, 3000, 1000, 1000, 1000, 1000, 1000],
'date_block_num': [7, 8, 9, 7, 8, 5, 6, 7, 8, 9],
'item_cnt_month': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]})
+-------+-------+--------------+--------------+
|shop_id|item_id|date_block_num|item_cnt_month|
+-------+-------+--------------+--------------+
| 24| 2000| 7| 1|
| 24| 2000| 8| 2|
| 24| 2000| 9| 3|
| 24| 3000| 7| 4|
| 24| 3000| 8| 5|
| 25| 1000| 5| 6|
| 25| 1000| 6| 7|
| 25| 1000| 7| 8|
| 25| 1000| 8| 9|
| 25| 1000| 9| 10|
+-------+-------+--------------+--------------+
24号店有商品2000和3000
日期块 7 中项目 2000 有 1 个计数,日期块 8 中有 2 个计数,依此类推
目标是创建一个 item_cnt_month 滞后列,其值为 item_cnt_month n 个月前,用于该商店的该商品。
要创建滞后特征,您可以使用下面的函数。
def lag_features(df, lags, group_cols, shift_col):
"""
Arguments:
df (pd.DataFrame)
lags (list((int)): the number of months to lag by
group_cols (list(str)): the list of columns that need to be the merged key
shift_col (str): the column name that is to be shifted by
"""
for lag in lags:
new_col = '{0}_lag_{1}'.format(shift_col, lag)
df[new_col] = df.groupby(group_cols)[shift_col].shift(lag)
return df
通过调用
lags = [1, 2]
group_cols = ['shop_id', 'item_id']
shift_col = 'item_cnt_month'
order_col = 'date_block_num'
df = df.sort_values(by=group_cols+[order_col], ascending=True)
df = lag_features(df, lags, group_cols, shift_col)
结果是这样的:
+-------+-------+--------------+--------------+--------------------+--------------------+
|shop_id|item_id|date_block_num|item_cnt_month|item_cnt_month_lag_1|item_cnt_month_lag_2|
+-------+-------+--------------+--------------+--------------------+--------------------+
| 24| 2000| 7| 1| NaN| NaN|
| 24| 2000| 8| 2| 1.0| NaN|
| 24| 2000| 9| 3| 2.0| 1.0|
| 24| 3000| 7| 4| NaN| NaN|
| 24| 3000| 8| 5| 4.0| NaN|
| 25| 1000| 5| 6| NaN| NaN|
| 25| 1000| 6| 7| 6.0| NaN|
| 25| 1000| 7| 8| 7.0| 6.0|
| 25| 1000| 8| 9| 8.0| 7.0|
| 25| 1000| 9| 10| 9.0| 8.0|
+-------+-------+--------------+--------------+--------------------+--------------------+
请注意,因为没有显式连接,您需要使用 .sort_values(all key columns and date column)
正确排序数据框
我正在研究 this Kaggle competition as the final project for the course I'm taking, and for that, I was trying to replicate this notebook,但他使用一个函数来获取滞后的功能,这对我来说占用了太多内存。这是他的代码:
def lag_feature(df, lags, col):
tmp = df[['date_block_num','shop_id','item_id',col]]
for i in lags:
shifted = tmp.copy()
shifted.columns = ['date_block_num','shop_id','item_id', col+'_lag_'+str(i)]
shifted['date_block_num'] += i
df = pd.merge(df, shifted, on=['date_block_num','shop_id','item_id'], how='left')
return df
在使用他的代码 运行 失败后,我做了一些细微的修改以尝试减少内存使用,我开始使用 google colab,因为它比我的笔记本电脑有更多的内存所以这里是我的代码:
def lag_feature(df, lags, col):
df = dd.from_pandas(df, chunksize=1000)
tmp = df[['date_block_num','shop_id','item_id',col]]
for i in lags:
shifted = tmp[tmp.date_block_num + i <= 34].copy()
shifted.columns = ['date_block_num','shop_id','item_id', col+'_lag_'+str(i)]
shifted['date_block_num'] += i
df = dd.merge(df, shifted, on=['date_block_num','shop_id','item_id'], how='left')
return df.compute()
但仍然使用太多内存,以至于我的代码使用了 google 为该函数调用提供的 10 Gb 内存
sales_train = lag_feature(sales_train, [1, 2, 3, 12, 20], 'item_cnt_month')
有什么方法可以减少内存使用量?只是为了展示,这是我的数据框:
Int64Index: 2829445 entries, 0 to 3134798
Data columns (total 8 columns):
date object
date_block_num int8
item_cnt_day float16
item_id int16
item_price float16
shop_id int8
item_cnt_month float16
item_category_id int8
dtypes: float16(3), int16(1), int8(3), object(1)
memory usage: 152.9+ MB
只是为了添加更多信息,'date_block_num' 列保留了一个数字,用于标识该功能发生在哪个月份,我想要做的是将上个月的一些数据放入该行。因此,如果我的延迟为 1,则意味着我想为我的数据框中的每个产品获取一个月前的数据,并将其添加到名称为 'feature_lag_1' 的另一列中。例如,使用此数据框:
date date_block_num item_cnt_day item_id item_price shop_id \
0 14.09.2013 8 1.0 2848 99.0 24
1 14.09.2013 8 1.0 2848 99.0 24
2 14.09.2013 8 1.0 2848 99.0 24
3 01.09.2013 8 1.0 2848 99.0 24
4 01.09.2013 8 1.0 2848 99.0 24
item_cnt_month item_category_id
0 2.0 30
1 2.0 30
2 2.0 30
3 2.0 30
4 2.0 30
这个函数调用:
sales_train = lag_feature(sales_train, [1], 'item_cnt_month')
我想要这个输出:
date date_block_num item_cnt_day item_id item_price shop_id \
0 14.09.2013 8 1.0 2848 99.0 24
1 14.09.2013 8 1.0 2848 99.0 24
2 14.09.2013 8 1.0 2848 99.0 24
3 01.09.2013 8 1.0 2848 99.0 24
4 01.09.2013 8 1.0 2848 99.0 24
item_cnt_month item_category_id item_cnt_month_lag_1
0 2.0 30 3.0
1 2.0 30 3.0
2 2.0 30 3.0
3 2.0 30 3.0
4 2.0 30 3.0
调用 df.compute()
会将您的完整结果转换为 Pandas Dataframe,因此如果您的结果不适合内存,那么 Dask 将不会在这里帮助您。
相反,更常见的是不调用计算,而是最终计算某种适合内存的聚合,或者,如果您需要完整的数据帧,则将其写入磁盘,如 df.to_parquet()
在Python 3.6+中可以重写该函数如下(需要先对dataframe进行预排序):
df = df.sort_values(['date_block_num']).reset_index(drop=True)
def lag_feature(df, lags, col):
key_columns = ['shop_id', 'item_id']
for lag in lags:
all_but_col = list(df.columns.difference([col]))
df[f'{col}_lag_{lag}'] = (df.set_index(all_but_col)
.groupby(level=key_columns)
.shift(lag)
.reset_index(drop=True))
return df
您面临的内存问题可能是由于具有同一数据帧的多个(子)副本。在pandas中没有必要这样做,正如其他人指出的那样,.shift
功能可以实现你需要的。
首先创建一个 pandas 数据框,它有两个商店,即 24 和 25。
df = pd.DataFrame({'shop_id':[24, 24, 24, 24, 24, 25, 25, 25, 25, 25],
'item_id': [2000, 2000, 2000, 3000, 3000, 1000, 1000, 1000, 1000, 1000],
'date_block_num': [7, 8, 9, 7, 8, 5, 6, 7, 8, 9],
'item_cnt_month': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]})
+-------+-------+--------------+--------------+
|shop_id|item_id|date_block_num|item_cnt_month|
+-------+-------+--------------+--------------+
| 24| 2000| 7| 1|
| 24| 2000| 8| 2|
| 24| 2000| 9| 3|
| 24| 3000| 7| 4|
| 24| 3000| 8| 5|
| 25| 1000| 5| 6|
| 25| 1000| 6| 7|
| 25| 1000| 7| 8|
| 25| 1000| 8| 9|
| 25| 1000| 9| 10|
+-------+-------+--------------+--------------+
24号店有商品2000和3000
日期块 7 中项目 2000 有 1 个计数,日期块 8 中有 2 个计数,依此类推
目标是创建一个 item_cnt_month 滞后列,其值为 item_cnt_month n 个月前,用于该商店的该商品。
要创建滞后特征,您可以使用下面的函数。
def lag_features(df, lags, group_cols, shift_col):
"""
Arguments:
df (pd.DataFrame)
lags (list((int)): the number of months to lag by
group_cols (list(str)): the list of columns that need to be the merged key
shift_col (str): the column name that is to be shifted by
"""
for lag in lags:
new_col = '{0}_lag_{1}'.format(shift_col, lag)
df[new_col] = df.groupby(group_cols)[shift_col].shift(lag)
return df
通过调用
lags = [1, 2]
group_cols = ['shop_id', 'item_id']
shift_col = 'item_cnt_month'
order_col = 'date_block_num'
df = df.sort_values(by=group_cols+[order_col], ascending=True)
df = lag_features(df, lags, group_cols, shift_col)
结果是这样的:
+-------+-------+--------------+--------------+--------------------+--------------------+
|shop_id|item_id|date_block_num|item_cnt_month|item_cnt_month_lag_1|item_cnt_month_lag_2|
+-------+-------+--------------+--------------+--------------------+--------------------+
| 24| 2000| 7| 1| NaN| NaN|
| 24| 2000| 8| 2| 1.0| NaN|
| 24| 2000| 9| 3| 2.0| 1.0|
| 24| 3000| 7| 4| NaN| NaN|
| 24| 3000| 8| 5| 4.0| NaN|
| 25| 1000| 5| 6| NaN| NaN|
| 25| 1000| 6| 7| 6.0| NaN|
| 25| 1000| 7| 8| 7.0| 6.0|
| 25| 1000| 8| 9| 8.0| 7.0|
| 25| 1000| 9| 10| 9.0| 8.0|
+-------+-------+--------------+--------------+--------------------+--------------------+
请注意,因为没有显式连接,您需要使用 .sort_values(all key columns and date column)