Улучшить производительность метода Apply - PullRequest
0 голосов
/ 16 октября 2018

Я бы хотел сгруппировать по переменной моего df "cod_id" и затем применить эту функцию:

[df.loc[df['dt_op'].between(d, d + pd.Timedelta(days = 7)), 'quantity'].sum() \
                        for d in df['dt_op']]

Переход от этого df:

print(df)
dt_op      quantity      cod_id
20/01/18      1            613
21/01/18      8            611
21/01/18      1            613 
...

К этому:

print(final_df)
n = 7

dt_op      quantity   product_code     Final_Quantity
20/01/18      1            613               2
21/01/18      8            611               8
25/01/18      1            613               1
...

Я пробовал с:

def lookforward(x):
    L = [x.loc[x['dt_op'].between(row.dt_op, row.dt_op + pd.Timedelta(days=7)), \
         'quantity'].sum() for row in x.itertuples(index=False)]
    return pd.Series(L, index=x.index)

s = df.groupby('cod_id').apply(lookforward)
s.index = s.index.droplevel(0)

df['Final_Quantity'] = s

print(df)

       dt_op  quantity  cod_id  Final_Quantity
0 2018-01-20         1     613               2
1 2018-01-21         8     611               8
2 2018-01-21         1     613               1

Но это не эффективное решение, поскольку оно вычислительно медленно ;

Как мне улучшить его производительность ?Я достиг бы этого даже с новым кодом / новой функцией , которая приводит к тому же результату.

РЕДАКТИРОВАТЬ:

Подмножество исходного набора данныхтолько с одним продуктом (cod_id == 2) я попытался запустить код, предоставленный "wm":

   print(df)

    cod_id  dt_op          quantita  final_sum
0        2 2017-01-03         1       54.0
1        2 2017-01-04         1       53.0
2        2 2017-01-13         1       52.0
3        2 2017-01-23         2       51.0
4        2 2017-01-26         1       49.0
5        2 2017-02-03         1       48.0
6        2 2017-02-27         1       47.0
7        2 2017-03-05         1       46.0
8        2 2017-03-15         1       45.0
9        2 2017-03-23         1       44.0
10       2 2017-03-27         2       43.0
11       2 2017-03-31         3       41.0
12       2 2017-04-04         1       38.0
13       2 2017-04-05         1       37.0
14       2 2017-04-15         2       36.0
15       2 2017-04-27         2       34.0
16       2 2017-04-30         1       32.0
17       2 2017-05-16         1       31.0
18       2 2017-05-18         1       30.0
19       2 2017-05-19         1       29.0
20       2 2017-06-03         1       28.0
21       2 2017-06-04         1       27.0
22       2 2017-06-07         1       26.0
23       2 2017-06-13         2       25.0
24       2 2017-06-14         1       23.0
25       2 2017-06-20         1       22.0
26       2 2017-06-22         2       21.0
27       2 2017-06-28         1       19.0
28       2 2017-06-30         1       18.0
29       2 2017-07-03         1       17.0
30       2 2017-07-06         2       16.0
31       2 2017-07-07         1       14.0
32       2 2017-07-13         1       13.0
33       2 2017-07-20         1       12.0
34       2 2017-07-28         1       11.0
35       2 2017-08-06         1       10.0
36       2 2017-08-07         1        9.0
37       2 2017-08-24         1        8.0
38       2 2017-09-06         1        7.0
39       2 2017-09-16         2        6.0
40       2 2017-09-20         1        4.0
41       2 2017-10-07         1        3.0
42       2 2017-11-04         1        2.0
43       2 2017-12-07         1        1.0

Ответы [ 2 ]

0 голосов
/ 18 октября 2018

Реализация точного поведения в вопросе затруднена из-за двух недостатков в пандах: не реализуются ни групповые / скользящие / трансформируемые, ни перспективные скользящие редкие даты (см. Другой ответ для более подробной информации).

Этот ответпытается обойти оба способа путем повторной выборки данных, заполнения всех дней и последующего объединения количественных значений с исходными данными.

# Create a temporary df with all in between days filled in with zeros
filled = df.set_index("dt_op").groupby("cod_id") \
           .resample("D").asfreq().fillna(0) \
           .quantity.to_frame()

# Reverse and sum
filled["quant_sum"] = filled.reset_index().set_index("dt_op") \
                            .iloc[::-1] \
                            .groupby("cod_id") \
                            .rolling(7, min_periods=1) \
                            .quantity.sum().astype(int)

# Join with original `df`, dropping the filled days
result = df.set_index(["cod_id", "dt_op"]).join(filled.quant_sum).reset_index()
0 голосов
/ 16 октября 2018

Редактировать 181017: этот подход не работает из-за функций прямой прокрутки в разреженных временных рядах , в настоящее время не поддерживаемых пандами, см. Комментарии.

Использование циклов for может привести к снижению производительности при выполнении операций с пандами.

Цикл for вокруг строк и их временная дельта в 7 дней можно заменить на .rolling("7D").Чтобы получить скользящую в прямом направлении дельту времени (текущая дата + 7 дней), мы инвертируем df по дате, как показано здесь .

Тогда пользовательская функция больше не требуется, иВы можете просто взять .quantity.sum() из группы.

quant_sum = df.sort_values("dt_op", ascending=False).groupby("cod_id") \
              .rolling("7D", on="dt_op").quantity.sum()

cod_id  dt_op     
611     2018-01-21    8.0
613     2018-01-21    1.0
        2018-01-20    2.0
Name: quantity, dtype: float64

result = df.set_index(["cod_id", "dt_op"])
result["final_sum"] = quant_sum
result.reset_index()

   cod_id      dt_op  quantity  final_sum
0     613 2018-01-20         1        2.0
1     611 2018-01-21         8        8.0
2     613 2018-01-21         1        1.0
...