#python #pandas
#python #pandas
Вопрос:
Как получить среднее значение только положительных значений после groupby в pandas?
MWE:
import numpy as np
import pandas as pd
flights = pd.read_csv('https://github.com/bhishanpdl/Datasets/blob/master/nycflights13.csv?raw=true')
print(flights.shape)
print(flights.iloc[:2,:4])
print()
not_cancelled = flights.dropna(subset=['dep_delay','arr_delay'])
df = (not_cancelled.groupby(['year','month','day'])['arr_delay']
.mean().reset_index()
)
df['avg_delay2'] = df[df.arr_delay>0]['arr_delay'].mean()
print(df.head())
Это дает все значения avg_delay2 как 16.66.
(336776, 19)
год месяц день dep_time
0 2013 1 1 517.0
1 2013 1 1 533.0
год месяц день arr_delay avg_delay2
0 2013 1 1 12.651023 16.665681
1 2013 1 2 12.692888 16.665681
2 2013 1 3 5.733333 16.665681
3 2013 1 4 -1.932819 16.665681
4 2013 1 5 -1.525802 16.665681
Что НЕВЕРНО.
# sanity check
a = not_cancelled.query(""" year==2013 amp; month ==1 amp; day ==1 """)['arr_delay']
a = a[a>0]
a.mean() # 32.48156182212581
Когда я делаю то же самое в R:
library(nycflights13)
not_cancelled = flights %>%
filter( !is.na(dep_delay), !is.na(arr_delay))
df = not_cancelled %>%
group_by(year,month,day) %>%
summarize(
# average delay
avg_delay1 = mean(arr_delay),
# average positive delay
avg_delay2 = mean(arr_delay[arr_delay>0]))
head(df)
Это дает правильный вывод для avg_delay2.
год месяц день avg_delay1 avg_delay2
2013 1 1 12.651023 32.48156
2013 1 2 12.692888 32.02991
2013 1 3 5.733333 27.66087
2013 1 4 -1.932819 28.30976
2013 1 5 -1.525802 22.55882
2013 1 6 4.236429 24.37270
Как это сделать в Pandas?
Ответ №1:
Я бы отфильтровал позитив, прежде чем groupby
df = (not_cancelled[not_cancelled.arr_delay >0].groupby(['year','month','day'])['arr_delay']
.mean().reset_index()
)
df.head()
потому что, как и в вашем коде, df
это отдельный фрейм данных после завершения groupby
операции, и
df['avg_delay2'] = df[df.arr_delay>0]['arr_delay'].mean()
присвоите то же значение df['avg_delay2']
Редактировать: Подобно R, вы можете сделать и то, и другое за один снимок, используя agg
:
def mean_pos(x):
return x[x>0].mean()
df = (not_cancelled.groupby(['year','month','day'])['arr_delay']
.agg({'arr_delay': 'mean', 'arr_delay_2': mean_pos})
)
df.head()
Комментарии:
1. Я сделал аналогичную вещь, мне потребовалось некоторое время, чтобы сделать это на python, в то время как это была всего лишь еще одна строка в R. У меня всегда было НЕПРАВИЛЬНОЕ впечатление, что
pandas
это почти полезно какdplyr
. Я ошибаюсь.2. @astro123 смотрите мой обновленный ответ для краткой версии.
3. Мы не можем использовать dictionary в будущих версиях pandas. Он выдает предупреждение об устаревании в pandas 23.
4. Я этого не осознавал. В этом случае просто сделайте
.agg(['mean', mean_pos])
, будут два новых столбца['mean', 'mean_pos']
, и вы можете переименовать их по своему усмотрению.
Ответ №2:
Обратите внимание, что начиная с pandas 23, использование dictionary в gropby agg устарело и будет удалено в будущем, поэтому мы не можем использовать этот метод.
Предупреждение
df = (not_cancelled.groupby(['year','month','day'])['arr_delay']
.agg({'arr_delay': 'mean', 'arr_delay_2': mean_pos})
)
FutureWarning: using a dict on a Series for aggregation
is deprecated and will be removed in a future version.
Итак, чтобы решить эту проблему в данном конкретном случае, мне пришла в голову другая идея.
Создайте новый столбец, в котором все неположительные значения nans, затем выполните обычную groupby.
import numpy as np
import pandas as pd
# read data
flights = pd.read_csv('https://github.com/bhishanpdl/Datasets/blob/master/nycflights13.csv?raw=true')
# select flights that are not cancelled
df = flights.dropna(subset=['dep_delay','arr_delay'])
# create new column to fill non-positive with nans
df['arr_delay_pos'] = df['arr_delay']
df.loc[df.arr_delay_pos <= 0,'arr_delay_pos'] = np.nan
df.groupby(['year','month','day'])[['arr_delay','arr_delay_pos']].mean().reset_index().head()
Это дает:
year month day arr_delay arr_delay_positive
0 2013 1 1 12.651023 32.481562
1 2013 1 2 12.692888 32.029907
2 2013 1 3 5.733333 27.660870
3 2013 1 4 -1.932819 28.309764
4 2013 1 5 -1.525802 22.558824
Проверка работоспособности
# sanity check
a = not_cancelled.query(""" year==2013 amp; month ==1 amp; day ==1 """)['arr_delay']
a = a[a>0]
a.mean() # 32.48156182212581