我想在非常大的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
,同时具有相同的视觉效果?
答案 0 :(得分:1)
下面我将展示如何创建内存占用较少的分类系列。 但是,请记住,如果问题允许,process your DataFrame in chunks可能会更容易。
NumPy数组中的最小值(以内存方式)占用1个字节。这些数组的dtype为np.int8
(对于8位整数),或np.bool
或np.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