Скользящая операция медленная производительность для создания нового столбца - PullRequest
1 голос
/ 09 мая 2019

Здравствуйте, я создал код, который имеет очень низкую производительность при создании нового столбца в кадре данных.Этот новый столбец представляет собой скользящее скользящее среднее с разными весами, которое я получаю из итерации цикла, но я думаю, что в Python может быть лучший / более быстрый способ сделать это.

  • Исходная база данных похожа на это (хотя и намного больше):

    idx = [np.array(['Jan', 'Jan', 'Feb', 'Mar', 'Mar', 'Mar','Apr', 'Apr', 'May', 'Jun', 'Jun', 'Jun','Jul', 'Aug', 'Aug', 'Sep', 'Sep', 'Oct','Oct', 'Oct', 'Nov', 'Dic', 'Dic',]),np.array(['A', 'B', 'B', 'A', 'B', 'C', 'A', 'B', 'B', 'A', 'B', 'C','A', 'B', 'C', 'A', 'B', 'C', 'A', 'B', 'A', 'B', 'C'])]
    data = [{'x': 1}, {'x': 5}, {'x': 3}, {'x': 2}, {'x': 7}, {'x': 3},{'x': 1}, {'x': 6}, {'x': 3}, {'x': 5}, {'x': 2}, {'x': 3},{'x': 1}, {'x': 9}, {'x': 3}, {'x': 2}, {'x': 7}, {'x': 3}, {'x': 6}, {'x': 8}, {'x': 2}, {'x': 7}, {'x': 9}]
    df = pd.DataFrame(data, index=idx, columns=['x'])
    df.index.names=['date','type']
    

И это выглядит так:

           x
date type
Jan  A     1
     B     5
Feb  B     3
Mar  A     2
     B     7
     C     3
Apr  A     1
     B     6
May  B     3
Jun  A     5
     B     2
     C     3
Jul  A     1
Aug  B     9
     C     3
Sep  A     2
     B     7
Oct  C     3
     A     6
     B     8
Nov  A     2
Dic  B     7
     C     9
  • Моя цель - улучшить следующий код, чтобы создать новый столбец в кадре данных (скользящее скользящее среднее с различными весами).Мой код:

    df=df.reset_index()
    df['rolling']=0
    for j in df['type'].unique():
        list_1=list(df['x'][df['type']==j])
        cumsum = [0]
        list_2=list(df['x'][df['type']==j].index)
        z=[]
        for i, h in enumerate(list_1, 1):
            if i>=4:
              cumsum.append(0.2*list_1[i-4]+0.3*list_1[i-3]+0.5*list_1[i-2])
            else:
              cumsum.append('NaN')
            cumsum.pop(0)
            z.append(cumsum[0])
        df['rolling'][list_2]=z
    

И это выглядит так:

   date type  x rolling
0   Jan    A  1     NaN
1   Jan    B  5     NaN
2   Feb    B  3     NaN
3   Mar    A  2     NaN
4   Mar    B  7     NaN
5   Mar    C  3     NaN
6   Apr    A  1     NaN
7   Apr    B  6     5.4
8   May    B  3     5.7
9   Jun    A  5     1.3
10  Jun    B  2     4.7
11  Jun    C  3     NaN
12  Jul    A  1     3.2
13  Aug    B  9     3.1
14  Aug    C  3     NaN
15  Sep    A  2     2.2
16  Sep    B  7     5.7
17  Oct    C  3       3
18  Oct    A  6     2.3
19  Oct    B  8     6.6
20  Nov    A  2     3.8
21  Dic    B  7     7.9
22  Dic    C  9       3

** Если у вас есть код, который имеет лучшую производительность, чем у меня, онбыло бы интересно узнать, насколько это быстрее.Если вы думаете, что ваш код лучше, но вы не знаете, насколько он быстрее, все равно опубликуйте его, потому что я это пойму с большим фреймом данных.Спасибо!

Ответы [ 2 ]

0 голосов
/ 09 мая 2019

Вот версия numpy.Это дает здоровое (68x на маленьком примере) ускорение.Поскольку он использует линейную корреляцию, этот коэффициент должен стать еще больше, если ваше реальное окно длиннее 3, потому что correlate переключится на более эффективный метод на основе fft.

import numpy as np
import pandas as pd
from scipy import signal

idx = [np.array(['Jan', 'Jan', 'Feb', 'Mar', 'Mar', 'Mar','Apr', 'Apr', 'May', 'Jun', 'Jun', 'Jun','Jul', 'Aug', 'Aug', 'Sep', 'Sep', 'Oct','Oct', 'Oct', 'Nov', 'Dic', 'Dic',]),np.array(['A', 'B', 'B', 'A', 'B', 'C', 'A', 'B', 'B', 'A', 'B', 'C','A', 'B', 'C', 'A', 'B', 'C', 'A', 'B', 'A', 'B', 'C'])]
data = [{'x': 1}, {'x': 5}, {'x': 3}, {'x': 2}, {'x': 7}, {'x': 3},{'x': 1}, {'x': 6}, {'x': 3}, {'x': 5}, {'x': 2}, {'x': 3},{'x': 1}, {'x': 9}, {'x': 3}, {'x': 2}, {'x': 7}, {'x': 3}, {'x': 6}, {'x': 8}, {'x': 2}, {'x': 7}, {'x': 9}]
df = pd.DataFrame(data, index=idx, columns=['x'])
df.index.names=['date','type']
df = df.reset_index()

weights = np.array((0.2,0.3,0.5))

def running_avg():
    if 'running' in df.columns:
        del df['running']
    n = len(weights)

    tp, x = df['type'].values, df['x'].values
    sidx = np.argsort(tp, kind='stable') 

    stp = tp[sidx]
    bnds = np.where(stp[1:] != stp[:-1])[0] + 1

    running = np.empty(sidx.shape)
    for bit in np.split(sidx, bnds):
        running[bit[:n]] = np.nan
        if len(bit) > n:
            running[bit[n:]] = signal.correlate(x[bit[:-1]], weights, 'valid', 'auto')
    df['running'] = running

def running_OP():
    df['rolling']=0
    for j in df['type'].unique():
        list_1=list(df['x'][df['type']==j])
        cumsum = [0]
        list_2=list(df['x'][df['type']==j].index)
        z=[]
        for i, h in enumerate(list_1, 1):
            if i>=4:
              cumsum.append(0.2*list_1[i-4]+0.3*list_1[i-3]+0.5*list_1[i-2])
            else:
              cumsum.append('NaN')
            cumsum.pop(0)
            z.append(cumsum[0])
        df['rolling'][list_2]=z

from timeit import repeat

T0 = np.array(repeat(running_OP, repeat=7, number=10))*100
print(f'\nOP: {T0.mean():.3f} ± {T0.std():.3f} ms')
T1 = np.array(repeat(running_avg, repeat=7, number=100))*10000
print(f'pp {T1.mean():.3f} ± {T1.std():.3f} \N{GREEK SMALL LETTER MU}s')

print("\nresults are " + ["different", "equal"][((np.isnan(df['running']) & np.isnan(df['rolling'].astype(float))) | (df['running'] == df['rolling'])).all()])
print(f'speedup roughly {T0.mean()/T1.mean()*1000:.0f}\N{MULTIPLICATION X}')

Пример выполнения:

OP: 62.500 ± 0.473 ms
pp 903.769 ± 11.491 μs

results are equal
speedup roughly 69✕
0 голосов
/ 09 мая 2019

Давайте попробуем это, чтобы увидеть, ускоряет ли это ваш код:

idx = [np.array(['Jan', 'Jan', 'Feb', 'Mar', 'Mar', 'Mar','Apr', 'Apr', 'May', 'Jun', 'Jun', 'Jun','Jul', 'Aug', 'Aug', 'Sep', 'Sep', 'Oct','Oct', 'Oct', 'Nov', 'Dic', 'Dic',]),np.array(['A', 'B', 'B', 'A', 'B', 'C', 'A', 'B', 'B', 'A', 'B', 'C','A', 'B', 'C', 'A', 'B', 'C', 'A', 'B', 'A', 'B', 'C'])]
data = [{'x': 1}, {'x': 5}, {'x': 3}, {'x': 2}, {'x': 7}, {'x': 3},{'x': 1}, {'x': 6}, {'x': 3}, {'x': 5}, {'x': 2}, {'x': 3},{'x': 1}, {'x': 9}, {'x': 3}, {'x': 2}, {'x': 7}, {'x': 3}, {'x': 6}, {'x': 8}, {'x': 2}, {'x': 7}, {'x': 9}]
df = pd.DataFrame(data, index=idx, columns=['x'])
df.index.names=['date','type']

df['rolling'] = df.groupby('type')['x'].rolling(4).apply(lambda x: x[-4]*.2 + x[-3]*.3 + x[-2]*.5, raw=True)\
                  .reset_index(level=2, drop=True).swaplevel(0,1)

df

Выход:

           x  rolling
date type            
Jan  A     1      NaN
     B     5      NaN
Feb  B     3      NaN
Mar  A     2      NaN
     B     7      NaN
     C     3      NaN
Apr  A     1      NaN
     B     6      5.4
May  B     3      5.7
Jun  A     5      1.3
     B     2      4.7
     C     3      NaN
Jul  A     1      3.2
Aug  B     9      3.1
     C     3      NaN
Sep  A     2      2.2
     B     7      5.7
Oct  C     3      3.0
     A     6      2.3
     B     8      6.6
Nov  A     2      3.8
Dic  B     7      7.9
     C     9      3.0

Сроки ....

Ваш код:

324 мс ± 1,55 мс на цикл (среднее ± стандартное отклонение из 7 циклов, по 1 циклу каждый)

Этот код:

12,6 мс ± 138 мкс на цикл (среднее ± стандартное отклонение из 7 циклов, по 100 циклов в каждом)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...