Pandas anti-unirse

Tengo dos tablas y me gustaría agregarlas para que solo se conserven todos los datos de la tabla A y los datos de la tabla B solo se agreguen si su clave es única (los valores clave son únicos en la tabla A y B, sin embargo, en algunos casos, a La clave se producirá tanto en la tabla A como en la B).

Creo que la forma de hacerlo implicará algún tipo de unión de filtrado (anti-join) para obtener los valores de la tabla B que no aparecen en la tabla A y luego agregar las dos tablas.

Estoy familiarizado con R y este es el código que usaría para hacer esto en R.

library("dplyr") ## Filtering join to remove values already in "TableA" from "TableB" FilteredTableB <- anti_join(TableB,TableA, by = "Key") ## Append "FilteredTableB" to "TableA" CombinedTable <- bind_rows(TableA,FilteredTableB) 

¿Cómo lograría esto en python?

Considere los siguientes marcos de datos

 TableA = pd.DataFrame(np.random.rand(4, 3), pd.Index(list('abcd'), name='Key'), ['A', 'B', 'C']).reset_index() TableB = pd.DataFrame(np.random.rand(4, 3), pd.Index(list('aecf'), name='Key'), ['A', 'B', 'C']).reset_index() 

 TableA 

introduzca la descripción de la imagen aquí


 TableB 

introduzca la descripción de la imagen aquí

Esta es una forma de hacer lo que quieras.

Método 1

 # Identify what values are in TableB and not in TableA key_diff = set(TableB.Key).difference(TableA.Key) where_diff = TableB.Key.isin(key_diff) # Slice TableB accordingly and append to TableA TableA.append(TableB[where_diff], ignore_index=True) 

introduzca la descripción de la imagen aquí

Método 2

 rows = [] for i, row in TableB.iterrows(): if row.Key not in TableA.Key.values: rows.append(row) pd.concat([TableA.T] + rows, axis=1).T 

Sincronización

4 filas con 2 superposiciones

Método 1 es mucho más rápido

introduzca la descripción de la imagen aquí

10,000 filas 5,000 se superponen

los bucles son malos

introduzca la descripción de la imagen aquí

Yo tuve el mismo problema. Esta respuesta utilizando how='outer' e indicator=True of merge me inspiró a encontrar esta solución:

 import pandas as pd import numpy as np TableA = pd.DataFrame(np.random.rand(4, 3), pd.Index(list('abcd'), name='Key'), ['A', 'B', 'C']).reset_index() TableB = pd.DataFrame(np.random.rand(4, 3), pd.Index(list('aecf'), name='Key'), ['A', 'B', 'C']).reset_index() print('TableA', TableA, sep='\n') print('TableB', TableB, sep='\n') TableB_only = pd.merge( TableA, TableB, how='outer', on='Key', indicator=True, suffixes=('_foo','')).query( '_merge == "right_only"') print('TableB_only', TableB_only, sep='\n') Table_concatenated = pd.concat((TableA, TableB_only), join='inner') print('Table_concatenated', Table_concatenated, sep='\n') 

Que imprime esta salida:

 TableA Key ABC 0 a 0.035548 0.344711 0.860918 1 b 0.640194 0.212250 0.277359 2 c 0.592234 0.113492 0.037444 3 d 0.112271 0.205245 0.227157 TableB Key ABC 0 a 0.754538 0.692902 0.537704 1 e 0.499092 0.864145 0.004559 2 c 0.082087 0.682573 0.421654 3 f 0.768914 0.281617 0.924693 TableB_only Key A_foo B_foo C_foo ABC _merge 4 e NaN NaN NaN 0.499092 0.864145 0.004559 right_only 5 f NaN NaN NaN 0.768914 0.281617 0.924693 right_only Table_concatenated Key ABC 0 a 0.035548 0.344711 0.860918 1 b 0.640194 0.212250 0.277359 2 c 0.592234 0.113492 0.037444 3 d 0.112271 0.205245 0.227157 4 e 0.499092 0.864145 0.004559 5 f 0.768914 0.281617 0.924693 

La respuesta más fácil imaginable:

 tableB = pd.concat([tableB, pd.Series(1)], axis=1) mergedTable = tableA.merge(tableB, how="left" on="key") answer = mergedTable[mergedTable.iloc[:,-1].isnull()][tableA.columns.tolist()] 

Debería ser el más rápido propuesto también.

Tendrá ambas tablas TableA y TableB modo que ambos objetos DataFrame tienen columnas con valores únicos en sus respectivas tablas, pero algunas columnas pueden tener valores que ocurren simultáneamente (tienen los mismos valores para una fila) en ambas tablas.

Luego, queremos combinar las filas en la TableA con las filas en la TableB que no coinciden con ninguna en la TableA para una columna ‘Clave’. El concepto es imaginarlo comparando dos series de longitud variable y combinando las filas en una serie sA con la otra sB si los valores de sB no coinciden con los de sA . El siguiente código resuelve este ejercicio:

 import pandas as pd TableA = pd.DataFrame([[2, 3, 4], [5, 6, 7], [8, 9, 10]]) TableB = pd.DataFrame([[1, 3, 4], [5, 7, 8], [9, 10, 0]]) removeTheseIndexes = [] keyColumnA = TableA.iloc[:,1] # your 'Key' column here keyColumnB = TableB.iloc[:,1] # same for i in range(0, len(keyColumnA)): firstValue = keyColumnA[i] for j in range(0, len(keyColumnB)): copycat = keyColumnB[j] if firstValue == copycat: removeTheseIndexes.append(j) TableB.drop(removeTheseIndexes, inplace = True) TableA = TableA.append(TableB) TableA = TableA.reset_index(drop=True) 

Tenga en cuenta que esto también afecta los datos de TableB . Puede usar inplace=False y reasignarlo a una newTable , luego TableA.append(newTable) alternativa.

 # Table A 0 1 2 0 2 3 4 1 5 6 7 2 8 9 10 # Table B 0 1 2 0 1 3 4 1 5 7 8 2 9 10 0 # Set 'Key' column = 1 # Run the script after the loop # Table A 0 1 2 0 2 3 4 1 5 6 7 2 8 9 10 3 5 7 8 4 9 10 0 # Table B 0 1 2 1 5 7 8 2 9 10 0 

Basado en una de las otras sugerencias, aquí hay una función que debería hacerlo. Usando solo funciones de pandas, sin bucles. También puede utilizar varias columnas como la clave. Si cambia la output = merged.loc[merged.dummy_col.isna(),tableA.columns.tolist()] línea output = merged.loc[merged.dummy_col.isna(),tableA.columns.tolist()] a output = merged.loc[~merged.dummy_col.isna(),tableA.columns.tolist()] tienes un semi_join.

 def anti_join(tableA,tableB,on): #if joining on index, make it into a column if tableB.index.name is not None: dummy = tableB.reset_index()[on] else: dummy = tableB[on] #create a dummy columns of 1s if isinstance(dummy, pd.Series): dummy = dummy.to_frame() dummy.loc[:,'dummy_col'] = 1 #preserve the index of tableA if it has one if tableA.index.name is not None: idx_name = tableA.index.name tableA = tableA.reset_index(drop = False) else: idx_name = None #do a left-join merged = tableA.merge(dummy,on=on,how='left') #keep only the non-matches output = merged.loc[merged.dummy_col.isna(),tableA.columns.tolist()] #reset the index (if applicable) if idx_name is not None: output = output.set_index(idx_name) return(output) 

indicator = True en el comando de merge le dirá qué unión se aplicó creando una nueva columna _merge con tres valores posibles:

  • left_only
  • right_only
  • both

right_only tomar right_only y right_only nuevamente a la primera tabla. Eso es.

Y no olvides soltar la columna _merge después de que la uses.

 outer_join = TableA.merge(TableB, how = 'outer', indicator = True) anti_join_B_only = outer_join[outer_join._merge == 'right_only'] anti_join_B_only = anti_join_B_only.drop('_merge', axis = 1) combined_table = TableA.merge(anti_join_B_only, how = 'outer') 

¡fácil!