Вопрос или проблема
У меня есть следующий датафрейм 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)
Пояснение к коду
-
Общее количество побед: Мы используем условие
(df['Rank'] == 1).cumsum()
, чтобы считать общее количество побед без смещения. Это позволяет избегать проблемы с отсутствующими значениями на первой строке. -
Количество побед в текущем году: Мы инициализируем новый столбец
Athle_recent_wins
значениями 0. Затем по каждому уникальному году в датах мы создаем маску и считаем победы только за этот год. Это гарантирует, что сумма начнется с правильного значения (0) в начале каждого года.
Итог
Результат будет отображать правильное количество побед как за весь период, так и за текущий год, обеспечивая корректное поведение для каждого года, что вы и хотели достичь. Если у вас возникнут дополнительные вопросы или потребуется помощь по другим аспектам анализа данных в Pandas, не стесняйтесь обратиться!