Кумулятивная сумма с начальным значением, отличным от 0, в Pandas

Вопрос или проблема

У меня есть следующий датафрейм df атлетов (индексированный по Athlete_ID) и их рангу (индексированный по Rank), вот слайд датафрейма с конкретным атлетом:

Rank    Athlete_ID  Date
13      143         25/4/2021
1       143         5/4/2021
6       143         24/2/2021
11      143         24/1/2021
4       143         1/1/2021
9       143         13/12/2020
8       143         22/11/2020
1       143         23/9/2020
9       143         6/9/2020
10      143         20/5/2020
1       143         18/3/2020
7       143         26/2/2020
1       143         29/1/2020
1       143         18/12/2019
1       143         20/11/2019
7       143         2/3/2019
4       143         10/2/2019
7       143         27/6/2018
9       143         6/5/2018
2       143         7/1/2018
2       143         17/12/2017
1       143         5/11/2017
3       143         8/10/2017

Я хочу подсчитать общее количество побед (номер ранга 1) и недавнее количество побед (число побед в этом году), вот мой код:

df['Athle_total_wins']=df.sort_values(['Athlete_ID','Date'],ascending=[True,True])['Rank'].shift(1).eq(1).groupby(df['Athlete_ID']).cumsum()
 
df['Athle_recent_wins']=df.sort_values(['Athlete_ID','Date'],ascending=[True,True])['Rank'].shift(1).eq(1).groupby([df['Athlete_ID'],df['Date'].dt.year]).cumsum()
 

и вывод:

Rank    Athlete_ID  Date        Athle_total_wins    Athle_recent_wins
13      143         25/4/2021   8                   1
1       143         5/4/2021    7                   0
6       143         24/2/2021   7                   0
11      143         24/1/2021   7                   0
4       143         1/1/2021    7                   0
9       143         13/12/2020  7                   4
8       143         22/11/2020  7                   4
1       143         23/9/2020   6                   3
9       143         6/9/2020    6                   3
10      143         20/5/2020   6                   3
1       143         18/3/2020   5                   2
7       143         26/2/2020   5                   2
1       143         29/1/2020   4                   1
1       143         18/12/2019  3                   1
1       143         20/11/2019  2                   0
7       143         2/3/2019    2                   0
4       143         10/2/2019   2                   0
7       143         27/6/2018   2                   0
9       143         6/5/2018    2                   0
2       143         7/1/2018    2                   0
2       143         17/12/2017  2                   2
1       143         5/11/2017   1                   1
3       143         8/10/2017   1                   1

что почти то, что я хочу, но не совсем, потому что последняя строка не начинается с 0 (т.е. что-то не так для последнего года 2017 года или последних нескольких строк). Желаемый вывод должен быть

Rank    Athlete_ID  Date        Athle_total_wins    Athle_recent_wins
13      143         25/4/2021   7                   1
1       143         5/4/2021    6                   0
6       143         24/2/2021   6                   0
11      143         24/1/2021   6                   0
4       143         1/1/2021    6                   0
9       143         13/12/2020  6                   4
8       143         22/11/2020  6                   4
1       143         23/9/2020   5                   3
9       143         6/9/2020    5                   3
10      143         20/5/2020   5                   3
1       143         18/3/2020   4                   2
7       143         26/2/2020   4                   2
1       143         29/1/2020   3                   1
1       143         18/12/2019  2                   1
1       143         20/11/2019  1                   0
7       143         2/3/2019    1                   0
4       143         10/2/2019   1                   0
7       143         27/6/2018   1                   0
9       143         6/5/2018    1                   0
2       143         7/1/2018    1                   0
2       143         17/12/2017  1                   1
1       143         5/11/2017   0                   1
3       143         8/10/2017   0                   0

Получите общее количество побед по Athlete_ID:

print( df.where(df.Rank == 1).groupby([df.Athlete_ID]).agg({'Rank': 'count'}) )
#            Rank
#Athlete_ID
#143            7

Получите количество побед по Athlete_ID и Date.year:

print( df.where(df.Rank == 1).groupby([df.Athlete_ID, df.Date.dt.year]).agg({'Rank': 'count'}) )
#                 Rank
#Athlete_ID Date
#143        2017     1
#           2018     0
#           2019     2
#           2020     3
#           2021     1

Ответ или решение

Вопрос о том, как задать начальное значение для функции кумулятивной суммы (cumsum) в Pandas, является распространенной задачей для аналитиков данных и разработчиков, работающих с временными рядами. В вашем случае вы хотите подсчитать общее количество побед (поскольку победа определяется как ранг 1) и недавние победы (победы в текущем году) для каждого спортсмена на основе предоставленного DataFrame.

Проблема с начальным значением

Исходный код, который вы предоставили, вычисляет общее количество побед, используя метод cumsum() с условием на равенство ранга 1, вызывая shift(1) для того, чтобы пропустить текущую строку. Это приводит к некорректной сумме для первого года, так как при первом столкновении с shift(1) значение будет равно NaN.

Решение

Для достижения желаемого результата — чтобы количество побед начиналось с 0 для нового года, необходимо немного изменить ваш код. Вместо применения shift(1) и использования кумулятивной суммы, вы можете создавать новый столбец, который будет учитывать только разговоры о победах вашего спортсмена.

Вот оптимизированный код для достижения вашего результата:

import pandas as pd

# Пример вашего DataFrame
data = {
    'Rank': [13, 1, 6, 11, 4, 9, 8, 1, 9, 10, 1, 7, 1, 1, 1, 7, 4, 7, 9, 2, 2, 1, 3],
    'Athlete_ID': [143]*21,
    'Date': pd.to_datetime([
        '2021-04-25', '2021-04-05', '2021-02-24', '2021-01-24', '2021-01-01',
        '2020-12-13', '2020-11-22', '2020-09-23', '2020-09-06', '2020-05-20',
        '2020-03-18', '2020-02-26', '2020-01-29', '2019-12-18', '2019-11-20',
        '2019-03-02', '2019-02-10', '2018-06-27', '2018-05-06', '2018-01-07',
        '2017-12-17', '2017-11-05', '2017-10-08'
    ])
}
df = pd.DataFrame(data)

# Подсчет общего количества побед
df['Athle_total_wins'] = (df['Rank'] == 1).cumsum()

# Подсчет побед в текущем году
df['Athle_recent_wins'] = 0  # Инициализация столбца недавних побед
for year in df['Date'].dt.year.unique():
    year_mask = df['Date'].dt.year == year
    df.loc[year_mask, 'Athle_recent_wins'] = (df.loc[year_mask, 'Rank'] == 1).cumsum()

# Проверка результата
print(df)

Пояснение к коду

  1. Общее количество побед: Мы используем условие (df['Rank'] == 1).cumsum(), чтобы считать общее количество побед без смещения. Это позволяет избегать проблемы с отсутствующими значениями на первой строке.

  2. Количество побед в текущем году: Мы инициализируем новый столбец Athle_recent_wins значениями 0. Затем по каждому уникальному году в датах мы создаем маску и считаем победы только за этот год. Это гарантирует, что сумма начнется с правильного значения (0) в начале каждого года.

Итог

Результат будет отображать правильное количество побед как за весь период, так и за текущий год, обеспечивая корректное поведение для каждого года, что вы и хотели достичь. Если у вас возникнут дополнительные вопросы или потребуется помощь по другим аспектам анализа данных в Pandas, не стесняйтесь обратиться!

Оцените материал
Добавить комментарий

Капча загружается...