2017-02-08 1 views
0

Я хочу добавить столбец к очень большому файлу данных pandas df1 (> 10GB как .csv), который указывает, выполнены ли несколько условий в других столбцах.Как заменить условие множественного df.loc категориальным типом на безопасную память в кадре данных pandas?

В настоящее время, я делаю

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 линии. Это, скорее всего, связано с миллионами строк «start» и «strop» в памяти.

Как заменить линии df.loc логическим состоянием, чтобы избежать MemoryError, имея одинаковые визуальные результаты?

ответ

1

Ниже я покажу, как создать категоричную серию с небольшим объемом памяти. Тем не менее, имейте в виду, что может быть проще process your DataFrame in chunks, если ваша проблема позволяет.


Наименьшие значения (по памяти) в массиве NumPy занимают 1 байт. Эти массивы имеют dtype np.int8 (для 8-битных ints), или 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. Это создает столбец DataFrame из DTYPE int8 заполненный нулями:

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 булевых рядов: по одному для каждого из трех условий и четвертого для их объединения. Если эти линии еще поднять 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  
+0

большое спасибо! Поскольку я продолжаю работать с df2.groupby (["col4"]). Cumcount() + 1, я бы хотел заменить "" категорию NaN.Это возможно? – sudonym

+0

Начиная с версии Pandas 0.19: «Установка NaN в« категориях »устарела и будет удалена в будущей версии pandas». Поэтому для обеспечения будущего кода не устанавливайте метку категории на «NaN». – unutbu

+0

Вместо этого вы можете удалить строки, где 'col4' пуст, прежде чем применять groupby:' df.loc [df ['col4']! = '']. Groupby (['col4']). Cumcount() '. – unutbu

Смежные вопросы