Pandas fusionando 101

  • ¿Cómo realizar una unión ( LEFT | RIGHT | FULL ) ( INNER | OUTER ) con pandas?
  • ¿Cómo agrego NaNs para las filas faltantes después de combinar?
  • ¿Cómo me deshago de los NaN después de la fusión?
  • ¿Puedo fusionarme en el índice?
  • Cruzar con los pandas?
  • ¿Cómo fusiono múltiples DataFrames?
  • merge join ? concat ? update ? ¿Quien? ¿Qué? ¡¿Por qué?!

… y más. He visto estas preguntas recurrentes sobre varias facetas de la funcionalidad de fusión de pandas. La mayor parte de la información sobre la fusión y sus diversos casos de uso hoy en día está fragmentada en docenas de publicaciones mal redactadas e inescrutables. El objective aquí es recostackr algunos de los puntos más importantes para la posteridad.

Esta QnA está destinada a ser la próxima entrega de una serie de guías de usuario útiles sobre modismos pandas comunes (consulte esta publicación sobre pivotaje y esta publicación sobre concatenación , que veré más adelante).

Tenga en cuenta que esta publicación no pretende ser un reemplazo de la documentación , ¡así que lea esto también! Algunos de los ejemplos son tomados de allí.

Esta publicación tiene como objective proporcionar a los lectores una introducción a la fusión con pandas con sabor a SQL, cómo usarla y cuándo no.

En particular, esto es lo que pasará con esta publicación:

  • Los conceptos básicos: tipos de combinaciones (IZQUIERDA, DERECHA, EXTERIOR, INTERIOR)

    • fusionarse con diferentes nombres de columna
    • evitando la columna de clave de combinación duplicada en la salida
  • Fusión con índice bajo diferentes condiciones.
    • efectivamente usando su índice nombrado
    • clave de fusión como el índice de uno y la columna de otro
  • Multiway se fusiona en columnas e índices (únicos y no únicos)
  • Alternativas notables para merge y join

Lo que este post no pasará:

  • Discusiones y horarios relacionados con el desempeño (por ahora). Principalmente menciones notables de mejores alternativas, donde sea apropiado.
  • Manejar sufijos, eliminar columnas adicionales, cambiar el nombre de las salidas y otros casos de uso específicos. Hay otros (leer: mejor) publicaciones que tratan con eso, ¡así que descúbralo!

Nota
La mayoría de los ejemplos predeterminan las operaciones de INNER JOIN mientras se muestran varias características, a menos que se especifique lo contrario.

Además, todos los DataFrames aquí se pueden copiar y replicar para que puedas jugar con ellos. Además, vea esta publicación sobre cómo leer DataFrames desde su portapapeles.

Finalmente, todas las representaciones visuales de las operaciones de JOIN se toman prestadas gracias al artículo https://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins .

Basta de hablar, solo muéstrame cómo usar merge !

Preparar

 np.random.seed(0) left = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': np.random.randn(4)}) right = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'value': np.random.randn(4)}) left key value 0 A 1.494079 1 B -0.205158 2 C 0.313068 3 D -0.854096 right key value 0 B -2.552990 1 D 0.653619 2 E 0.864436 3 F -0.742165 

En aras de la simplicidad, la columna clave tiene el mismo nombre (por ahora).

Un INNER JOIN está representado por

Nota
A aquí se refiere a las claves de la columna de unión en el DataFrame de la left , B refiere a las claves de la columna de unión en el DataFrame de la right , y la intersección representa las claves comunes tanto a la left como a la right . La región sombreada representa las claves que están presentes en el resultado de UNIR. Esta convención será seguida en todo momento. Tenga en cuenta que los diagtwigs de Venn no son una representación 100% precisa de las operaciones de UNIR, así que tómelos con una pizca de sal.

Para realizar un INNER JOIN, llame a pd.merge especificando el DataFrame izquierdo, el DataFrame derecho y la clave de combinación.

 pd.merge(left, right, on='key') key value_x value_y 0 B 0.400157 1.867558 1 D 2.240893 -0.977278 

Esto devuelve solo las filas de left y right que comparten una clave común (en este ejemplo, “B” y “D”).

En versiones más recientes de pandas (v0.21 o algo así), merge es ahora una función de primer orden, por lo que puede llamar a DataFrame.merge .

 left.merge(right, on='key') # Or, if you want to be explicit # left.merge(right, on='key', how='inner') key value_x value_y 0 B 0.400157 1.867558 1 D 2.240893 -0.977278 

Una UNIÓN EXTERNA IZQUIERDA , o UNA UNIÓN IZQUIERDA se representa por

Esto se puede realizar especificando how='left' .

 left.merge(right, on='key', how='left') key value_x value_y 0 A 1.764052 NaN 1 B 0.400157 1.867558 2 C 0.978738 NaN 3 D 2.240893 -0.977278 

Tenga en cuenta cuidadosamente la colocación de NaNs aquí. Si especifica how='left' , solo se usan las teclas de la left , y los datos que faltan de la right se reemplazan por NaN.

Y de manera similar, para una JUNTA EXTERNA DERECHA , o UNA JUNTA CORRECTA que es …

… especifique how='right' :

 left.merge(right, on='key', how='right') key value_x value_y 0 B 0.400157 1.867558 1 D 2.240893 -0.977278 2 E NaN 0.950088 3 F NaN -0.151357 

Aquí, se usan las teclas de la right y los datos que faltan de la left se reemplazan por NaN.

Finalmente, para la ÚLTIMA UNIÓN EXTERNA , dada por

especifique how='outer' .

 left.merge(right, on='key', how='outer') key value_x value_y 0 A 1.764052 NaN 1 B 0.400157 1.867558 2 C 0.978738 NaN 3 D 2.240893 -0.977278 4 E NaN 0.950088 5 F NaN -0.151357 

Esto utiliza las claves de ambos cuadros, y se insertan NaN para las filas que faltan en ambos.

La documentación resume estas diversas combinaciones muy bien:

introduzca la descripción de la imagen aquí

Otros JOINs – LEFT-Excluding, RIGHT-Excluding y FULL-Excluding / ANTI JOINs

Si necesita JOINs con exclusión IZQUIERDA y JOIN con DERECHA en dos pasos.

Para JOIN IZQUIERDO Excluido, representado como

Comience por realizar una JUNTA EXTERNA IZQUIERDA y luego filtre (¡excluyendo!) Las filas que vienen de la left solamente,

 (left.merge(right, on='key', how='left', indicator=True) .query('_merge == "left_only"') .drop('_merge', 1)) key value_x value_y 0 A 1.764052 NaN 2 C 0.978738 NaN 

Dónde,

 left.merge(right, on='key', how='left', indicator=True ) key value_x value_y _merge 0 A 1.764052 NaN left_only 1 B 0.400157 1.867558 both 2 C 0.978738 NaN left_only 3 D 2.240893 -0.977278 both 

Y de manera similar, para un JOIN que excluye el DERECHO,

 (left.merge(right, on='key', how='right', indicator=True ) .query('_merge == "right_only"') .drop('_merge', 1)) key value_x value_y 2 E NaN 0.950088 3 F NaN -0.151357 

Por último, si tiene que hacer una combinación que solo retenga las teclas de la izquierda o la derecha, pero no de ambas (IOW, realizando un ANTI-JOIN ),

Puedes hacer esto de manera similar:

 (left.merge(right, on='key', how='outer', indicator=True) .query('_merge != "both"') .drop('_merge', 1)) key value_x value_y 0 A 1.764052 NaN 2 C 0.978738 NaN 4 E NaN 0.950088 5 F NaN -0.151357 

Nombres diferentes para columnas clave

Si las columnas clave tienen nombres diferentes, por ejemplo, left tiene keyLeft y right tiene keyRight lugar de key entonces tendrá que especificar left_on y right_on como argumentos en lugar de on :

 left2 = left.rename({'key':'keyLeft'}, axis=1) right2 = right.rename({'key':'keyRight'}, axis=1) left2 keyLeft value 0 A 0.706573 1 B 0.010500 2 C 1.785870 3 D 0.126912 right2 keyRight value 0 B 0.401989 1 D 1.883151 2 E -1.347759 3 F -1.270485 

 left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner') keyLeft value_x keyRight value_y 0 B 0.010500 B 0.401989 1 D 0.126912 D 1.883151 

Evitando la columna de clave duplicada en la salida

Al fusionar en keyLeft desde la left y keyRight desde la right , si solo desea que keyLeft o keyRight (pero no ambas) en la salida, puede comenzar configurando el índice como un paso preliminar.

 left3 = left2.set_index('keyLeft') left3.merge(right2, left_index=True, right_on='keyRight') value_x keyRight value_y 0 0.010500 B 0.401989 1 0.126912 D 1.883151 

Contraste esto con la salida del comando justo antes (thst is, la salida de left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner') ), notará que falta keyLeft . Puede averiguar qué columna mantener en función de qué índice de cuadro se establece como clave. Esto puede importar cuando, por ejemplo, realizar alguna operación de OUTER JOIN.

Fusionando solo una columna de uno de los DataFrames

Por ejemplo, considere

 right3 = right.assign(newcol=np.arange(len(right))) right3 key value newcol 0 B 1.867558 0 1 D -0.977278 1 2 E 0.950088 2 3 F -0.151357 3 

Si se le pide que combine solo “new_val” (sin ninguna de las otras columnas), generalmente puede agrupar columnas antes de fusionar:

 left.merge(right3[['key', 'newcol']], on='key') key value newcol 0 B 0.400157 0 1 D 2.240893 1 

Si está realizando una IZQUIERDA EXTERNA IZQUIERDA, una solución más eficaz incluiría un map :

 # left['newcol'] = left['key'].map(right3.set_index('key')['newcol'])) left.assign(newcol=left['key'].map(right3.set_index('key')['newcol'])) key value newcol 0 A 1.764052 NaN 1 B 0.400157 0.0 2 C 0.978738 NaN 3 D 2.240893 1.0 

Como se mencionó, esto es similar, pero más rápido que

 left.merge(right3[['key', 'newcol']], on='key', how='left') key value newcol 0 A 1.764052 NaN 1 B 0.400157 0.0 2 C 0.978738 NaN 3 D 2.240893 1.0 

Fusión en múltiples columnas

Para unirse en más de una columna, especifique una lista para on (o left_on y right_on , según corresponda).

 left.merge(right, on=['key1', 'key2'] ...) 

O, en el caso de que los nombres sean diferentes,

 left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2']) 

Otras operaciones y funciones de merge* útiles

  • Fusionando un DataFrame con Series en el índice : vea esta respuesta .
  • Además de merge , DataFrame.update y DataFrame.combine_first también se usan en ciertos casos para actualizar un DataFrame con otro.

  • pd.merge_ordered es una función útil para las JOIN ordenadas.

  • pd.merge_asof (read: merge_asOf) es útil para uniones aproximadas .

Esta sección solo cubre lo más básico, y está diseñada para solo abrir el apetito. Para ver más ejemplos y casos, consulte la documentación sobre merge , join y concat , así como los enlaces a las especificaciones de la función.


Basado en índices * -JOIN (+ merge índice-columna s)

Preparar

 left = pd.DataFrame({'value': np.random.randn(4)}, index=['A', 'B', 'C', 'D']) right = pd.DataFrame({'value': np.random.randn(4)}, index=['B', 'D', 'E', 'F']) left.index.name = right.index.name = 'idxkey' left value idxkey A 2.269755 B -1.454366 C 0.045759 D -0.187184 right value idxkey B 1.532779 D 1.469359 E 0.154947 F 0.378163 

Normalmente, una combinación en el índice se vería así:

 left.merge(right, left_index=True, right_index=True) value_x value_y idxkey B 0.410599 0.761038 D 1.454274 0.121675 

Soporte para nombres de índice

Si se nombra su índice, entonces los usuarios de v0.23 también pueden especificar el nombre del nivel on (o left_on y right_on según sea necesario).

 left.merge(right, on='idxkey') value_x value_y idxkey B 0.410599 0.761038 D 1.454274 0.121675 

Fusión en el índice de una, columna (s) de otra

Es posible (y bastante simple) usar el índice de uno, y la columna de otro, para realizar una fusión. Por ejemplo,

 left.merge(right, left_on='key1', right_index=True) 

O viceversa ( right_on=... e left_index=True ).

 right2 = right.reset_index().rename({'idxkey' : 'colkey'}, axis=1) right2 colkey value 0 B 1.222445 1 D 0.208275 2 E 0.976639 3 F 0.356366 left.merge(right2, left_index=True, right_on='colkey') value_x colkey value_y 0 -1.070753 B 1.222445 1 -0.403177 D 0.208275 

En este caso especial, el índice para la left tiene un nombre, por lo que también puede usar el nombre del índice con left_on , así:

 left.merge(right2, left_on='idxkey', right_on='colkey') value_x colkey value_y 0 -1.070753 B 1.222445 1 -0.403177 D 0.208275 

DataFrame.join
Además de estos, hay otra opción sucinta. Puede usar DataFrame.join que por defecto se une en el índice. DataFrame.join realiza una IZQUIERDA DataFrame.join IZQUIERDA de forma predeterminada, por how='inner' aquí es necesario how='inner'

 left.join(right, how='inner', lsuffix='_x', rsuffix='_y') value_x value_y idxkey B 0.410599 0.761038 D 1.454274 0.121675 

Tenga en cuenta que necesitaba especificar los argumentos de lsuffix y rsuffix ya que de lo contrario, la join daría error:

 left.join(right) ValueError: columns overlap but no suffix specified: Index(['value'], dtype='object') 

Dado que los nombres de las columnas son los mismos. Esto no sería un problema si tuvieran un nombre diferente.

 left.rename(columns={'value':'leftvalue'}).join(right, how='inner') leftvalue value idxkey B -1.454366 1.532779 D -0.187184 1.469359 

pd.concat
Por último, como alternativa para uniones basadas en índices, puede usar pd.concat :

 pd.concat([left, right], axis=1, sort=False, join='inner') value value idxkey B -1.980796 1.230291 D 0.156349 1.202380 

Omita join='inner' si necesita un FULL OUTER JOIN (el valor predeterminado):

 pd.concat([left, right], axis=1, sort=False) value value A -0.887786 NaN B -1.980796 1.230291 C -0.347912 NaN D 0.156349 1.202380 E NaN -0.387327 F NaN -0.302303 

Para obtener más información, consulte esta publicación canónica en pd.concat por @piRSquared .


Generalizando: merge múltiples DataFrames

Preparar

 A = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'valueA': np.random.randn(4)}) B = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'valueB': np.random.randn(4)}) C = pd.DataFrame({'key': ['D', 'E', 'J', 'C'], 'valueC': np.ones(4)}) dfs = [A, B, C] 

A menudo, la situación surge cuando se deben fusionar varios DataFrames. De forma ingenua, esto se puede hacer encadenando llamadas de merge :

 A.merge(B, on='key').merge(C, on='key') key valueA valueB valueC 0 D 0.922207 -1.099401 1.0 

Sin embargo, esto se va rápidamente de las manos para muchos DataFrames. Además, puede ser necesario generalizar para un número desconocido de DataFrames. Para hacer esto, un truco simple que se usa con frecuencia es con functools.reduce , y puedes usarlo para lograr una UNIÓN INTERNA como la siguiente:

 from functools import reduce reduce(pd.merge, dfs) key valueA valueB valueC 0 D 0.465662 1.488252 1.0 

Tenga en cuenta que cada columna, además de la columna “clave”, debe tener un nombre diferente para que esto funcione fuera de la caja. De lo contrario, es posible que necesite utilizar un lambda .

Para un FULL OUTER JOIN, puedes curry pd.merge usando functools.partial :

 from functools import partial outer_merge = partial(pd.merge, how='outer') reduce(outer_merge, dfs) key valueA valueB valueC 0 A 0.056165 NaN NaN 1 B -1.165150 -1.536244 NaN 2 C 0.900826 NaN 1.0 3 D 0.465662 1.488252 1.0 4 E NaN 1.895889 1.0 5 F NaN 1.178780 NaN 6 J NaN NaN 1.0 

Como habrá notado, esto es bastante poderoso; también puede usarlo para controlar los nombres de columna durante la fusión. Simplemente agregue más argumentos de palabras clave según sea necesario:

 partial(pd.merge, how='outer', left_index=True, right_on=...) 

La alternativa: pd.concat
Si los valores de su columna son únicos, entonces tiene sentido usar pd.concat , esto es más rápido que una combinación de dos vías a la vez.

 pd.concat([ df.set_index('key') for df in dfs], axis=1, join='inner' ).reset_index() key valueA valueB valueC 0 D 0.465662 1.488252 1.0 

Multiway se fusionan en índices únicos

Si está fusionando múltiples DataFrames en índices únicos, una vez más debería preferir pd.concat para un mejor rendimiento.

 # Note, the "key" column values are unique, so the index is unique. A2 = A.set_index('key') B2 = B.set_index('key') C2 = C.set_index('key') dfs2 = [A2, B2, C2] 

 pd.concat(dfs2, axis=1, sort=False, join='inner') valueA valueB valueC key D 0.922207 -1.099401 1.0 

Como siempre, omita join='inner' para una JUNTA EXTERNA COMPLETA.

Fusión multiway en índices con duplicados

concat es rápido, pero tiene sus defectos. No puede manejar duplicados.

 A3 = pd.DataFrame({'key': ['A', 'B', 'C', 'D', 'D'], 'valueA': np.random.randn(5)}) 

 pd.concat([df.set_index('key') for df in [A3, B, C]], axis=1, join='inner') ValueError: Shape of passed values is (3, 4), indices imply (3, 2) 

En esta situación, join es la mejor opción, ya que puede manejar índices no únicos ( join llamadas merge bajo el capó).

 # For inner join. For left join, pass `pd.DataFrame.join` directly to `reduce`. inner_join = partial(pd.DataFrame.join, how='inner') reduce(inner_join, [A3.set_index('key'), B2, C2]) valueA valueB valueC key D -0.674333 -1.099401 1.0 D 0.031831 -1.099401 1.0