Tengo dos marcos de datos df1 y df2, donde df2 es un subconjunto de df1. ¿Cómo obtengo un nuevo marco de datos (df3) que es la diferencia entre los dos marcos de datos?
En otras palabras, ¿un marco de datos que tiene todas las filas / columnas en df1 que no están en df2?
Respuestas:
Mediante el uso
drop_duplicates
pd.concat([df1,df2]).drop_duplicates(keep=False)
Update :
df1=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]}) df2=pd.DataFrame({'A':[1],'B':[2]})
Saldrá como a continuación, lo cual está mal
pd.concat([df1, df2]).drop_duplicates(keep=False) Out[655]: A B 1 2 3
Out[656]: A B 1 2 3 2 3 4 3 3 4
Método 1: usar
isin
contuple
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))] Out[657]: A B 1 2 3 2 3 4 3 3 4
Método 2:
merge
conindicator
df1.merge(df2,indicator = True, how='left').loc[lambda x : x['_merge']!='both'] Out[421]: A B _merge 1 2 3 left_only 2 3 4 left_only 3 3 4 left_only
fuente
pd.concat([df1,df2]).drop_duplicates(subset = ['col1','col2'], keep=False)
float
(porque12.00000000001 != 12
). Una mejor práctica es encontrar la intersección establecida de los ID en dos marcos de datos y obtener la diferencia basada en eso.indicator=True
) es una herramienta muy versátil y útil, me encantaría verlo en la parte superior de esta respuesta, pero con la combinación 'externa' y no 'izquierda' para cubrir las 3 situaciones.Para las filas, intente esto, dónde
Name
está la columna de índice conjunto (puede ser una lista para varias columnas comunes o especificarleft_on
yright_on
):m = df1.merge(df2, on='Name', how='outer', suffixes=['', '_'], indicator=True)
La
indicator=True
configuración es útil ya que agrega una columna llamada_merge
, con todos los cambios entredf1
ydf2
, categorizados en 3 tipos posibles: "left_only", "right_only" o "ambos".Para columnas, intente esto:
fuente
merge
withindicator=True
es la solución clásica para comparar marcos de datos por campos dados.Respuesta aceptada El método 1 no funcionará para marcos de datos con NaN dentro, como
pd.np.nan != pd.np.nan
. No estoy seguro de si esta es la mejor manera, pero puede evitarsedf1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]
fuente
edit2, descubrí una nueva solución sin la necesidad de configurar el índice
newdf=pd.concat[df1,df2].drop_duplicates(keep=False)
De acuerdo, encontré que la respuesta del voto más alto ya contiene lo que he descubierto. Sí, solo podemos usar este código a condición de que no haya duplicados en cada dos dfs.
Tengo un método complicado Primero establecemos 'Nombre' como el índice de dos marcos de datos dados por la pregunta. Como tenemos el mismo 'Nombre' en dos dfs, podemos simplemente eliminar el índice de df 'más pequeño' del df 'más grande' . Aquí está el código.
df1.set_index('Name',inplace=True) df2.set_index('Name',inplace=True) newdf=df1.drop(df2.index)
fuente
import pandas as pd # given df1 = pd.DataFrame({'Name':['John','Mike','Smith','Wale','Marry','Tom','Menda','Bolt','Yuswa',], 'Age':[23,45,12,34,27,44,28,39,40]}) df2 = pd.DataFrame({'Name':['John','Smith','Wale','Tom','Menda','Yuswa',], 'Age':[23,12,34,44,28,40]}) # find elements in df1 that are not in df2 df_1notin2 = df1[~(df1['Name'].isin(df2['Name']) & df1['Age'].isin(df2['Age']))].reset_index(drop=True) # output: print('df1\n', df1) print('df2\n', df2) print('df_1notin2\n', df_1notin2) # df1 # Age Name # 0 23 John # 1 45 Mike # 2 12 Smith # 3 34 Wale # 4 27 Marry # 5 44 Tom # 6 28 Menda # 7 39 Bolt # 8 40 Yuswa # df2 # Age Name # 0 23 John # 1 12 Smith # 2 34 Wale # 3 44 Tom # 4 28 Menda # 5 40 Yuswa # df_1notin2 # Age Name # 0 45 Mike # 1 27 Marry # 2 39 Bolt
fuente
Quizás una línea más simple, con nombres de columna idénticos o diferentes. Funcionó incluso cuando df2 ['Nombre2'] contenía valores duplicados.
newDf = df1.set_index('Name1') .drop(df2['Name2'], errors='ignore') .reset_index(drop=False)
fuente
Una ligera variación de la solución de nice @ liangli que no requiere cambiar el índice de los marcos de datos existentes:
newdf = df1.drop(df1.join(df2.set_index('Name').index))
fuente
Encontrar diferencias por índice. Suponiendo que df1 es un subconjunto de df2 y los índices se trasladan al subconjunto
df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna() # Example df1 = pd.DataFrame({"gender":np.random.choice(['m','f'],size=5), "subject":np.random.choice(["bio","phy","chem"],size=5)}, index = [1,2,3,4,5]) df2 = df1.loc[[1,3,5]] df1 gender subject 1 f bio 2 m chem 3 f phy 4 m bio 5 f bio df2 gender subject 1 f bio 3 f phy 5 f bio df3 = df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna() df3 gender subject 2 m chem 4 m bio
fuente
Además de la respuesta aceptada, me gustaría proponer una solución más amplia que pueda encontrar una diferencia de conjunto 2D de dos marcos de datos con cualquier
index
/columns
(es posible que no coincidan para ambos marcos de datos). También el método permite configurar la tolerancia para losfloat
elementos para la comparación de marcos de datos (utilizanp.isclose
)import numpy as np import pandas as pd def get_dataframe_setdiff2d(df_new: pd.DataFrame, df_old: pd.DataFrame, rtol=1e-03, atol=1e-05) -> pd.DataFrame: """Returns set difference of two pandas DataFrames""" union_index = np.union1d(df_new.index, df_old.index) union_columns = np.union1d(df_new.columns, df_old.columns) new = df_new.reindex(index=union_index, columns=union_columns) old = df_old.reindex(index=union_index, columns=union_columns) mask_diff = ~np.isclose(new, old, rtol, atol) df_bool = pd.DataFrame(mask_diff, union_index, union_columns) df_diff = pd.concat([new[df_bool].stack(), old[df_bool].stack()], axis=1) df_diff.columns = ["New", "Old"] return df_diff
Ejemplo:
In [1] df1 = pd.DataFrame({'A':[2,1,2],'C':[2,1,2]}) df2 = pd.DataFrame({'A':[1,1],'B':[1,1]}) print("df1:\n", df1, "\n") print("df2:\n", df2, "\n") diff = get_dataframe_setdiff2d(df1, df2) print("diff:\n", diff, "\n")
Out [1] df1: A C 0 2 2 1 1 1 2 2 2 df2: A B 0 1 1 1 1 1 diff: New Old 0 A 2.0 1.0 B NaN 1.0 C 2.0 NaN 1 B NaN 1.0 C 1.0 NaN 2 A 2.0 NaN C 2.0 NaN
fuente
Como se mencionó aquí que
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
es la solución correcta pero producirá una salida incorrecta si
df1=pd.DataFrame({'A':[1],'B':[2]}) df2=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]})
En ese caso, la solución anterior dará un marco de datos vacío , en su lugar, debe usar el
concat
método después de eliminar los duplicados de cada marco de datos.Utilizar
concate with drop_duplicates
df1=df1.drop_duplicates(keep="first") df2=df2.drop_duplicates(keep="first") pd.concat([df1,df2]).drop_duplicates(keep=False)
fuente