unir juntar inner dataframes data python pandas dataframe

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:

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