Низкая производительность при использовании .head () с пандами в цикле - PullRequest
0 голосов
/ 08 октября 2018

Я был поражен, обнаружив, какое снижение производительности я испытал при использовании цикла for и метода .head() в пандах.Я ищу какой-либо совет по моей проблеме, который я иллюстрирую ниже.

import pandas as pd
import numpy as np

#create dictionary of 10000 dataframes
numdfs = 10000

alldf = {i:pd.DataFrame({'a':np.random.randn(250),'b':np.random.randn(250),'c':np.random.randn(250),'d':np.random.randn(250)}) for i in range(numdfs)}

count = 250
runningsum = 0
for i in range(numdfs):
    df = alldf[i].head(count)
    df['is negative'] = (df['b'] < 0).cummax().astype(int)
    runningsum += df['is negative'].max()

Приведенный выше код занимает около 15 минут для выполнения на моей машине, удаляя .head(count) из первой строки послецикл заставляет его выполнить менее чем за минуту!

Может кто-нибудь поделиться какой-то точки зрения, почему это так, и как я могу повысить производительность?.head () используется, потому что в реальном примере я хочу оценить условие до потенциально различного числа выборок в кадрах данных

1 Ответ

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

(df['b'] < 0).cummax().astype(int).max() просто проверяет, является ли какое-либо значение меньше 0. Вы можете использовать (df['b'] < 0).any() вместо этого.Также нет необходимости в преобразовании int, поскольку логические значения считаются 1 / 0 для True / False соответственно.

В качестве примечания, loc / iloc имеют тенденциючтобы быть более эффективным, чем другие формы нарезки, но это не является основной причиной низкой производительности, несмотря на ваши тесты.

Вы можете использовать выражение генератора с sum для эквивалентного алгоритма:

sum((v.loc[:250, 'b'] < 0).any() for v in alldf.values())

Вот некоторые тесты производительности:

np.random.seed(0)  # use this to reproduce results
numdfs = 10**2     # create 100 dataframes

alldf = {i: pd.DataFrame({col: np.random.randn(250) for col in 'abcd'}) \
         for i in range(numdfs)}

def jpp(alldf):
    return sum((v.loc[:250, 'b'] < 0).any() for v in alldf.values())

def original(alldf):
    count=250
    runningsum = 0
    for i in range(len(alldf)):
        df = alldf[i].head(count)
        df['is negative'] = (df['b'] < 0).cummax().astype(int)
        runningsum += df['is negative'].max()
    return runningsum

assert jpp(alldf) == original(alldf)

%timeit jpp(alldf)       # 46.6 ms
%timeit original(alldf)  # 6.46 s

Алгоритм все еще довольно медленный, так как вы без необходимости проверяете каждое единственное значение в 'b', когда возможно сократитьсхема, если найдено значение меньше нуля.Циклический путь возможен с numba, который улучшает исходный алгоритм в ~ 12 000 раз.

from numba import njit

@njit
def any_below_zero(arr, k):
    for i in range(k):
        if arr[i] < 0:
            return 1
    return 0

def jpp_nb(alldf):
    return sum(any_below_zero(v['b'].values, 250) for v in alldf.values())

%timeit jpp_nb(alldf)    # 525 µs

Для 10000 кадров данных, как в ваших тестах, это работает менее чем за секунду:

numdfs = 10**5     # create 10000 dataframes
alldf = {i: pd.DataFrame({col: np.random.randn(250) for col in 'abcd'}) \
         for i in range(numdfs)}

%timeit jpp_nb(alldf)    # 746 ms
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...