Суммарные суммы на одном и том же кадре данных - PullRequest
3 голосов
/ 13 марта 2020

Я пытаюсь выполнить несколько сумм для одного и того же кадра данных, а затем объединить новые кадры данных в один окончательный кадр данных. Есть ли краткий способ сделать это, или мне нужно использовать итерацию?

У меня есть диктант этой формы {key: [list_of_idx], ...}, и мне нужно сгруппировать мой фрейм данных для каждого ключа.

Пример данных

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)

Ввод (df)

   data  idx
0     2    1
1    10    2
2     9    3
3     3    4
4     6    5

Ожидаемый вывод (dff)

     data
grp      
a      30
c       6
b       5

Примечание: я застрял с python 2.7 и pandas 0.16.1

Время результата

Я проверил предложенные методы и рассчитал время выполнения. Я показываю среднее время выполнения (используя 1000 выполнений для каждого ответа): я не могу проверить первый ответ Куанг Хоанг из-за моей 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)  

Ответы [ 2 ]

5 голосов
/ 13 марта 2020

Это должно быть (довольно) немного быстрее:

s = pd.Series(groups).explode()
df.reindex(s).groupby(s.index)['data'].sum()

Вывод:

a    30
b     5
c     6
Name: data, dtype: int64

Обновление: аналогичный подход для более ранней версии pandas, хотя это может не так быстро

s = pd.concat([pd.DataFrame({'grp':a, 'idx':b}) for a,b in groups.items()],
             ignore_index=True).set_index('grp')
df.reindex(s.idx).groupby(s.index)['data'].sum()
4 голосов
/ 13 марта 2020

Умное использование pd.concat

pd.concat({k: df.loc[v] for k, v in groups.items()}).sum(level=0)

   data
a    22
b     8
c     2

ПРИМЕЧАНИЕ: Это волшебно работает для всех столбцов.
Предположим, у нас есть more_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')

Тогда

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

Но я бы придерживался большего 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

Чтобы это работало с несколькими столбцами, мне нужно настроить defaultdict немного по-другому:

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

Вот как это выглядело бы без defaultdict, но с использованием метода setdefault из объекта dict.

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
...