los pandas obtienen filas que NO están en otro marco de datos
Tengo dos marcos de datos de pandas que tienen algunas filas en común.
Supongamos que el marco de datos2 es un subconjunto del marco de datos1.
¿Cómo puedo obtener las filas del marco de datos1 que no están en el marco de datos2?
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]})
df1
col1 col2
0 1 10
1 2 11
2 3 12
3 4 13
4 5 14
df2
col1 col2
0 1 10
1 2 11
2 3 12
Resultado Esperado:
col1 col2
3 4 13
4 5 14
La solución actualmente seleccionada produce resultados incorrectos. Para resolver correctamente este problema, podemos realizar una unión izquierda desde df1
hasta df2
, asegurándonos de obtener primero solo 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 izquierda, eliminando duplicados df2
para que cada fila de df1
uniones con exactamente 1 fila de df2
. Utilice el parámetro indicator
para devolver una columna adicional que indique de qué tabla proviene 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
Crea 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 son incorrectas
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, 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)
Un método sería almacenar el resultado de una combinación interna de ambos dfs, luego podemos simplemente seleccionar las filas cuando los valores de una columna no estén en este común:
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 utilizar isin
el cual producirá NaN
filas 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 las filas de la misma manera, esto no funcionará:
df2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11, 12,13]})
producirá el df completo:
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