Pandas groupby: cómo conseguir una unión de cuerdas

Tengo un dataframe como este:

ABC 0 1 0.749065 This 1 2 0.301084 is 2 3 0.463468 a 3 4 0.643961 random 4 1 0.866521 string 5 2 0.120737 ! 

Vocación

 In [10]: print df.groupby("A")["B"].sum() 

volverá

 A 1 1.615586 2 0.421821 3 0.463468 4 0.643961 

Ahora me gustaría hacer “lo mismo” para la columna “C”. Debido a que esa columna contiene cadenas, sum () no funciona (aunque podría pensar que concatenaría las cadenas). Lo que realmente me gustaría ver es una lista o un conjunto de cadenas para cada grupo, es decir,

 A 1 {This, string} 2 {is, !} 3 {a} 4 {random} 

He estado tratando de encontrar maneras de hacer esto.

Series.unique () ( http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.unique.html ) no funciona, aunque

 df.groupby("A")["B"] 

es un

 pandas.core.groupby.SeriesGroupBy object 

Así que esperaba que cualquier método de la serie funcionara. ¿Algunas ideas?

 In [4]: df = read_csv(StringIO(data),sep='\s+') In [5]: df Out[5]: ABC 0 1 0.749065 This 1 2 0.301084 is 2 3 0.463468 a 3 4 0.643961 random 4 1 0.866521 string 5 2 0.120737 ! In [6]: df.dtypes Out[6]: A int64 B float64 C object dtype: object 

Cuando aplica su propia función, no hay exclusiones automáticas de columnas no numéricas. Esto es más lento, sin embargo, que la aplicación de .sum() al groupby

 In [8]: df.groupby('A').apply(lambda x: x.sum()) Out[8]: ABC A 1 2 1.615586 Thisstring 2 4 0.421821 is! 3 3 0.463468 a 4 4 0.643961 random 

sum por defecto concatena

 In [9]: df.groupby('A')['C'].apply(lambda x: x.sum()) Out[9]: A 1 Thisstring 2 is! 3 a 4 random dtype: object 

Puedes hacer prácticamente lo que quieras

 In [11]: df.groupby('A')['C'].apply(lambda x: "{%s}" % ', '.join(x)) Out[11]: A 1 {This, string} 2 {is, !} 3 {a} 4 {random} dtype: object 

Haciendo esto todo un grupo de marcos a la vez. La clave es devolver una Series

 def f(x): return Series(dict(A = x['A'].sum(), B = x['B'].sum(), C = "{%s}" % ', '.join(x['C']))) In [14]: df.groupby('A').apply(f) Out[14]: ABC A 1 2 1.615586 {This, string} 2 4 0.421821 {is, !} 3 3 0.463468 {a} 4 4 0.643961 {random} 

Puede utilizar el método de aplicación para aplicar una función arbitraria a los datos agrupados. Así que si quieres un conjunto, aplícalo. Si quieres una lista, aplica la list .

 >>> d AB 0 1 This 1 2 is 2 3 a 3 4 random 4 1 string 5 2 ! >>> d.groupby('A')['B'].apply(list) A 1 [This, string] 2 [is, !] 3 [a] 4 [random] dtype: object 

Si desea algo más, simplemente escriba una función que haga lo que usted quiere y luego apply .

Es posible que pueda usar la función aggregate (o agg ) para concatenar los valores. (Código no probado)

 df.groupby('A')['B'].agg(lambda col: ''.join(col)) 

Una solución simple sería:

 >>> df.groupby(['A','B']).c.unique().reset_index() 

Si desea sobrescribir la columna B en el dataframe, esto debería funcionar:

  df = df.groupby('A',as_index=False).agg(lambda x:'\n'.join(x)) 

Podrías probar esto:

 df.groupby('A').agg({'B':'sum','C':'-'.join})