Estoy tratando de realizar múltiples sumas en la misma trama de datos y luego concatenar las nuevas tramas de datos en una sola trama de datos final. ¿Hay una manera concisa de hacer esto, o tengo que usar iteración?
Tengo un diccionario de esta forma {key: [list_of_idx], ...}
y la necesidad de GroupBy mi trama de datos para cada tecla.
Data de muestra
import random
random.seed(1)
df_len = 5
df = {'idx':{i: i+1 for i in range(df_len)}, 'data':{i:random.randint(1,11) for i in range(df_len)}}
df = pd.DataFrame(df).set_index('idx')
# Groups with the idx to groupby
groups = {'a': [1,2,3,4,5],
'b': [1,4],
'c': [5]}
# I'm trying to avoid/find a faster way than this
dfs = []
for grp in groups:
_df = df.loc[groups[grp]]
_df['grp'] = grp
_df = _df.groupby('grp').sum()
dfs.append(_df)
dff = pd.concat(dfs)
Entrada (df)
data idx
0 2 1
1 10 2
2 9 3
3 3 4
4 6 5
Resultados previstos (DFF)
data
grp
a 30
c 6
b 5
Nota: estoy atascado con Python 2.7 y 0.16.1 pandas
resultado tiempo
Probé los métodos propuestos y calcular el tiempo de ejecución. Muestro el tiempo medio por cada ejecución (1000 utilizando ejecuciones por cada respuesta): Yo Couln't probar Quang Hoang primera respuesta, debido a mi versión de los pandas.
time method
0.00696 sec my method (question)
0.00328 sec piRSquared (pd.concat)
0.00024 sec piRSquared (collections and defaultdict)
0.00444 sec Quang Hoang (2nd method : concat + reindex)
El uso inteligente de pd.concat
pd.concat({k: df.loc[v] for k, v in groups.items()}).sum(level=0)
data
a 22
b 8
c 2
NOTA: Esto funciona mágicamente para todas las columnas.
Supongamos que tenemosmore_data
import random
random.seed(1)
df_len = 5
df = {
'idx':{i: i+1 for i in range(df_len)},
'data':{i:random.randint(1,11) for i in range(df_len)},
'more_data':{i:random.randint(1,11) for i in range(df_len)},
}
df = pd.DataFrame(df).set_index('idx')
Luego
pd.concat({k: df.loc[v] for k, v in groups.items()}).sum(level=0)
data more_data
a 22 42
b 8 19
c 2 7
Pero me quedo con más de Python: collections.defaultdict
from collections import defaultdict
results = defaultdict(int)
for k, V in groups.items():
for v in V:
results[k] += df.at[v, 'data']
pd.Series(results)
a 22
b 8
c 2
dtype: int64
Para que esto funcione con múltiples columnas, tengo que configurar el defaultdict
un poco diferente:
from collections import defaultdict
results = defaultdict(lambda: defaultdict(int))
for k, V in groups.items():
for v in V:
for c in df.columns:
results[c][k] += df.at[v, c]
pd.DataFrame(results)
data more_data
a 22 42
b 8 19
c 2 7
Esto es lo que se vería como sin defaultdict
pero utilizando el método setdefault
del dict
objeto en su lugar.
results = {}
for k, V in groups.items():
for v in V:
for c in df.columns:
results.setdefault(c, {})
results[c].setdefault(k, 0)
results[c][k] += df.at[v, c]
pd.DataFrame(results)
data more_data
a 22 42
b 8 19
c 2 7