(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