pandas obtienen filas que NO están en otro dataframe

Tengo dos marcos de datos pandas que tienen algunas filas en común.

Supongamos que dataframe2 es un subconjunto de dataframe1.

¿Cómo puedo obtener las filas de dataframe1 que no están en dataframe2?

df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 'col2' : [10, 11, 12, 13, 14]}) df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 'col2' : [10, 11, 12]}) 

Un método sería almacenar el resultado de una combinación interna de ambos dfs, luego, simplemente podemos seleccionar las filas cuando los valores de una columna no son comunes:

 In [119]: common = df1.merge(df2,on=['col1','col2']) print(common) df1[(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))] col1 col2 0 1 10 1 2 11 2 3 12 Out[119]: col1 col2 3 4 13 4 5 14 

EDITAR

Otro método que ha encontrado es usar isin que producirá filas de NaN que puede eliminar:

 In [138]: df1[~df1.isin(df2)].dropna() Out[138]: col1 col2 3 4 13 4 5 14 

Sin embargo, si df2 no ​​inicia filas de la misma manera, esto no funcionará:

 df2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11, 12,13]}) 

producirá todo el df:

 In [140]: df1[~df1.isin(df2)].dropna() Out[140]: col1 col2 0 1 10 1 2 11 2 3 12 3 4 13 4 5 14 

La solución seleccionada actualmente produce resultados incorrectos. Para resolver correctamente este problema, podemos realizar una unión a la izquierda desde df1 a df2 , asegurándonos de obtener primero las filas únicas para df2 .

Primero, necesitamos modificar el DataFrame original para agregar la fila con datos [3, 10].

 df1 = pd.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 'col2' : [10, 11, 12, 13, 14, 10]}) df2 = pd.DataFrame(data = {'col1' : [1, 2, 3], 'col2' : [10, 11, 12]}) df1 col1 col2 0 1 10 1 2 11 2 3 12 3 4 13 4 5 14 5 3 10 df2 col1 col2 0 1 10 1 2 11 2 3 12 

Realice una unión a la izquierda, eliminando los duplicados en df2 para que cada fila de df1 una con exactamente 1 fila de df2 . Use el indicator parámetro para devolver una columna adicional que indique de qué tabla era la fila.

 df_all = df1.merge(df2.drop_duplicates(), on=['col1','col2'], how='left', indicator=True) df_all col1 col2 _merge 0 1 10 both 1 2 11 both 2 3 12 both 3 4 13 left_only 4 5 14 left_only 5 3 10 left_only 

Crear una condición booleana:

 df_all['_merge'] == 'left_only' 0 False 1 False 2 False 3 True 4 True 5 True Name: _merge, dtype: bool 

¿Por qué otras soluciones están equivocadas?

Algunas soluciones cometen el mismo error: solo verifican que cada valor esté independientemente en cada columna, no juntos en la misma fila. Agregar la última fila, que es única pero tiene los valores de ambas columnas de df2 expone el error:

 common = df1.merge(df2,on=['col1','col2']) (~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2)) 0 False 1 False 2 False 3 True 4 True 5 False dtype: bool 

Esta solución obtiene el mismo resultado incorrecto:

 df1.isin(df2.to_dict('l')).all(1) 

Suponiendo que los índices son coherentes en los marcos de datos (sin tener en cuenta los valores de columna reales):

 df1[~df1.index.isin(df2.index)] 

Como ya se indicó, isin requiere que las columnas y los índices sean iguales para una coincidencia. Si la coincidencia solo debe estar en el contenido de la fila, una forma de obtener la máscara para filtrar las filas presentes es convertir las filas en un índice (múltiple):

 In [77]: df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 'col2' : [10, 11, 12, 13, 14, 10]}) In [78]: df2 = pandas.DataFrame(data = {'col1' : [1, 3, 4], 'col2' : [10, 12, 13]}) In [79]: df1.loc[~df1.set_index(list(df1.columns)).index.isin(df2.set_index(list(df2.columns)).index)] Out[79]: col1 col2 1 2 11 4 5 14 5 3 10 

Si se debe tener en cuenta el índice, set_index tiene un argumento de palabra clave adjunto para agregar columnas al índice existente. Si las columnas no se alinean, la lista (df.columns) puede reemplazarse con especificaciones de columna para alinear los datos.

 pandas.MultiIndex.from_tuples(df.to_records(index = False).tolist()) 

podría usarse alternativamente para crear los índices, aunque dudo que esto sea más eficiente.

Supongamos que tiene dos marcos de datos, df_1 y df_2 que tienen varios campos (nombres de columna) y desea buscar las únicas entradas en df_1 que no están en df_2 sobre la base de algunos campos (por ejemplo, fields_x, fields_y), siga los siguientes pasos.

Paso1.Agregue una columna key1 y key2 a df_1 y df_2 respectivamente.

Step2.Merge los marcos de datos como se muestra a continuación. field_x y field_y son nuestras columnas deseadas.

Paso 3.Seleccione solo las filas de df_1 donde key1 no es igual a key2.

Step4.Drop key1 y key2.

Este método resolverá su problema y funciona rápido incluso con grandes conjuntos de datos. Lo he intentado para marcos de datos con más de 1,000,000 de filas.

 df_1['key1'] = 1 df_2['key2'] = 1 df_1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'left') df_1 = df_1[~(df_1.key2 == df_1.key1)] df_1 = df_1.drop(['key1','key2'], axis=1) 

un poco tarde, pero podría valer la pena revisar el parámetro “indicador” de pd.merge.

Vea esta otra pregunta para ver un ejemplo: Compare PandaS DataFrames y devuelva las filas que faltan en la primera

También puedes concat df1 , df2 :

 x = pd.concat([df1, df2]) 

y luego eliminar todos los duplicados:

 y = x.drop_duplicates(keep=False, inplace=False) 

Puedes hacerlo usando el método isin (dict) :

 In [74]: df1[~df1.isin(df2.to_dict('l')).all(1)] Out[74]: col1 col2 3 4 13 4 5 14 

Explicación:

 In [75]: df2.to_dict('l') Out[75]: {'col1': [1, 2, 3], 'col2': [10, 11, 12]} In [76]: df1.isin(df2.to_dict('l')) Out[76]: col1 col2 0 True True 1 True True 2 True True 3 False False 4 False False In [77]: df1.isin(df2.to_dict('l')).all(1) Out[77]: 0 True 1 True 2 True 3 False 4 False dtype: bool 

Qué tal esto:

 df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 'col2' : [10, 11, 12, 13, 14]}) df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 'col2' : [10, 11, 12]}) records_df2 = set([tuple(row) for row in df2.values]) in_df2_mask = np.array([tuple(row) in records_df2 for row in df1.values]) result = df1[~in_df2_mask] 

Aquí hay otra forma de resolver esto:

 df1[~df1.index.isin(df1.merge(df2, how='inner', on=['col1', 'col2']).index)] 

O:

 df1.loc[df1.index.difference(df1.merge(df2, how='inner', on=['col1', 'col2']).index)] 

Mi forma de hacerlo consiste en agregar una nueva columna que sea única para un dataframe y usarla para elegir si desea mantener una entrada

 df2[col3] = 1 df1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'outer') df1['Empt'].fillna(0, inplace=True) 

Esto hace que cada entrada en df1 tenga un código – 0 si es exclusivo de df1, 1 si está en ambos marcos de datos. A continuación, utiliza esto para restringir a lo que quieres

 answer = nonuni[nonuni['Empt'] == 0]