Как уменьшить использование памяти и ускорить код - PullRequest
3 голосов
/ 29 сентября 2019

Я использую огромный набор данных с 5 столбцами и более 90 миллионов строк. Код прекрасно работает с частью данных, но когда дело доходит до цели, я получаю ошибку памяти. Я читал о генераторах, но это кажется мне очень сложным. Могу ли я получить объяснение на основе этого кода?

df = pd.read_csv('D:.../test.csv', names=["id_easy","ordinal", "timestamp", "latitude", "longitude"])

df = df[:-1]
df.loc[:,'timestamp'] = pd.to_datetime(df.loc[:,'timestamp'])
pd.set_option('float_format', '{:f}'.format)
df['epoch'] = df.loc[:, 'timestamp'].astype('int64')//1e9
df['day_of_week'] = pd.to_datetime(df['epoch'], unit="s").dt.weekday_name
del df['timestamp']

for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']:
    day_df = df.loc[df['day_of_week'] == day]
    day_df.to_csv(f'{day}.csv', index=False,)

Ошибка появляется при последней операции for loop

Пример данных:

d4ace40905729245a5a0bc3fb748d2b3    1   2016-06-01T08:18:46.000Z    22.9484 56.7728
d4ace40905729245a5a0bc3fb748d2b3    2   2016-06-01T08:28:05.000Z    22.9503 56.7748

ОБНОВЛЕНО

Я сделал это:

chunk_list = []  

for chunk in df_chunk:  
    chunk_list.append(chunk)
df_concat = pd.concat(chunk_list)

Понятия не имею, как поступить сейчас? Как применить остальную часть кода?

Ответы [ 3 ]

2 голосов
/ 29 сентября 2019

Мой совет - переключиться на Dask или Spark .

Если вы хотите продолжить использовать панд, попробуйте следующие советы, чтобы прочитать файл CSV, с pandas.read_csv :

  1. chunksize параметр: тотпозволяет читать часть файлов одновременно. Например, в вашем случае вы можете использовать размер чанка, равный миллиону, вы получите 90 чанков и сможете работать с каждым чанком отдельно.
  2. dtype параметр: с помощью этого параметра вы можете указать тип данных каждогостолбец просто, передав словарь, как это: {‘a’: np.float32, ‘b’: np.int32, ‘c’: ‘Int32’}
    Панды могут использовать 64-разрядные типы данных, в то время как 32-разрядных может быть достаточно для вас. С помощью этого трюка вы могли бы сэкономить 50% пространства.

Практический пример

Попробуйте этот код:

df_chunks = pd.read_csv('test.csv', chunksize=1000000, iterator=True, 
                         parse_dates=['timestamp'], error_bad_lines=False,
                         dtype={"ordinal":'int32', "latitude": 'float32', "longitude":'float32'})
for chunk in df_chunks:
    # chunk = chunk.apply(...) # process the single chunk 
    for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']:
        day_df = chunk.loc[chunk['day_of_week'] == day]
        day_df.to_csv(f'{day}.csv', mode='a', index=0, header=False)

Таким образом, вы работаете с одним чанкомданных за один раз и никогда не работать со всеми данными вместе. mode='a' указывает пандам на добавление.

Примечание 1 : вам не нужно pandas.concat здесь. Единственное, что делает iterator и chunksize=1000000, это предоставить вам объект считывателя, который выполняет итерацию 1000000 строк, вместо чтения всего объекта. Используя concat, вы теряете все преимущества использования итераторов и загрузки всего файла в память, точно так же, как при использовании законов csv без указания размера фрагмента.

Note2 : если 'MemoryError' ошибка сохраняется, попробуйте меньший размер.

1 голос
/ 30 сентября 2019

Сложные улучшения:

  • для ленивого перебора файла (потенциально очень большого) вместо чтения всего файла в память - укажите вызов от chunksize до read_csv (указав числостроки для чтения за одну итерацию)
  • оператор df = df[:-1] не применим в подходе итератор и при условии, что последняя строка имеет неправильный формат 99695386 [space] NaN NaN NaN NaN - мы можем обработать ее и пропуститьуказав опцию error_bad_lines=False
  • , оператор df.loc[:,'timestamp'] = pd.to_datetime(df.loc[:,'timestamp']) также можно удалить, используя parse_dates=['timestamp'] в качестве опции для pd.read_csv call
  • , мы добавим к существующему целевому CSV-файлуприменение mode='a' (добавить в файл)

На практике:

n_rows = 10 * 6  # adjust empirically
reader = pd.read_csv('test.csv', names=["id_easy","ordinal", "timestamp", "latitude", "longitude"], 
                     parse_dates=['timestamp'], chunksize=n_rows, error_bad_lines=False)                               
day_names = ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')

for df in reader: 
    if not df.empty: 
        df['epoch'] = df.loc[:, 'timestamp'].astype('int64') // 1e9 
        df['day_of_week'] = pd.to_datetime(df['epoch'], unit="s").dt.weekday_name 
        del df['timestamp']
        for day in day_names: 
            day_df = df.loc[df['day_of_week'] == day] 
            if not day_df.empty:
                day_df.to_csv(f'{day}.csv', index=False, header=False, mode='a') 

https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#io-chunking

0 голосов
/ 29 сентября 2019

Вы можете использовать что-то вроде pypy (альтернативная реализация Python, которая не совместима со многими пакетами, но быстрее и имеет лучшее управление памятью). Он не использовался для поддержки панд (поэтому вам нужно было бы перебирать каждую строку, но pypy очень быстро это делает), но я считаю, что если вы используете версию с этого выпуска , то теперь это может бытьиспользуется с пандами.

...