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
TableB
Esta es una forma de hacer lo que quieras.
# 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)
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
4 filas con 2 superposiciones
Método 1 es mucho más rápido
10,000 filas 5,000 se superponen
los bucles son malos
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!