pandas de três vias unindo vários quadros de dados em colunas

191

Eu tenho 3 arquivos CSV. Cada um tem a primeira coluna como o nome (string) das pessoas, enquanto todas as outras colunas em cada dataframe são atributos dessa pessoa.

Como posso "unir" todos os três documentos CSV para criar um único CSV com cada linha tendo todos os atributos para cada valor exclusivo do nome da string da pessoa?

A join()função no pandas especifica que eu preciso de um índice múltiplo, mas estou confuso sobre o que um esquema de indexação hierárquica tem a ver com a criação de uma junção com base em um único índice.

lollercoaster
fonte
2
Você não precisa de um multi-índice. Ele afirma nos documentos de junção que você não tem um índice múltiplo ao passar várias colunas para ingressar, então ele lidará com isso.
Cwharland
1
Nos meus ensaios, df1.join([df2, df3], on=[df2_col1, df3_col1])não funcionou.
Lollercoaster 15/05
Você precisa encadeá-los como na resposta dada. DF1 Mesclar e df2 seguida, mesclar o resultado com DF3
cwharland

Respostas:

472

Importações assumidas:

import pandas as pd

A resposta de John Galt é basicamente uma reduceoperação. Se eu tiver mais do que um punhado de quadros de dados, eu os colocaria em uma lista como esta (gerada através de compreensões ou loops de lista ou outros enfeites):

dfs = [df0, df1, df2, dfN]

Supondo que eles tenham alguma coluna em comum, como nameno seu exemplo, eu faria o seguinte:

df_final = reduce(lambda left,right: pd.merge(left,right,on='name'), dfs)

Dessa forma, seu código deve funcionar com qualquer número de quadros de dados que você deseja mesclar.

Editar 1 de agosto de 2016 : Para aqueles que usam Python 3: reducefoi movido para functools. Portanto, para usar esta função, primeiro você precisará importar esse módulo:

from functools import reduce
Kit
fonte
11
Eu apenas tentei usar isso e ele falhou porque reducefoi substituído functools.reduceEntãoimport functools functools.reduce(.......)
mattr
3
Como essa solução funcionará se os nomes dos campos a serem ingressados ​​forem diferentes? Por exemplo, em três quadros de dados eu poderia ter name1, name2e name3respectivamente.
Ps0604
2
Isso não significa que temos n-1chamadas para a função de mesclagem? Acho que nesse caso em que o número de quadros de dados é pequeno, não importa, mas me pergunto se há uma solução mais escalável.
Eapolinario 21/06
1
Isso não funcionou muito para meus dfs com vários índices da coluna (estava injetando o 'on' como uma coluna que funcionava para a primeira mesclagem, mas as fusões subsequentes falharam); em vez disso, consegui trabalhar com:df = reduce(lambda left, right: left.join(right, how='outer', on='Date'), dfs)
Adrian Torrie
+1 a ps0604. e se as colunas de junção forem diferentes, isso funciona? devemos usar o pd.merge, caso as colunas de junção sejam diferentes? obrigado
steve
106

Você pode tentar isso se tiver 3 quadros de dados

# Merge multiple dataframes
df1 = pd.DataFrame(np.array([
    ['a', 5, 9],
    ['b', 4, 61],
    ['c', 24, 9]]),
    columns=['name', 'attr11', 'attr12'])
df2 = pd.DataFrame(np.array([
    ['a', 5, 19],
    ['b', 14, 16],
    ['c', 4, 9]]),
    columns=['name', 'attr21', 'attr22'])
df3 = pd.DataFrame(np.array([
    ['a', 15, 49],
    ['b', 4, 36],
    ['c', 14, 9]]),
    columns=['name', 'attr31', 'attr32'])

pd.merge(pd.merge(df1,df2,on='name'),df3,on='name')

alternativamente, como mencionado por cwharland

df1.merge(df2,on='name').merge(df3,on='name')
Zero
fonte
34
Para parece mais limpo que você pode encadear-los df1.merge(df2,on='name').merge(df3,on='name')
cwharland
1
Como essa solução funcionará se os nomes dos campos a serem ingressados ​​forem diferentes? Por exemplo, em três quadros de dados que pode ter name1, name2e name3respectivamente
ps0604
4
@ ps0604df1.merge(df2,left_on='name1', right_on='name2').merge(df3,left_on='name1', right_on='name3').drop(columns=['name2', 'name3']).rename(columns={'name1':'name'})
Michael H.
e ainda mais, como fazer isso usando o índice. Parece não funcionar se 'name' for o índice e não um nome de coluna.
21719 Brian Brian D
85

Esta é uma situação ideal para o join método

o join método é criado exatamente para esses tipos de situações. Você pode associar qualquer número de DataFrames a ele. O DataFrame chamador se junta ao índice da coleção de DataFrames passados. Para trabalhar com vários DataFrames, você deve colocar as colunas de junção no índice.

O código ficaria assim:

filenames = ['fn1', 'fn2', 'fn3', 'fn4',....]
dfs = [pd.read_csv(filename, index_col=index_col) for filename in filenames)]
dfs[0].join(dfs[1:])

Com os dados de @ zero, você pode fazer o seguinte:

df1 = pd.DataFrame(np.array([
    ['a', 5, 9],
    ['b', 4, 61],
    ['c', 24, 9]]),
    columns=['name', 'attr11', 'attr12'])
df2 = pd.DataFrame(np.array([
    ['a', 5, 19],
    ['b', 14, 16],
    ['c', 4, 9]]),
    columns=['name', 'attr21', 'attr22'])
df3 = pd.DataFrame(np.array([
    ['a', 15, 49],
    ['b', 4, 36],
    ['c', 14, 9]]),
    columns=['name', 'attr31', 'attr32'])

dfs = [df1, df2, df3]
dfs = [df.set_index('name') for df in dfs]
dfs[0].join(dfs[1:])

     attr11 attr12 attr21 attr22 attr31 attr32
name                                          
a         5      9      5     19     15     49
b         4     61     14     16      4     36
c        24      9      4      9     14      9
Ted Petrou
fonte
4
Juntando todos os dfs a uma trama de dados vazia também funciona: pd.DataFrame().join(dfs, how="outer"). Isso pode ser mais limpo em algumas situações.
Dominik
4
Esse é um conselho decente e agora foi incorporado ao pandas que mescla 101 (consulte a seção sobre a mesclagem de vários quadros de dados). É importante notar que, se suas chaves se juntam são únicos, utilizando pd.concatresultará em sintaxe mais simples: pd.concat([df.set_index('name') for df in dfs], axis=1, join='inner').reset_index(). concattambém é mais versátil ao lidar com nomes de colunas duplicados em vários dfs ( joinnão é tão bom nisso), embora você possa executar uniões internas ou externas apenas com ela.
cs95
dfs[0].join(dfs[1:])deve ser editado para, dfs[0].join(dfs[1:], sort=False) caso contrário, um FutureWarningirá aparecer. Obrigado pelo bom exemplo.
gies0r 19/04
Eu recebo um erro ao tentar isso: ValueError: Indexes have overlapping valuesembora, pela inspeção dos quadros de dados individuais na lista, eles não pareçam ter valores sobrepostos.
SomJura
17

Isso também pode ser feito da seguinte maneira para obter uma lista de quadros de dados df_list:

df = df_list[0]
for df_ in df_list[1:]:
    df = df.merge(df_, on='join_col_name')

ou se os quadros de dados estiverem em um objeto gerador (por exemplo, para reduzir o consumo de memória):

df = next(df_list)
for df_ in df_list:
    df = df.merge(df_, on='join_col_name')
AlexG
fonte
11

No python3.6.3 com pandas0.22.0, você também pode usar concat, desde que você defina como índice, as colunas que deseja usar para a associação

pd.concat(
    (iDF.set_index('name') for iDF in [df1, df2, df3]),
    axis=1, join='inner'
).reset_index()

onde df1,, df2e df3são definidos como na resposta de John Galt

import pandas as pd
df1 = pd.DataFrame(np.array([
    ['a', 5, 9],
    ['b', 4, 61],
    ['c', 24, 9]]),
    columns=['name', 'attr11', 'attr12']
)
df2 = pd.DataFrame(np.array([
    ['a', 5, 19],
    ['b', 14, 16],
    ['c', 4, 9]]),
    columns=['name', 'attr21', 'attr22']
)
df3 = pd.DataFrame(np.array([
    ['a', 15, 49],
    ['b', 4, 36],
    ['c', 14, 9]]),
    columns=['name', 'attr31', 'attr32']
)
Igor Fobia
fonte
2
Essa deve ser a resposta aceita. É o mais rápido.
R. Zhu
4

Não é necessário um multi-índice para executar operações de junção . Basta definir corretamente a coluna do índice na qual executar as operações de junção (que comandam df.set_index('Name')por exemplo)

A joinoperação é executada por padrão no índice. No seu caso, você apenas precisa especificar que a Namecoluna corresponde ao seu índice. Abaixo está um exemplo

Um tutorial pode ser útil.

# Simple example where dataframes index are the name on which to perform the join operations
import pandas as pd
import numpy as np
name = ['Sophia' ,'Emma' ,'Isabella' ,'Olivia' ,'Ava' ,'Emily' ,'Abigail' ,'Mia']
df1 = pd.DataFrame(np.random.randn(8, 3), columns=['A','B','C'], index=name)
df2 = pd.DataFrame(np.random.randn(8, 1), columns=['D'],         index=name)
df3 = pd.DataFrame(np.random.randn(8, 2), columns=['E','F'],     index=name)
df = df1.join(df2)
df = df.join(df3)

# If you a 'Name' column that is not the index of your dataframe, one can set this column to be the index
# 1) Create a column 'Name' based on the previous index
df1['Name']=df1.index
# 1) Select the index from column 'Name'
df1=df1.set_index('Name')

# If indexes are different, one may have to play with parameter how
gf1 = pd.DataFrame(np.random.randn(8, 3), columns=['A','B','C'], index=range(8))
gf2 = pd.DataFrame(np.random.randn(8, 1), columns=['D'], index=range(2,10))
gf3 = pd.DataFrame(np.random.randn(8, 2), columns=['E','F'], index=range(4,12))

gf = gf1.join(gf2, how='outer')
gf = gf.join(gf3, how='outer')
Guillaume Jacquenot
fonte
4

Aqui está um método para mesclar um dicionário de quadros de dados enquanto mantém os nomes das colunas sincronizados com o dicionário. Também preenche os valores ausentes, se necessário:

Esta é a função para mesclar um ditado de quadros de dados

def MergeDfDict(dfDict, onCols, how='outer', naFill=None):
  keys = dfDict.keys()
  for i in range(len(keys)):
    key = keys[i]
    df0 = dfDict[key]
    cols = list(df0.columns)
    valueCols = list(filter(lambda x: x not in (onCols), cols))
    df0 = df0[onCols + valueCols]
    df0.columns = onCols + [(s + '_' + key) for s in valueCols] 

    if (i == 0):
      outDf = df0
    else:
      outDf = pd.merge(outDf, df0, how=how, on=onCols)   

  if (naFill != None):
    outDf = outDf.fillna(naFill)

  return(outDf)

OK, vamos gerar dados e testar isso:

def GenDf(size):
  df = pd.DataFrame({'categ1':np.random.choice(a=['a', 'b', 'c', 'd', 'e'], size=size, replace=True),
                      'categ2':np.random.choice(a=['A', 'B'], size=size, replace=True), 
                      'col1':np.random.uniform(low=0.0, high=100.0, size=size), 
                      'col2':np.random.uniform(low=0.0, high=100.0, size=size)
                      })
  df = df.sort_values(['categ2', 'categ1', 'col1', 'col2'])
  return(df)


size = 5
dfDict = {'US':GenDf(size), 'IN':GenDf(size), 'GER':GenDf(size)}   
MergeDfDict(dfDict=dfDict, onCols=['categ1', 'categ2'], how='outer', naFill=0)
rz1317
fonte
3

Solução Simples:

Se os nomes das colunas forem semelhantes:

 df1.merge(df2,on='col_name').merge(df3,on='col_name')

Se os nomes das colunas forem diferentes:

df1.merge(df2,left_on='col_name1', right_on='col_name2').merge(df3,left_on='col_name1', right_on='col_name3').drop(columns=['col_name2', 'col_name3']).rename(columns={'col_name1':'col_name'})
Gil Baggio
fonte
2

Existe outra solução na documentação do pandas (que não vejo aqui),

usando o .append

>>> df = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
   A  B
0  1  2
1  3  4
>>> df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
   A  B
0  5  6
1  7  8
>>> df.append(df2, ignore_index=True)
   A  B
0  1  2
1  3  4
2  5  6
3  7  8

o ignore_index=True é usado para ignorar o índice do quadro de dados anexado, substituindo-o pelo próximo índice disponível no de origem.

Se houver nomes de colunas diferentes, Nanserá apresentado.

Sylhare
fonte
é semântico para alguém que usa a palavra "ingressar" para dizer reunir os dois quadros de dados. (não necessarely como o SQL operação de junção)
Sylhare
1

Os três quadros de dados são

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Vamos mesclar esses quadros usando o pd.merge aninhado

insira a descrição da imagem aqui

Aqui vamos nós, temos nosso dataframe mesclado.

Feliz análise !!!

decision_scientist_noah
fonte