Pandas: Filtrado de DataFrame usando groupby y una función

Usando Python 3.3 y Pandas 0.10

Tengo un DataFrame que se construye a partir de la concatenación de varios archivos CSV. Primero, filtro todos los valores en la columna Nombre que contienen una cadena determinada. El resultado se ve algo como esto (acortado por razones de brevedad, en realidad hay más columnas):

Name ID 'A' 1 'B' 2 'C' 3 'C' 3 'E' 4 'F' 4 ... ... 

Ahora mi problema es que quiero eliminar un caso especial de valores “duplicados”. Quiero eliminar todos los duplicados de ID (en realidad toda la fila) donde los valores de Nombre correspondientes que se asignan a esta ID no son similares. En el ejemplo anterior, me gustaría mantener las filas con ID 1, 2 y 3. Donde ID = 4 los valores de Nombre no son iguales y quiero eliminarlos.

Intenté usar la siguiente línea de código (según la sugerencia de aquí: Python Pandas: eliminar entradas según el número de ocurrencias ).

Código:

 df[df.groupby('ID').apply(lambda g: len({x for x in g['Name']})) == 1] 

Sin embargo, eso me da el error: ValueError: Item wrong length 51906 instead of 109565!

Editar:

En lugar de usar apply() , también he intentado usar transform() , pero eso me da el error: AttributeError: 'int' object has no attribute 'ndim' . ¡Una explicación de por qué el error es diferente por función es muy apreciada!

Además, quiero mantener todas las filas donde ID = 3 en el ejemplo anterior.

Gracias de antemano, Matthijs

En lugar de len longitud, creo que desea considerar el número de valores únicos de Nombre en cada grupo. Use nunique() , y vea esta receta para filtrar grupos.

 df[df.groupby('ID').Name.transform(lambda x: x.nunique() == 1).astype('bool')] 

Si actualiza a pandas 0.12, puede usar el nuevo método de filter en grupos, lo que lo hace más conciso y directo.

 df.groupby('ID').filter(lambda x: x.Name.nunique() == 1) 

Una observación general: a veces, por supuesto, quieres saber la longitud del grupo, pero me parece que el size es una opción más segura que la len , lo que en algunos casos ha sido problemático.

Primero podrías soltar los duplicados:

 In [11]: df = df.drop_duplicates() In [12]: df Out[12]: Name ID 0 A 1 1 B 2 2 C 3 4 E 4 5 F 4 

El groupby id y solo consideramos aquellos con un elemento:

 In [13]: g = df.groupby('ID') In [14]: size = (g.size() == 1) In [15]: size Out[15]: ID 1 True 2 True 3 True 4 False dtype: bool In [16]: size[size].index Out[16]: Int64Index([1, 2, 3], dtype=int64) In [17]: df['ID'].isin(size[size].index) Out[17]: 0 True 1 True 2 True 4 False 5 False Name: ID, dtype: bool 

Y el índice booleano por esto:

 In [18]: df[df['ID'].isin(size[size].index)] Out[18]: Name ID 0 A 1 1 B 2 2 C 3