如何用分类类型将df.loc多个条件替换为pandas数据框中的安全内存?

时间:2017-02-08 10:23:17

标签: python pandas boolean

我想在非常大的pandas数据框df1(> 10GB,作为.csv)中添加一列,指示是否满足其他列中的多个条件。

目前,我正在做

df.loc[(df.col1  == 1) & (df.col2 == 0) & (df.col3 == 1), "col4"] = "start"

df.loc[(df.col1  == 1) & (df2.col2 == 1) & (df.col3 == 0), "col4"] = "stop"

但是,我从第一个MemoryError行获得df.loc。这很可能是由于内存中有数百万个“开始”和“strop”字符串。

如何使用布尔条件替换df.loc行以避免MemoryError,同时具有相同的视觉效果?

1 个答案:

答案 0 :(得分:1)

下面我将展示如何创建内存占用较少的分类系列。 但是,请记住,如果问题允许,process your DataFrame in chunks可能会更容易。

NumPy数组中的最小值(以内存方式)占用1个字节。这些数组的dtype为np.int8(对于8位整数),或np.boolnp.dtype('S1')

In [121]: np.dtype('int8').itemsize
Out[121]: 1

In [124]: np.dtype('int64').itemsize
Out[124]: 8

In [122]: np.dtype('bool').itemsize
Out[122]: 1

In [123]: np.dtype('S1').itemsize
Out[123]: 1

DataFrame列中的基础数据存储在NumPy数组中。 因此,要使DataFrame尽可能小,请使用1字节dtype。 这会创建一个填充零的dtype int8的DataFrame列:

df['col4'] = np.zeros(len(df), dtype='int8')

1代表"start"2代表"stop"

df.loc[(df.col1  == 1) & (df.col2 == 0) & (df.col3 == 1), "col4"] = 1
df.loc[(df.col1  == 1) & (df.col2 == 1) & (df.col3 == 0), "col4"] = 2

请注意,除col4列所需的内存外,上面两行需要额外的空间来计算4个布尔系列:一个用于3个条件中的每一个,第四个用于组合它们。如果这些行仍然引发MemoryErrors,您可以尝试

mask = (df.col1  == 1)     # <-- requires space for 1 boolean Series, `mask`
mask &= (df.col2 == 0)     # <-- requires space for 2 boolean Series: mask and a tempory Series
mask &= (df.col3 == 1)     # <-- requires no additional space
df.loc[mask, 'col4'] = 1   # <-- requires no additional space

如果保存记忆是最重要的,你应该停在这里。 但是,如果您希望将1和2显示为"start""stop",则可以将DataFrame列更改为category dtype:

df['col4'] = df['col4'].astype('category')

然后更改类别标签:

df['col4'].cat.categories = ['', 'start', 'stop']
import numpy as np
import pandas as pd
np.random.seed(2017)
nrows, ncols = 20, 3
df = pd.DataFrame(np.random.randint(2, size=(nrows, ncols)), 
                  columns=['col1', 'col2', 'col3'])
df['col4'] = np.zeros(len(df), dtype='int8')
print(df['col4'].nbytes)
# df.loc[(df.col1  == 1) & (df.col2 == 0) & (df.col3 == 1), "col4"] = 1
# df.loc[(df.col1  == 1) & (df.col2 == 1) & (df.col3 == 0), "col4"] = 2
mask = (df.col1  == 1)
mask &= (df.col2 == 0)
mask &= (df.col3 == 1)
df.loc[mask, 'col4'] = 1
mask = (df.col1  == 1)
mask &= (df.col2 == 1)
mask &= (df.col3 == 0)
df.loc[mask, 'col4'] = 2
df['col4'] = df['col4'].astype('category')
print(df['col4'].nbytes)
df['col4'].cat.categories = ['', 'start', 'stop']
print(df['col4'].nbytes)
print(df)

产量

20   # the number of bytes required by `col4`
44   # a category column requires a bit more space
44   # the change of labels require a tiny bit more space, but not shown here

    col1  col2  col3   col4
0      1     1     0   stop
1      1     0     0       
2      0     0     1       
3      1     1     1       
4      0     0     0       
5      0     0     1       
6      1     0     0       
7      0     0     0       
8      1     0     1  start
9      1     1     0   stop
10     1     1     1       
11     1     0     1  start
12     0     0     0       
13     0     0     1       
14     0     0     0       
15     1     0     1  start
16     0     1     0       
17     0     1     1       
18     1     0     1  start
19     0     0     1