Pandas groupby кумулятивная сумма

93

Я хотел бы добавить столбец совокупной суммы в мой фрейм данных Pandas, чтобы:

name | day       | no
-----|-----------|----
Jack | Monday    | 10
Jack | Tuesday   | 20
Jack | Tuesday   | 10
Jack | Wednesday | 50
Jill | Monday    | 40
Jill | Wednesday | 110

становится:

Jack | Monday     | 10  | 10
Jack | Tuesday    | 30  | 40
Jack | Wednesday  | 50  | 90
Jill | Monday     | 40  | 40
Jill | Wednesday  | 110 | 150

Я пробовал различные комбо df.groupbyи df.agg(lambda x: cumsum(x))безрезультатно.

kc2819
источник
Вы действительно уверены, что хотите агрегировать данные по рабочим дням? Это теряет индекс, и кумулятивная сумма не имеет смысла, если есть несколько недель. В ответах dmitry-andreev и @vjayky вместо этого вычисляется сумма за последовательность дней для каждого имени. Подумайте, как это можно было бы расширить, если бы был столбец даты, по которому записи можно было бы сортировать перед группировкой и агрегированием.
Элиас Хасл

Ответы:

89

Это должно сработать, нужно groupby()дважды:

df.groupby(['name', 'day']).sum() \
  .groupby(level=0).cumsum().reset_index()

Пояснение:

print(df)
   name        day   no
0  Jack     Monday   10
1  Jack    Tuesday   20
2  Jack    Tuesday   10
3  Jack  Wednesday   50
4  Jill     Monday   40
5  Jill  Wednesday  110

# sum per name/day
print( df.groupby(['name', 'day']).sum() )
                 no
name day           
Jack Monday      10
     Tuesday     30
     Wednesday   50
Jill Monday      40
      Wednesday  110

# cumulative sum per name/day
print( df.groupby(['name', 'day']).sum() \
         .groupby(level=0).cumsum() )
                 no
name day           
Jack Monday      10
     Tuesday     40
     Wednesday   90
Jill Monday      40
     Wednesday  150

Dataframe в результате первой суммы индексируется 'name'и 'day'. Вы можете увидеть это, распечатав

df.groupby(['name', 'day']).sum().index 

При вычислении кумулятивной суммы вы хотите сделать это по 'name', соответствующей первому индексу (уровень 0).

Наконец, используйте, reset_indexчтобы имена повторялись.

df.groupby(['name', 'day']).sum().groupby(level=0).cumsum().reset_index()

   name        day   no
0  Jack     Monday   10
1  Jack    Tuesday   40
2  Jack  Wednesday   90
3  Jill     Monday   40
4  Jill  Wednesday  150
CT Zhu
источник
3
Спасибо за ответ. Однако у меня было несколько вопросов: 1. Не могли бы вы объяснить, что означает «level = [0]»? 2. Кроме того, как вы можете видеть, раньше у вас были номера строк во фрейме данных, и эти номера строк исчезнут после того, как вы проведете кумулятивную сумму. Есть ли способ вернуть их?
user3694373
5
1), номер индекса должен быть изменен, так как суммы из нескольких строк, например, 2-е число, 40, равно 10 + 20 + 10, какое значение индекса он должен получить? 1, 2 или 3? Итак, давайте продолжим использовать nameand dayas multiIndex, что имеет больший смысл ( reset_index()чтобы получить intиндекс, если хотите ). 2) level=[0]средство groupbyдолжно работать на 1-м уровне MultiIndex, а именно на колонке name.
CT Zhu
Спасибо CT. Я понял это позже и попробовал reset_index (), чтобы решить мою проблему. Спасибо за подробное объяснение!
user3694373
4
Есть небольшая ошибка: в первом по groupby()умолчанию используется сортировка ключей, поэтому, если вы добавите строку «Джек-Четверг» внизу входного набора данных, вы получите неожиданные результаты. И поскольку я groupby()могу работать с названиями уровней, я нахожу их df.groupby(['name', 'day'], sort=False).sum().groupby(by='name').cumsum().reset_index()менее загадочными.
Николай
Как переименовать столбец?
Джонатан Лам,
47

Это работает в пандах 0.16.2

In[23]: print df
        name          day   no
0      Jack       Monday    10
1      Jack      Tuesday    20
2      Jack      Tuesday    10
3      Jack    Wednesday    50
4      Jill       Monday    40
5      Jill    Wednesday   110
In[24]: df['no_cumulative'] = df.groupby(['name'])['no'].apply(lambda x: x.cumsum())
In[25]: print df
        name          day   no  no_cumulative
0      Jack       Monday    10             10
1      Jack      Tuesday    20             30
2      Jack      Tuesday    10             40
3      Jack    Wednesday    50             90
4      Jill       Monday    40             40
5      Jill    Wednesday   110            150
Дмитрий Андреев
источник
Очень полезно показать, как добавить его обратно в df. Я пробовал использовать преобразование, но с cumsum () это не помогло.
zerovector
2
Обратите внимание, что этот ответ (кажется эквивалентным более простому решению @vjayky ) не суммируется до nameи dayдо вычисления кумулятивной суммы name(примечание: в результате есть 2 строки для Джек + вторник). Это то, что делает его проще, чем ответ CT Zhu .
Николай
39

Модификация к ответу @Dmitry. Это проще и работает в pandas 0.19.0:

print(df) 

 name        day   no
0  Jack     Monday   10
1  Jack    Tuesday   20
2  Jack    Tuesday   10
3  Jack  Wednesday   50
4  Jill     Monday   40
5  Jill  Wednesday  110

df['no_csum'] = df.groupby(['name'])['no'].cumsum()

print(df)
   name        day   no  no_csum
0  Jack     Monday   10       10
1  Jack    Tuesday   20       30
2  Jack    Tuesday   10       40
3  Jack  Wednesday   50       90
4  Jill     Monday   40       40
5  Jill  Wednesday  110      150
вжайки
источник
2
Это кажется наиболее простым решением, если вам не нужна двухэтапная агрегация , как указано в вопросе.
Николай
Единственное, что мне не особенно нравится, это то, что он преобразовал мой int dtype в float.
Крис Фарр,
Это должен быть принятый ответ на окончание групповой части. @ChrisFarr Похоже, что для меня больше не конвертируется в float, начиная с pandas 1.0.3.
Луи Ян
8

ты должен использовать

df['cum_no'] = df.no.cumsum()

http://pandas.pydata.org/pandas-docs/version/0.19.2/generated/pandas.DataFrame.cumsum.html

Другой способ сделать это

import pandas as pd
df = pd.DataFrame({'C1' : ['a','a','a','b','b'],
           'C2' : [1,2,3,4,5]})
df['cumsum'] = df.groupby(by=['C1'])['C2'].transform(lambda x: x.cumsum())
df

введите описание изображения здесь

сушмит
источник
3
При этом рассчитывается глобальная промежуточная сумма, а не отдельная сумма для каждой группы отдельно. Таким образом, Джилл-Понедельник получает значение 130 ( 90как сумма всех значений Джека, + 40, значение для Джилл-Понедельник).
Николай
@Nickolay только что добавил еще один ответ, дайте мне знать, работает ли он
сушмит
Я не уверен, рассчитывает ли он глобальную текущую сумму согласно моему примеру, строка 3 получает значение 4
sushmit
Почему я использую здесь lambda x: x.cumsum () вместо pandas.series.cumsum ()?
Цзиньхуа Ван
7

Вместо df.groupby(by=['name','day']).sum().groupby(level=[0]).cumsum() (см. Выше) вы также можете сделатьdf.set_index(['name', 'day']).groupby(level=0, as_index=False).cumsum()

  • df.groupby(by=['name','day']).sum() фактически просто перемещает оба столбца в MultiIndex
  • as_index=False означает, что вам не нужно впоследствии вызывать reset_index
Кристоф
источник
Спасибо за публикацию, это помогло мне понять, что здесь происходит! Обратите внимание, что groupby().sum()это не просто перемещение обоих столбцов в MultiIndex - он также суммирует два значения для Jack + Tuesday. И as_index=False, похоже, не имеет никакого эффекта в этом случае, поскольку индекс уже был установлен до groupby. А так groupby().cumsum()как имя / день извлекается из столбцов фрейма данных, вам нужно либо добавить результирующий числовой столбец в исходный фрейм данных (как предложили vjayky и Дмитрий), либо переместить имя / день в индекс, а затем reset_index.
Николай
0

data.csv:

name,day,no
Jack,Monday,10
Jack,Tuesday,20
Jack,Tuesday,10
Jack,Wednesday,50
Jill,Monday,40
Jill,Wednesday,110

Код:

import numpy as np
import pandas as pd

df = pd.read_csv('data.csv')
print(df)
df = df.groupby(['name', 'day'])['no'].sum().reset_index()
print(df)
df['cumsum'] = df.groupby(['name'])['no'].apply(lambda x: x.cumsum())
print(df)

Выход:

   name        day   no
0  Jack     Monday   10
1  Jack    Tuesday   20
2  Jack    Tuesday   10
3  Jack  Wednesday   50
4  Jill     Monday   40
5  Jill  Wednesday  110
   name        day   no
0  Jack     Monday   10
1  Jack    Tuesday   30
2  Jack  Wednesday   50
3  Jill     Monday   40
4  Jill  Wednesday  110
   name        day   no  cumsum
0  Jack     Monday   10      10
1  Jack    Tuesday   30      40
2  Jack  Wednesday   50      90
3  Jill     Monday   40      40
4  Jill  Wednesday  110     150
Аадж Каал
источник