Как улучшить производительность в среднем вычисления в Python Dataframe - PullRequest
2 голосов
/ 31 октября 2019

Я пытаюсь улучшить производительность текущего фрагмента кода, в результате чего я циклически перебираю фрейм данных (фрейм данных 'r') и нахожу средние значения из другого фрейма данных (фрейма данных 'p') на основе критериев.

Я хочу найти среднее значение всех значений (столбец 'Val') из фрейма данных 'p', где (r.RefDate = p.RefDate) & (r.Item = p.Item) & (p.StartDate> =r.StartDate) & (p.EndDate <= r.EndDate) </p>

Для этого можно создать фиктивные данные, как показано ниже;

import pandas as pd
import numpy as np
from datetime import datetime


######### START CREATION OF DUMMY DATA ##########

rng = pd.date_range('2019-01-01', '2019-10-28')
daily_range = pd.date_range('2019-01-01','2019-12-31')

p = pd.DataFrame(columns=['RefDate','Item','StartDate','EndDate','Val'])
for item in ['A','B','C','D']:
    for date in daily_range:
        daily_p = pd.DataFrame({ 'RefDate': rng,
                            'Item':item, 
                            'StartDate':date,
                            'EndDate':date,
                            'Val' : np.random.randint(0,100,len(rng))}) 
        p = p.append(daily_p)


r = pd.DataFrame(columns=['RefDate','Item','PeriodStartDate','PeriodEndDate','AvgVal'])
for item in ['A','B','C','D']:
    r1 = pd.DataFrame({ 'RefDate': rng,
                        'Item':item, 
                        'PeriodStartDate':'2019-10-25',
                        'PeriodEndDate':'2019-10-31',#datetime(2019,10,31),
                        'AvgVal' : 0}) 
    r = r.append(r1)
r.reset_index(drop=True,inplace=True)

######### END CREATION OF DUMMY DATA ##########

Часть кода, которую я сейчас вычисляюи хотел бы улучшить производительность следующим образом

for i in r.index:

    avg_price = p['Val'].loc[((p['StartDate'] >= r.loc[i]['PeriodStartDate']) & 
                         (p['EndDate'] <= r.loc[i]['PeriodEndDate']) &
                         (p['RefDate'] == r.loc[i]['RefDate']) &
                         (p['Item'] == r.loc[i]['Item']))].mean()

    r['AvgVal'].loc[i] = avg_price

Ответы [ 2 ]

1 голос
/ 01 ноября 2019

Первое изменение заключается в том, что при генерации r DataFrame оба значения PeriodStartDate и PeriodEndDate создаются как datetime , см. Следующий фрагментВаш код инициализации, измененный мной:

r1 = pd.DataFrame({'RefDate': rng, 'Item':item,
    'PeriodStartDate': pd.to_datetime('2019-10-25'),
    'PeriodEndDate': pd.to_datetime('2019-10-31'), 'AvgVal': 0}) 

Чтобы повысить скорость, я установил индекс в обоих фреймах данных равным RefDate и Item (оба столбца сравниваются наравенство) и отсортировано по индексу:

p.set_index(['RefDate', 'Item'], inplace=True)
p.sort_index(inplace=True)
r.set_index(['RefDate', 'Item'], inplace=True)
r.sort_index(inplace=True)

Таким образом, доступ по индексу значительно быстрее.

Затем я определил следующую функцию, вычисляющую среднее значение для строк из p"связано с" текущей строкой из r :

def myMean(row):
    pp = p.loc[row.name]
    return pp[pp.StartDate.ge(row.PeriodStartDate) &
        pp.EndDate.le(row.PeriodEndDate)].Val.mean()

И единственное, что нужно сделать, это применить эту функцию (к каждой строке в r ) и сохраните результат в AvgVal :

r.AvgVal = r.apply(myMean2, axis=1)

Используя % timeit , я сравнил время выполнения кода, предложенного EdH с моим и получил результат почти в 10 раз короче.

Проверьте сами.

0 голосов
/ 31 октября 2019

С помощью iterrows мне удалось улучшить производительность, хотя все еще могут быть более быстрые способы.

for index, row in r.iterrows():      
    avg_price = p['Val'].loc[((p['StartDate'] >= row.PeriodStartDate) & 
                         (p['EndDate'] <= row.PeriodEndDate) &
                         (p['RefDate'] == row.RefDate) &
                         (p['Item'] == row.Item))].mean()

    r.loc[index, 'AvgVal'] = avg_price
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...