Выполнить несколько операций в одном групповом вызове с пандами? - PullRequest
1 голос
/ 12 июня 2019

Я бы хотел создать итоговый кадр данных после группировки по дате. Я хочу иметь столбец, который показывает среднее значение данного столбца, как оно есть, и среднее значение этого же столбца после фильтрации для экземпляров, которые больше 0. Я выяснил, как я могу это сделать (ниже), но это требует выполнения два отдельных groupby вызова, переименование столбцов и последующее их объединение. Я упал, как будто это можно сделать за один звонок. Я пытался использовать eval для этого, но продолжал получать сообщение об ошибке, и мне говорили, что нужно использовать apply, что я не могу использовать eval для объекта groupby.

Код, который дает мне то, что я хочу, но не очень эффективен:

# Sample data

data = pd.DataFrame(
          {"year" : [2013, 2013, 2013, 2014, 2014, 2014],
           "month" : [1, 2, 3, 1, 2, 3],
           "day": [1, 1, 1, 1, 1, 1],
           "delay": [0, -4, 50, -60, 9, 10]})

subset = (data
          .groupby(['year', 'month', 'day'])['delay']
          .mean()
          .reset_index()
          .rename(columns = {'delay' : 'avg_delay'})
         )

subset_1 = (data[data.delay > 0]
          .groupby(['year', 'month', 'day'])['delay']
          .mean()
          .reset_index()
          .rename(columns = {'delay' : 'avg_delay_pos'})
         )

combined = pd.merge(subset, subset_1, how='left', on=['year', 'month', 'day'])
combined

   year  month  day  avg_delay  avg_delay_pos
0  2013      1    1          0            NaN
1  2013      2    1         -4            NaN
2  2013      3    1         50           50.0
3  2014      1    1        -60            NaN
4  2014      2    1          9            9.0
5  2014      3    1         10           10.0

Ответы [ 2 ]

1 голос
/ 16 июня 2019

IIUC, вы можете использовать следующий код:

>>> data['avg_delay'] = data.pop('delay')
>>> data['avg_delay_pos'] = data.loc[data['avg_delay'].gt(0), 'avg_delay']
>>> data
   day  month  year  avg_delay  avg_delay_pos
0    1      1  2013          0            NaN
1    1      2  2013         -4            NaN
2    1      3  2013         50           50.0
3    1      1  2014        -60            NaN
4    1      2  2014          9            9.0
5    1      3  2014         10           10.0
>>> 

Пояснение:

  • Сначала я удаляю столбец delay и присваиваю ему новое имя avg_delay, поэтому я фактически переименовываю имя delay в avg_delay.

  • Затем я создаю новый столбец с именем avg_delay_pos, который сначала использует loc, чтобы получить значения больше нуля, и поскольку индекс не сбрасывается, поэтому он будет делать индексы больше, чем от нуля до значений avg_delay, а остальные не будут содержать никаких назначений, в которых указано, что они будут NaN, как вы ожидали.

0 голосов
/ 14 июня 2019

Решение зависит от вашей проблемы, но вы можете сделать это с помощью одного группового вызова. Чтобы получить «avg_delay_pos», вам просто нужно удалить отрицательные (и нулевые) значения.

df['delay_pos'] = df['delay'].where(df['delay'] > 0)

(df.filter(like='delay')
   .groupby(pd.to_datetime(df[['year', 'month', 'day']]))
   .mean()
   .add_prefix('avg_'))                                                                                                                                 

            avg_delay  avg_delay_pos
2013-01-01          0            NaN
2013-02-01         -4            NaN
2013-03-01         50           50.0
2014-01-01        -60            NaN
2014-02-01          9            9.0
2014-03-01         10           10.0

Разбивка

where используется для маскировки значений, которые не являются положительными.

df['delay_pos'] = df['delay'].where(df['delay'] > 0)
# df['delay'].where(df['delay'] > 0)                                                                                                  

0     NaN
1     NaN
2    50.0
3     NaN
4     9.0
5    10.0
Name: delay, dtype: float64

Затем извлеките столбцы задержки, по которым мы хотим сгруппировать,

df.filter(like='delay')                                                                                                             

   delay  delay_pos
0      0        NaN
1     -4        NaN
2     50       50.0
3    -60        NaN
4      9        9.0
5     10       10.0

Затем выполните groupby в день,

_.groupby(pd.to_datetime(df[['year', 'month', 'day']])).mean()

            delay  delay_pos
2013-01-01      0        NaN
2013-02-01     -4        NaN
2013-03-01     50       50.0
2014-01-01    -60        NaN
2014-02-01      9        9.0
2014-03-01     10       10.0

Если pd.to_datetime используется для преобразования столбцов года / месяца / дня в один столбец даты и времени, более эффективно группировать по одному столбцу, чем по нескольким.

pd.to_datetime(df[['year', 'month', 'day']])                                                                                        

0   2013-01-01
1   2013-02-01
2   2013-03-01
3   2014-01-01
4   2014-02-01
5   2014-03-01
dtype: datetime64[ns]

Финальный .add_prefix('avg_') добавляет к результату префикс "_avg".


Альтернативный способ сделать это, если вам нужны отдельные столбцы год / месяц / день, будет

df['delay_pos'] = df['delay'].where(df['delay'] > 0)
df.groupby(['year', 'month', 'day']).mean().add_prefix('avg_').reset_index()

   year  month  day  avg_delay  avg_delay_pos
0  2013      1    1          0            NaN
1  2013      2    1         -4            NaN
2  2013      3    1         50           50.0
3  2014      1    1        -60            NaN
4  2014      2    1          9            9.0
5  2014      3    1         10           10.0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...