Encuentra cuentas de valores dentro de un dataframe de pandas de cadenas

Quiero obtener el recuento de frecuencia de cadenas dentro de una columna. Por un lado, esto es similar a contraer un dataframe a un conjunto de filas que solo refleja las cadenas en la columna. Pude resolver esto con un bucle, pero sé que hay una mejor solución.

Ejemplo df:

2017-08-09 2017-08-10 id 0 pre pre 2 active_1-3 active_1 3 active_1 active_1 4 active_3-7 active_3-7 5 active_1 active_1 

Y quiero salir:

  2017-08-09 2017-08-10 pre 1 1 active_1 2 3 active_1-3 3 0 active_3-7 1 1 

Busqué en muchos foros pero no pude encontrar una buena respuesta.

Supongo que un enfoque pivot_table es el correcto, pero no pude obtener los argumentos correctos para colapsar una tabla que no tenía un índice obvio para el df de salida.

Pude hacer que esto funcionara mediante la iteración de cada columna, usando value_counts (), y agregando cada serie de conteo de valores en un nuevo dataframe, pero sé que hay una mejor solución.

 for i in range(len(date_cols)): new_values = df[date_cols[i]].value_counts() output_df = pd.concat([output_df , new_values], axis=1) 

¡Gracias!

Puede usar value counts y pd.Series (Gracias por mejorar Jon), es decir

 ndf = df.apply(pd.Series.value_counts).fillna(0) 
            2017-08-09 2017-08-10
 active_1 2 3.0
 active_1-3 1 0.0
 active_3-7 1 1.0
 pre 1 1.0

Tiempos :

 k = pd.concat([df]*1000) # @cᴏʟᴅsᴘᴇᴇᴅ's method %%timeit pd.get_dummies(kT).groupby(by=lambda x: x.split('_', 1)[1], axis=1).sum().T 1 loop, best of 3: 5.68 s per loop %%timeit # @cᴏʟᴅsᴘᴇᴇᴅ's method k.stack().str.get_dummies().sum(level=1).T 10 loops, best of 3: 84.1 ms per loop # My method %%timeit k.apply(pd.Series.value_counts).fillna(0) 100 loops, best of 3: 7.57 ms per loop # FabienP's method %%timeit k.unstack().groupby(level=0).value_counts().unstack().T.fillna(0) 100 loops, best of 3: 7.35 ms per loop #@Wen's method (fastest for now) pd.concat([pd.Series(collections.Counter(k[x])) for x in df.columns],axis=1) 100 loops, best of 3: 4 ms per loop 

No sé por qué me adicto a usar apply de esta manera extraña …

 df.apply(lambda x : x.groupby(x).count()).fillna(0) Out[31]: 2017-08-09 2017-08-10 active_1 2 3.0 active_1-3 1 0.0 active_3-7 1 1.0 pre 1 1.0 

O

 import collections df.apply(lambda x : pd.Series(collections.Counter(x))).fillna(0) 

Como lo que esperaba simple para bucle es más rápido que aplicar

 pd.concat([pd.Series(collections.Counter(df[x])) for x in df.columns],axis=1) 

stack + get_dummies + sum :

 df.stack().str.get_dummies().sum(level=1).T 2017-08-09 2017-08-10 active_1 2 3 active_1-3 1 0 active_3-7 1 1 pre 1 1 

Muy piR-esque si lo digo yo mismo, con elegancia, no con velocidad.


Alternativa con pd.get_dummies + groupby :

 pd.get_dummies(df.T).groupby(by=lambda x: x.split('_', 1)[1], axis=1).sum().T 2017-08-09 2017-08-10 active_1 2 3 active_1-3 1 0 active_3-7 1 1 pre 1 1 

Otra solución usando groupby y value_counts

 df.unstack().groupby(level=0).value_counts().unstack().T.fillna(0) Out[]: 2017-08-09 2017-08-10 active_1 2.0 3.0 active_1-3 1.0 0.0 active_3-7 1.0 1.0 pre 1.0 1.0 

O evitando la última llamada a fillna

 df.unstack().groupby(level=0).value_counts().unstack(fill_value=0).T