Эффективный способ объединения нескольких больших DataFrames

Предположим, у меня есть 4 небольших DataFrames

df1, df2, df3Иdf4

import pandas as pd
 из импорта functools уменьшает
 импорт numpy как np

df1 = pd . DataFrame ([[ 'a' , 1 , 10 ], [ 'a' , 2 , 20 ], [ 'b' , 1 , 4 ], [ 'c' , 1 , 2 ], [ 'e' , 2 , 10 ]]) 
df2 = pd . DataFrame ([[ 'a' , 1 , 15 ], [ 'a' , 2 ,                   20 ], [ 'c' , 1 , 2 ]]) 
df3 = pd . DataFrame ([[ 'd' , 1 , 10 ], [ 'e' , 2 , 20 ], [ 'f' , 1 , 1 ]])   
df4 = pd . DataFrame ([[ 'd' , 1 , 10 ], [ 'e' , 2 , 20 ], [ 'f' , 1 , 15 ]])                      


df1 . columns = [ 'name' , 'id' , 'price' ] 
df2 . columns = [ 'name' , 'id' , 'price' ] 
df3 . columns = [ 'name' , 'id' , 'price' ]     
df4 . columns = [ 'name' , 'id' , 'price' ]               

df1 = df1 . rename ( columns = { 'price' : 'pricepart1' }) 
df2 = df2 . rename ( columns = { 'price' : 'pricepart2' }) 
df3 = df3 . rename ( columns = { 'price' : 'pricepart3' }) 
df4 = df4 . rename ( columns = { 'price' : 'pricepart4' }) prettyprinted "># Merge dataframes
df = pd.merge(df1, df2, left_on=['name', 'id'], right_on=['name', 'id'], how='outer')
df = pd.merge(df , df3, left_on=['name', 'id'], right_on=['name', 'id'], how='outer')
df = pd.merge(df , df4, left_on=['name', 'id'], right_on=['name', 'id'], how='outer')

# Fill na values with 'missing'
df = df.fillna('missing')

Создание выше - это 4 DataFrames, я бы хотел, чтобы это было в коде ниже.

from functools import reduce
import pandas as pd
import numpy as np
dfList = []

#To create the 48 DataFrames of size 62245 X 3
for i in range(0, 49):

    dfList.append(pd.DataFrame(np.random.randint(0,100,size=(62245, 3)), columns=['name',  'id',  'pricepart' + str(i + 1)]))


#The solution I came up with to extend the solution to more than 3 DataFrames
df_merged = reduce(lambda  left, right: pd.merge(left, right, left_on=['name', 'id'], right_on=['name', 'id'], how='outer'), dfList).fillna('missing')

Таким образом, я достиг этих DataFrames MemoryError 4, у которых не так много строк и столбцов.

В принципе, я хочу расширить вышеупомянутое внешнее решение MemoryError до MULTIPLE (48) DataFrames размером 62245 X 3:

Поэтому я придумал это решение, построив из другого ответа StackOverflow, который использовал сокращение лямбда:

int64

Это вызывает a int64.

Я не знаю, что делать, чтобы остановить ядро ??от смерти. Я застрял на этом в течение двух дней. Некоторый код для операции EXACT merge, который я выполнил, не вызывает того float64или чего-то, что дает вам то же самое результат, был бы действительно оценен.

Кроме того , 3 колонки в главном DataFrame (не воспроизводимые 48 DataFrames в примере) имеют тип float64, float16и MemoryErrorя предпочел бы им оставаться таким образом из-за целого числа и intermediatedfList = dfList tempdfList = [] #Until я сливаться все 48 кадров два за раз, пока он не станет размером 2 в то время как ( len ( intermediatedfList ) ! = 2 ): # Если есть четное число DataFrames, если len ( intermediatedfList )% 2 == 0 : #Go с шагом в два для i в диапазоне ( 0 , len ( intermediatedfList ), 2 ): #Merge DataFrame в индексе i, i + 1 df1 = pd . merge ( intermediatedfList [ i ], intermediatedfList [ i + 1 ], left_on = [ 'name' , 'id' ], right_on = [ 'name' , 'id' ], how = 'outer' ) print ( df1 . info ( memory_usage = 'deep' )) # Подключить его к этому списку tempdfList . append ( df1 ) #After DataFrames в промежуточном fList, объединяющем его по два за один раз с использованием вспомогательного списка tempdfList, #Set intermediatedfList, равный tempdfList, поэтому он может продолжить цикл while. intermedfList = tempdfList else : # Если есть нечетное число DataFrames, сохраните первый DataFrame out tempdfList = [ intermediatedfList [ 0 ]] #Go с шагом в два, начиная с 1 вместо 0 для i в диапазоне ( 1 , len ( промежуточное fList ) , 2 ): #Merge DataFrame в индексе i, i + 1 df1 = pd . merge ( intermediatedfList [ i ], intermediatedfList [ i + 1 ], left_on = [ 'name' , 'id' ], right_on = [ 'name' , 'id' ], how = 'outer' ) print ( df1 . info ( memory_usage = 'deep' )) tempdfList . append ( df1 ) #After DataFrames в промежуточном fList, объединяющем его по два за один раз с использованием вспомогательного списка tempdfList, #Set intermediatedfList, равный tempdfList, поэтому он может продолжить цикл while. intermediatedfList = tempdfList, который он представляет.

РЕДАКТИРОВАТЬ:

Вместо того, чтобы итеративно пытаться запускать операции слияния или использовать уменьшающие лямбда-функции, я сделал это в группах по 2! Кроме того, я изменил тип данных для некоторых столбцов, некоторые из них не обязательно MemoryError. Поэтому я его спустил MemoryError. Он становится очень далеко, но все еще заканчивается тем, что он бросает pd.concat.

df_list = [df1, df2, ...]
for df in df_list:
    df.set_index(['name', 'id'], inplace=True)

df = pd.concat(df_list, axis=1) # join='inner'
df.reset_index(inplace=True)

Есть ли способ оптимизировать мой код, чтобы избежать этого concat, я даже использовал RAM AWS 192GB (теперь я должен им 7 $, который я мог бы дать одному из yall), который становится дальше, чем то, что я получил, и это все еще бросает joinпосле сокращения списка из 28 DataFrames до 4.

python,pandas,dataframe,merge,out-of-memory,

3

Ответов: 4


2 принят
+50

Вы можете получить некоторую выгоду от выполнения индекс выровнен с functools импорта уменьшить DF = уменьшить ( лямбда х , у : х . Присоединиться ( у ), df_list ) enation использованием merge. Это, надеюсь, будет быстрее и эффективнее памяти, чем внешнее слияние.

for

В качестве альтернативы вы можете заменить concat(второй шаг) на итеративный int:

pd.to_numeric

Это может быть или не быть лучше, чем merge.


1

Вы можете попробовать простой forцикл. Единственная оптимизация памяти, которую я применил, - это переход вниз к наиболее оптимальному intтипу через .import pandas as pd dfs = {} dfs[1] = pd.DataFrame([['a', 1, 10], ['a', 2, 20], ['b', 1, 4], ['c', 1, 2], ['e', 2, 10]]) dfs[2] = pd.DataFrame([['a', 1, 15], ['a', 2, 20], ['c', 1, 2]]) dfs[3] = pd.DataFrame([['d', 1, 10], ['e', 2, 20], ['f', 1, 1]]) dfs[4] = pd.DataFrame([['d', 1, 10], ['e', 2, 20], ['f', 1, 15]]) df = dfs[1].copy() for i in range(2, max(dfs)+1): df = pd.merge(df, dfs[i].rename(columns={2: i+1}), left_on=[0, 1], right_on=[0, 1], how='outer').fillna(-1) df.iloc[:, 2:] = df.iloc[:, 2:].apply(pd.to_numeric, downcast='integer') print(df) 0 1 2 3 4 5 0 a 1 10 15 -1 -1 1 a 2 20 20 -1 -1 2 b 1 4 -1 -1 -1 3 c 1 2 2 -1 -1 4 e 2 10 -1 20 20 5 d 1 -1 -1 10 10 6 f 1 -1 -1 1 15

Я также использую словарь для хранения данных. Это хорошая практика для хранения переменного числа переменных.

object

Вы не должны, как правило, комбинировать строки, такие как «отсутствует» с числовыми типами, поскольку это превратит вашу целую серию в -1серии типов. Здесь мы используем NaN, но вы можете использовать floatс floatDTYPE вместо этого.


0

Похоже, часть того, что предназначалось для данных dask, предназначалось для выполнения (из памяти операционных систем с файловыми кадрами). См. Лучший способ объединения двух больших наборов данных в Pandas, например, кода. Извините, не копирование и вставка, но не хочу, чтобы я выглядел так, как будто я пытаюсь взять кредит у ответчика в связанной записи.


Итак, у вас есть 48 dfs с тремя столбцами каждый - имя, id и другой столбец для каждого df.

Вы не должны использовать слияние ....

Вместо этого, если вы конкатцируете все dfs

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

Вы получите:

Out[3]: 
   id name  pricepart1  pricepart2  pricepart3  pricepart4
0   1    a        10.0         NaN         NaN         NaN
1   2    a        20.0         NaN         NaN         NaN
2   1    b         4.0         NaN         NaN         NaN
3   1    c         2.0         NaN         NaN         NaN
4   2    e        10.0         NaN         NaN         NaN
0   1    a         NaN        15.0         NaN         NaN
1   2    a         NaN        20.0         NaN         NaN
2   1    c         NaN         2.0         NaN         NaN
0   1    d         NaN         NaN        10.0         NaN
1   2    e         NaN         NaN        20.0         NaN
2   1    f         NaN         NaN         1.0         NaN
0   1    d         NaN         NaN         NaN        10.0
1   2    e         NaN         NaN         NaN        20.0
2   1    f         NaN         NaN         NaN        15.0

Теперь вы можете группировать по имени и id и брать сумму:

df.groupby(['name','id']).sum().fillna('missing').reset_index()

Если вы попробуете его с 48 dfs, вы увидите, что он решает MemoryError:

dfList = []
#To create the 48 DataFrames of size 62245 X 3
for i in range(0, 49):
    dfList.append(pd.DataFrame(np.random.randint(0,100,size=(62245, 3)), columns=['name',  'id',  'pricepart' + str(i + 1)]))

df = pd.concat(dfList)
df.groupby(['name','id']).sum().fillna('missing').reset_index()
питон, панды, dataframe, слияние, из-за нехватки памяти,