Python: Как наиболее эффективно векторизовать это для -l oop (который использует pandas groupby) - PullRequest
1 голос
/ 10 апреля 2020

Я пытаюсь создать функцию агрегирования акций с циклом по датам. По сути, я хочу суммировать переменную на основе группировки и итерации по датам. Однако у меня есть несколько записей для данного дня, что немного усложняет проблему. В настоящее время я использую pandas .groupby, но он работает слишком медленно для моих> 3-метровых строк, поэтому мне интересно, как я мог бы вместо этого векторизовать его.

Представленный некоторым примером кода, я хотел бы векторизовать ниже. Представьте, что я складываю что-то, скажем, яблоки для группы покупателей, и я хотел бы отслеживать, сколько яблок я складирую для каждого покупателя в любой день. Каждый клиент может внести «депозит» из нескольких яблок за один день до даты в будущем, и может сделать несколько депозитов каждый день. Пример кода выглядит так:

import pandas as pd

df = {'StartDate': ['2020-01-01','2020-01-01','2020-01-01','2020-01-02','2020-01-02','2020-01-02','2020-01-03','2020-01-04','2020-01-04','2020-01-05'],
      'EndDate':['2020-01-02','2020-01-02','2020-01-05','2020-01-05','2020-01-03','2020-01-04','2020-01-06','2020-01-06','2020-01-06','2020-01-06'],
      'Apples':[5,6,2,4,4,10,8,9,3,7],
      'Customer':['A','B','A','C','A','A','B','B','C','A']}
df = pd.DataFrame(data=df)

cycledates = ['2020-01-01','2020-01-02','2020-01-03','2020-01-04','2020-01-05','2020-01-06']

def stockfunction(indata, ingroupingvars, cycledates):
    stock_agg = pd.DataFrame()
    for i in cycledates:
        stock_input = indata.loc[
            (indata['StartDate'] <= i) & (indata['EndDate'] > i)]
        stock_input['EvaluationDate'] = i
        stock_marg = stock_input.groupby(ingroupingvars)['Apples'].sum().unstack(level=0)
        stock_agg = stock_agg.append(stock_marg)
    return stock_agg

ingroupingvars = ['Customer','EvaluationDate']
stock = stockfunction(df,ingroupingvars,cycledates)

Пример вывода

Кадр данных выглядит так:

StartDate     EndDate  Apples Customer
0  2020-01-01  2020-01-02       5        A
1  2020-01-01  2020-01-02       6        B
2  2020-01-01  2020-01-05       2        A
3  2020-01-02  2020-01-05       4        C
4  2020-01-02  2020-01-03       4        A
5  2020-01-02  2020-01-04      10        A
6  2020-01-03  2020-01-06       8        B
7  2020-01-04  2020-01-06       9        B
8  2020-01-04  2020-01-06       3        C
9  2020-01-05  2020-01-06       7        A

И конечный результат выглядит так:

Customer         A     B    C
EvaluationDate               
2020-01-01       7   6.0  NaN
2020-01-02      16   NaN  4.0
2020-01-03      12   8.0  4.0
2020-01-04       2  17.0  7.0
2020-01-05       7  17.0  3.0

... просто для того, чтобы мои исходные данные работали вечно.

1 Ответ

1 голос
/ 10 апреля 2020

Похоже, вы хотите сделать кросс-слияние и запрос:

# convert to datetime type for safe comparison
Eval_df = pd.to_datetime(pd.Series(cycledates, name='EvaluationDate')).to_frame()

df['StartDate'] = pd.to_datetime(df['StartDate'])
df['EndDate'] = pd.to_datetime(df['EndDate'])


(df.assign(dummy=1)
   .merge(cycledates.assign(dummy=1), on='dummy')
   .query('StartDate<=EvaluationDate<EndDate')
   .groupby(['EvaluationDate', 'Customer'])      # ingroupingvars
   ['Apples'].sum()                              #
   .unstack('Customer', fill_value=0)            # remove fill_value=0 if you want the NaN
)

Вывод:

Customer         A   B  C
EvaluationDate           
2020-01-01       7   6  0
2020-01-02      16   0  4
2020-01-03      12   8  4
2020-01-04       2  17  7
2020-01-05       7  17  3
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...