Подтвердить что ты не робот

Как конкатенировать несколько pandas.DataFrames без использования в MemoryError

У меня есть три DataFrames, которые я пытаюсь объединить.

concat_df = pd.concat([df1, df2, df3])

В результате получается MemoryError. Как я могу это решить?

Обратите внимание, что большинство существующих похожих вопросов относятся к MemoryErrors, возникающим при чтении больших файлов. У меня нет этой проблемы. Я прочитал свои файлы в DataFrames. Я просто не могу объединить эти данные.

4b9b3361

Ответ 1

Я благодарен сообществу за их ответы. Однако, в моем случае, я узнал, что проблема на самом деле связана с тем, что я использовал 32-битный Python.

Есть ограничения памяти, определенные для Windows 32 и 64-разрядной ОС. Для 32-разрядного процесса он составляет всего 2 ГБ. Таким образом, даже если ваша оперативная память имеет более 2 ГБ, и даже если вы используете 64-разрядную ОС, но вы используете 32-битный процесс, то этот процесс будет ограничен только 2 ГБ ОЗУ - в моем случае этот процесс был Python.

Я обновил до 64-битного Python и не имел ошибки памяти с тех пор!

Другие актуальные вопросы: Ограничения 32-разрядной памяти Python на 64-битных окнах, Должен ли я использовать 32-разрядный Python или Python 64 бит, Почему этот размерный массив слишком большой для загрузки?

Ответ 2

Я советую вам поместить ваши данные в единый файл csv путем конкатенации. Затем прочитайте свой файл csv.

Выполните следующее:

# write df1 content in file.csv
df1.to_csv('file.csv', index=False)
# append df2 content to file.csv
df2.to_csv('file.csv', mode='a', columns=False, index=False)
# append df3 content to file.csv
df3.to_csv('file.csv', mode='a', columns=False, index=False)

# free memory
del df1, df2, df3

# read all df1, df2, df3 contents
df = pd.read_csv('file.csv')

Если это решение не enougth performante, чтобы сделать более крупные файлы, чем обычно. У:

df1.to_csv('file.csv', index=False)
df2.to_csv('file1.csv', index=False)
df3.to_csv('file2.csv', index=False)

del df1, df2, df3

Затем запустите команду bash:

cat file1.csv >> file.csv
cat file2.csv >> file.csv
cat file3.csv >> file.csv

Или файлы concat csv в python:

def concat(file1, file2):
    with open(file2, 'r') as filename2:
        data = file2.read()
    with open(file1, 'a') as filename1:
        file.write(data)

concat('file.csv', 'file1.csv')
concat('file.csv', 'file2.csv')
concat('file.csv', 'file3.csv')

После прочтения:

df = pd.read_csv('file.csv')

Ответ 3

Подобно тому, что предлагает @glegoux, также pd.DataFrame.to_csv может писать в режиме добавления, так что вы можете сделать что-то вроде:

df1.to_csv(filename)
df2.to_csv(filename, mode='a', columns=False)
df3.to_csv(filename, mode='a', columns=False)

del df1, df2, df3
df_concat = pd.read_csv(filename)

Ответ 4

Своего рода угадывание здесь, но возможно:

df1 = pd.concat([df1,df2])
del df2
df1 = pd.concat([df1,df3])
del df3

Очевидно, вы могли бы сделать это больше как цикл, но ключ - вы хотите удалить df2, df3 и т.д., когда идете. Поскольку вы делаете это в вопросе, вы никогда не очищаете старые данные, поэтому вы используете примерно вдвое больше памяти, чем вам нужно.

В общем, если вы читаете и объединяете, я бы сделал это примерно так (если у вас было 3 CSV: foo0, foo1, foo2):

concat_df = pd.DataFrame()
for i in range(3):
    temp_df = pd.read_csv('foo'+str(i)+'.csv')
    concat_df = pd.concat( [concat_df, temp_df] )

Другими словами, когда вы читаете файлы, вы временно сохраняете небольшие временные рамки памяти, пока не объедините их в объединенный df, concat_df. Как вы сейчас это делаете, вы сохраняете все меньшие данные, даже после их конкатенации.

Ответ 5

Dask может быть хорошим вариантом для обработки больших фреймов данных. Пройдите Dask Docs

Ответ 6

Вы можете сохранить отдельные данные в HDF Store, а затем вызвать хранилище точно так же, как один большой фрейм.

# name of store
fname = 'my_store'

with pd.get_store(fname) as store:

    # save individual dfs to store
    for df in [df1, df2, df3, df_foo]:
        store.append('df',df,data_columns=['FOO','BAR','ETC']) # data_columns = identify the column in the dfs you are appending

    # access the store as a single df
    df = store.select('df', where = ['A>2'])  # change where condition as required (see documentation for examples)
    # Do other stuff with df #

# close the store when you're done
os.remove(fname)

Ответ 7

Другая опция:

1) Запишите df1 в .csv файл: df1.to_csv('Big file.csv')

2) Откройте CSV файл, затем добавьте df2:

with open('Big File.csv','a') as f:
    df2.to_csv(f, header=False)

3) Повторите шаг 2 с помощью df3

with open('Big File.csv','a') as f:
    df3.to_csv(f, header=False)

Ответ 8

Проблема, как и в других ответах, проблема памяти. И решение заключается в хранении данных на диске, а затем для создания уникального фрейма данных.

С такими огромными данными производительность является проблемой.

Решения csv очень медленны, поскольку происходит преобразование в текстовом режиме. Решения HDF5 короче, элегантнее и быстрее, чем при использовании двоичного режима. Я предлагаю третий путь в двоичном режиме: pickle, который, кажется, еще быстрее, но более технический и нуждается в дополнительной комнате. И четвертый, от руки.

Здесь код:

import numpy as np
import pandas as pd

# a DataFrame factory:
dfs=[]
for i in range(10):
    dfs.append(pd.DataFrame(np.empty((10**5,4)),columns=range(4)))

# a csv solution
def bycsv(dfs):
    md,hd='w',True
    for df in dfs:
        df.to_csv('df_all.csv',mode=md,header=hd,index=None)
        md,hd='a',False
    #del dfs
    df_all=pd.read_csv('df_all.csv',index_col=None)
    os.remove('df_all.csv') 
    return df_all    

Лучшие решения:

def byHDF(dfs):
    store=pd.HDFStore('df_all.h5')
    for df in dfs:
        store.append('df',df,data_columns=list('0123'))
    #del dfs
    df=store.select('df')
    store.close()
    os.remove('df_all.h5')
    return df

def bypickle(dfs):
    c=[]
    with open('df_all.pkl','ab') as f:
        for df in dfs:
            pickle.dump(df,f)
            c.append(len(df))    
    #del dfs
    with open('df_all.pkl','rb') as f:
        df_all=pickle.load(f)
        offset=len(df_all)
        df_all=df_all.append(pd.DataFrame(np.empty(sum(c[1:])*4).reshape(-1,4)))

        for size in c[1:]:
            df=pickle.load(f)
            df_all.iloc[offset:offset+size]=df.values 
            offset+=size
    os.remove('df_all.pkl')
    return df_all

Для однородных кадров данных мы можем сделать еще лучше:

def byhand(dfs):
    mtot=0
    with open('df_all.bin','wb') as f:
        for df in dfs:
            m,n =df.shape
            mtot += m
            f.write(df.values.tobytes())
            typ=df.values.dtype                
    #del dfs
    with open('df_all.bin','rb') as f:
        buffer=f.read()
        data=np.frombuffer(buffer,dtype=typ).reshape(mtot,n)
        df_all=pd.DataFrame(data=data,columns=list(range(n))) 
    os.remove('df_all.bin')
    return df_all

И некоторые тесты на (мало, 32 Мб) данных для сравнения производительности. вам нужно умножить примерно на 128 для 4 Гб.

In [92]: %time w=bycsv(dfs)
Wall time: 8.06 s

In [93]: %time x=byHDF(dfs)
Wall time: 547 ms

In [94]: %time v=bypickle(dfs)
Wall time: 219 ms

In [95]: %time y=byhand(dfs)
Wall time: 109 ms

Проверка:

In [195]: (x.values==w.values).all()
Out[195]: True

In [196]: (x.values==v.values).all()
Out[196]: True

In [197]: (x.values==y.values).all()
Out[196]: True

Конечно, все это должно быть улучшено и настроено в соответствии с вашей проблемой.

В качестве примера df3 можно разделить на chancks размером 'total_memory_size - df_total_size', чтобы иметь возможность запускать bypickle.

Я могу отредактировать его, если вы дадите больше информации о своей структуре и размере данных, если хотите. Красивый вопрос!

Ответ 9

У меня были аналогичные проблемы с производительностью при попытке объединить большое количество DataFrames в "растущий" DataFrame.

Мое обходное решение заключалось в добавлении всех вспомогательных DataFrames в список и последующем объединении списка DataFrames после завершения обработки суб-DataFrames. Это приведет к почти полному времени выполнения.