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