¿Cómo analizar todas las entradas duplicadas en este DataFrame de Pandas?

Me gustaría poder calcular estadísticas descriptivas sobre datos en un DataFrame de Pandas, pero solo me interesan las entradas duplicadas. Por ejemplo, digamos que tengo el DataFrame creado por:

import pandas as pd data={'key1':[1,2,3,1,2,3,2,2],'key2':[2,2,1,2,2,4,2,2],'data':[5,6,2,6,1,6,2,8]} frame=pd.DataFrame(data,columns=['key1','key2','data']) print frame key1 key2 data 0 1 2 5 1 2 2 6 2 3 1 2 3 1 2 6 4 2 2 1 5 3 4 6 6 2 2 2 7 2 2 8 

Como puede ver, las filas 0,1,3,4,6 y 7 están todas duplicadas (usando ‘key1’ y ‘key2’. Sin embargo, si indizo este DataFrame de esta manera:

 frame[frame.duplicated(['key1','key2'])] 

yo obtengo

  key1 key2 data 3 1 2 6 4 2 2 1 6 2 2 2 7 2 2 8 

(es decir, las filas 1 y 2 no aparecen porque no están indexadas a Verdadero por el método duplicado).

Ese es mi primer problema. Mi segundo problema se refiere a cómo extraer las estadísticas descriptivas de esta información. Olvidando el duplicado faltante por el momento, digamos que quiero calcular el .min () y el .max () para las entradas duplicadas (para que pueda obtener un rango). Puedo usar groupby y estos métodos en el objeto groupby así:

 a.groupby(['key1','key2']).min() 

lo que da

  key1 key2 data key1 key2 1 2 1 2 6 2 2 2 2 1 

Los datos que quiero están obviamente aquí, pero ¿cuál es la mejor manera de extraerlos? ¿Cómo indexo el objeto resultante para obtener lo que quiero (que es la clave1, clave2, información de datos)?

EDITAR para Pandas 0.17 o posterior:

Como el argumento take_last del método duplicated() fue desaprobado en favor del nuevo argumento de keep desde Pandas 0.17 , consulte esta respuesta para conocer el enfoque correcto:

  • Invoque el método duplicated() con keep=False , es decir, frame.duplicated(['key1', 'key2'], keep=False) .

Por lo tanto, para extraer los datos necesarios para esta pregunta específica, basta con lo siguiente:

 In [81]: frame[frame.duplicated(['key1', 'key2'], keep=False)].groupby(('key1', 'key2')).min() Out[81]: data key1 key2 1 2 5 2 2 1 [2 rows x 1 columns] 

Curiosamente, este cambio en Pandas 0.17 puede atribuirse parcialmente a esta pregunta, como se menciona en este número .


Para versiones anteriores Pandas 0.17 :

Podemos jugar con el argumento take_last del método duplicated() :

take_last : boolean , por defecto False

Para un conjunto de filas duplicadas distintas, marque todas las filas menos la última como duplicadas. El valor predeterminado es para todos, pero la primera fila debe estar marcada.

Si establecemos el valor de take_last en True , take_last todos menos la última fila duplicada. Combinando esto junto con su valor predeterminado de False , que marca todas las filas menos la primera, nos permite marcar todas las filas duplicadas:

 In [76]: frame.duplicated(['key1', 'key2']) Out[76]: 0 False 1 False 2 False 3 True 4 True 5 False 6 True 7 True dtype: bool In [77]: frame.duplicated(['key1', 'key2'], take_last=True) Out[77]: 0 True 1 True 2 False 3 False 4 True 5 False 6 True 7 False dtype: bool In [78]: frame.duplicated(['key1', 'key2'], take_last=True) | frame.duplicated(['key1', 'key2']) Out[78]: 0 True 1 True 2 False 3 True 4 True 5 False 6 True 7 True dtype: bool In [79]: frame[frame.duplicated(['key1', 'key2'], take_last=True) | frame.duplicated(['key1', 'key2'])] Out[79]: key1 key2 data 0 1 2 5 1 2 2 6 3 1 2 6 4 2 2 1 6 2 2 2 7 2 2 8 [6 rows x 3 columns] 

Ahora solo necesitamos usar los métodos groupby y min , y creo que la salida está en el formato requerido:

 In [81]: frame[frame.duplicated(['key1', 'key2'], take_last=True) | frame.duplicated(['key1', 'key2'])].groupby(('key1', 'key2')).min() Out[81]: data key1 key2 1 2 5 2 2 1 [2 rows x 1 columns] 

Para obtener una lista de todas las entradas duplicadas con Pandas versión 0.17, simplemente puede configurar ‘keep = False’ en la función duplicada .

 frame[frame.duplicated(['key1','key2'],keep=False)] key1 key2 data 0 1 2 5 1 2 2 6 3 1 2 6 4 2 2 1 6 2 2 2 7 2 2 8 

Aquí hay una posible solución para devolver todos los valores duplicados en las dos columnas (es decir, las filas 0, 1, 3, 4, 6, 7):

 >>> key1_dups = frame.key1[frame.key1.duplicated()].values >>> key2_dups = frame.key2[frame.key2.duplicated()].values >>> frame[frame.key1.isin(key1_dups) & frame.key2.isin(key2_dups)] key1 key2 data 0 1 2 5 1 2 2 6 3 1 2 6 4 2 2 1 6 2 2 2 7 2 2 8 

( Edición : en realidad, el df.duplicated(take_last=True) | df.duplicated() en @ La respuesta de Yoel es más df.duplicated(take_last=True) | df.duplicated() .

Para consultar los resultados de su operación groupby , puede usar loc . Por ejemplo:

 >>> dups = frame[frame.key1.isin(key1_dups) & frame.key2.isin(key2_dups)] >>> grouped = dups.groupby(['key1','key2']).min() >>> grouped data key1 key2 1 2 5 2 2 1 >>> grouped.loc[1, 2] data 5 Name: (1, 2), dtype: int64 

Alternativamente, gire grouped nuevamente en un DataFrame de “apariencia normal” restableciendo ambos índices:

 >>> grouped.reset_index(level=0).reset_index(level=0) key2 key1 data 0 2 1 5 1 2 2 1