juntar - unir dataframes en python
Comparando dos dataframes y obteniendo las diferencias (7)
Tengo dos marcos de datos. Ejemplos:
df1:
Date Fruit Num Color
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 8.6 Orange
2013-11-24 Apple 7.6 Green
2013-11-24 Celery 10.2 Green
df2:
Date Fruit Num Color
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 8.6 Orange
2013-11-24 Apple 7.6 Green
2013-11-24 Celery 10.2 Green
2013-11-25 Apple 22.1 Red
2013-11-25 Orange 8.6 Orange
Cada marco de datos tiene la fecha como índice. Ambos marcos de datos tienen la misma estructura.
Lo que quiero hacer es comparar estos dos marcos de datos y encontrar qué filas están en df2 que no están en df1. Quiero comparar la fecha (índice) y la primera columna (Banana, APple, etc.) para ver si existen en df2 vs df1.
He probado lo siguiente:
- Salida de diferencia en dos marcos de datos de Pandas uno al lado del otro - destacando la diferencia
- Comparando dos dataframes de pandas para las diferencias
Para el primer enfoque, recibo este error: "Excepción: solo se pueden comparar objetos DataFrame identificados de forma idéntica" . Intenté eliminar la fecha como índice pero obtuve el mismo error.
En el tercer enfoque , obtengo la afirmación de devolver False, pero no puedo entender cómo ver realmente las diferentes filas.
Cualquier puntero sería bienvenido
Al pasar los dataframes a concat en un diccionario, se obtiene un marco de datos de múltiples índices desde el que puede eliminar fácilmente los duplicados, lo que da como resultado un marco de datos de múltiples índices con las diferencias entre los marcos de datos:
import sys
if sys.version_info[0] < 3:
from StringIO import StringIO
else:
from io import StringIO
import pandas as pd
DF1 = StringIO("""Date Fruit Num Color
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 8.6 Orange
2013-11-24 Apple 7.6 Green
2013-11-24 Celery 10.2 Green
""")
DF2 = StringIO("""Date Fruit Num Color
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 8.6 Orange
2013-11-24 Apple 7.6 Green
2013-11-24 Celery 10.2 Green
2013-11-25 Apple 22.1 Red
2013-11-25 Orange 8.6 Orange""")
df1 = pd.read_table(DF1, sep=''/s+'')
df2 = pd.read_table(DF2, sep=''/s+'')
#%%
dfs_dictionary = {''DF1'':df1,''DF2'':df2}
df=pd.concat(dfs_dictionary)
df.drop_duplicates(keep=False)
Resultado:
Date Fruit Num Color
2 4 2013-11-25 Apple 22.1 Red
5 2013-11-25 Orange 8.6 Orange
Basándome en la respuesta de Alko que casi me funcionó, excepto en el paso de filtrado (donde obtengo: ValueError: cannot reindex from a duplicate axis
), aquí está la solución final que utilicé:
# join the dataframes
united_data = pd.concat([data1, data2, data3, ...])
# group the data by the whole row to find duplicates
united_data_grouped = united_data.groupby(list(united_data.columns))
# detect the row indices of unique rows
uniq_data_idx = [x[0] for x in united_data_grouped.indices.values() if len(x) == 1]
# extract those unique values
uniq_data = united_data.iloc[uniq_data_idx]
Este enfoque, df1 != df2
, solo funciona para dataframes con filas y columnas idénticas. De hecho, todos los ejes de marcos de datos se comparan con el método _indexed_same
, y se _indexed_same
excepción si se encuentran diferencias, incluso en el orden de columnas / índices.
Si te entendí bien, no quieres encontrar cambios, sino una diferencia simétrica. Para eso, un enfoque podría ser concatenar marcos de datos:
>>> df = pd.concat([df1, df2])
>>> df = df.reset_index(drop=True)
agrupar por
>>> df_gpby = df.groupby(list(df.columns))
obtener índice de registros únicos
>>> idx = [x[0] for x in df_gpby.groups.values() if len(x) == 1]
filtrar
>>> df.reindex(idx)
Date Fruit Num Color
9 2013-11-25 Orange 8.6 Orange
8 2013-11-25 Apple 22.1 Red
Hay una solución más simple que es más rápida y mejor, y si los números son diferentes, incluso puedes darte diferencias de cantidades:
df1_i = df1.set_index([''Date'',''Fruit'',''Color''])
df2_i = df2.set_index([''Date'',''Fruit'',''Color''])
df_diff = df1_i.join(df2_i,how=''outer'',rsuffix=''_'').fillna(0)
df_diff = (df_diff[''Num''] - df_diff[''Num_''])
Aquí df_diff es una sinopsis de las diferencias. Incluso puede usarlo para encontrar las diferencias en cantidades. En tu ejemplo:
Explicación: De manera similar a comparar dos listas, para hacerlo de manera eficiente primero debemos ordenarlas y luego compararlas (la conversión de la lista a conjuntos / hash también sería rápida, ambas son una mejora increíble para el bucle de comparación doble simple O (N ^ 2)
Nota: el siguiente código produce las tablas:
df1=pd.DataFrame({
''Date'':[''2013-11-24'',''2013-11-24'',''2013-11-24'',''2013-11-24''],
''Fruit'':[''Banana'',''Orange'',''Apple'',''Celery''],
''Num'':[22.1,8.6,7.6,10.2],
''Color'':[''Yellow'',''Orange'',''Green'',''Green''],
})
df2=pd.DataFrame({
''Date'':[''2013-11-24'',''2013-11-24'',''2013-11-24'',''2013-11-24'',''2013-11-25'',''2013-11-25''],
''Fruit'':[''Banana'',''Orange'',''Apple'',''Celery'',''Apple'',''Orange''],
''Num'':[22.1,8.6,7.6,10.2,22.1,8.6],
''Color'':[''Yellow'',''Orange'',''Green'',''Green'',''Red'',''Orange''],
})
Tengo esta solución. ¿Esto te ayuda?
text = """df1:
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 8.6 Orange
2013-11-24 Apple 7.6 Green
2013-11-24 Celery 10.2 Green
df2:
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 8.6 Orange
2013-11-24 Apple 7.6 Green
2013-11-24 Celery 10.2 Green
2013-11-25 Apple 22.1 Red
2013-11-25 Orange 8.6 Orange
argetz45
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 118.6 Orange
2013-11-24 Apple 74.6 Green
2013-11-24 Celery 10.2 Green
2013-11-25 Nuts 45.8 Brown
2013-11-25 Apple 22.1 Red
2013-11-25 Orange 8.6 Orange
2013-11-26 Pear 102.54 Pale"""
.
from collections import OrderedDict
import re
r = re.compile(''([a-zA-Z/d]+).*/n''
''(20/d/d-[01]/d-[0123]/d.+/n?''
''(.+/n?)*)''
''(?=[ /n]*/Z''
''|''
''/n+[a-zA-Z/d]+.*/n''
''20/d/d-[01]/d-[0123]/d)'')
r2 = re.compile(''((20/d/d-[01]/d-[0123]/d) +([^/d.]+)(?<! )[^/n]+)'')
d = OrderedDict()
bef = []
for m in r.finditer(text):
li = []
for x in r2.findall(m.group(2)):
if not any(x[1:3]==elbef for elbef in bef):
bef.append(x[1:3])
li.append(x[0])
d[m.group(1)] = li
for name,lu in d.iteritems():
print ''%s/n%s/n'' % (name,''/n''.join(lu))
resultado
df1
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 8.6 Orange
2013-11-24 Apple 7.6 Green
2013-11-24 Celery 10.2 Green
df2
2013-11-25 Apple 22.1 Red
2013-11-25 Orange 8.6 Orange
argetz45
2013-11-25 Nuts 45.8 Brown
2013-11-26 Pear 102.54 Pale
Un detalle importante a tener en cuenta es que sus datos tienen valores de índice duplicados , por lo que para realizar cualquier comparación directa necesitamos convertir todo como único con df.reset_index()
y, por lo tanto, podemos realizar selecciones basadas en las condiciones. Una vez que en su caso el índice está definido, supongo que le gustaría mantener el índice para que haya una solución de una sola línea:
[~df2.reset_index().isin(df1.reset_index())].dropna().set_index(''Date'')
Una vez que el objetivo desde una perspectiva pitonica es mejorar la legibilidad, podemos romper un poco:
# keep the index name, if it does not have a name it uses the default name
index_name = df.index.name if df.index.name else ''index''
# setting the index to become unique
df1 = df1.reset_index()
df2 = df2.reset_index()
# getting the differences to a Dataframe
df_diff = df2[~df2.isin(df1)].dropna().set_index(index_name)
# given
df1=pd.DataFrame({''Date'':[''2013-11-24'',''2013-11-24'',''2013-11-24'',''2013-11-24''],
''Fruit'':[''Banana'',''Orange'',''Apple'',''Celery''],
''Num'':[22.1,8.6,7.6,10.2],
''Color'':[''Yellow'',''Orange'',''Green'',''Green'']})
df2=pd.DataFrame({''Date'':[''2013-11-24'',''2013-11-24'',''2013-11-24'',''2013-11-24'',''2013-11-25'',''2013-11-25''],
''Fruit'':[''Banana'',''Orange'',''Apple'',''Celery'',''Apple'',''Orange''],
''Num'':[22.1,8.6,7.6,1000,22.1,8.6],
''Color'':[''Yellow'',''Orange'',''Green'',''Green'',''Red'',''Orange'']})
# find which rows are in df2 that aren''t in df1 by Date and Fruit
df_2notin1 = df2[~(df2[''Date''].isin(df1[''Date'']) & df2[''Fruit''].isin(df1[''Fruit'']) )].dropna().reset_index(drop=True)
# output
print(''df_2notin1/n'', df_2notin1)
# Color Date Fruit Num
# 0 Red 2013-11-25 Apple 22.1
# 1 Orange 2013-11-25 Orange 8.6