У меня есть пандас dataframe с двумя столбцами, как это,

Item    Value
0   A   7
1   A   2
2   A   -6
3   A   -70
4   A   8
5   A   0

Я хочу накапливать сумму по столбцу, Value. Но при создании кумулятивной суммы, если значение становится отрицательным, я хочу сбросить его обратно на 0.

В настоящее время я использую цикл, показанный ниже, чтобы выполнить это,

sum_ = 0
cumsum = []

for val in sample['Value'].values:
    sum_ += val
    if sum_ < 0:
        sum_ = 0
    cumsum.append(sum_)

print(cumsum) # [7, 9, 3, 0, 8, 8]

Я ищу более эффективный способ сделать это в чистых пандах.

14
Sreeram TP 15 Авг 2019 в 16:44

2 ответа

Лучший ответ

Немного измените также этот метод медленно, что numba

sumlm = np.frompyfunc(lambda a,b: 0 if a+b < 0 else a+b,2,1)
newx=sumlm.accumulate(df.Value.values, dtype=np.object)
newx
Out[147]: array([7, 9, 3, 0, 8, 8], dtype=object)

numba решение

from numba import njit
@njit
def cumli(x, lim):
    total = 0
    result = []
    for i, y in enumerate(x):
        total += y
        if total < lim:
            total = 0
        result.append(total)
    return result
cumli(df.Value.values,0)
Out[166]: [7, 9, 3, 0, 8, 8]
7
YO and BEN_W 15 Авг 2019 в 14:51

Это всего лишь комментарий WeNYoBen.

Если вы можете избежать списков, обычно рекомендуется избегать этого.

Пример

from numba import njit
import numpy as np

#with lists
@njit()
def cumli(x, lim):
    total = 0
    result = []
    for i, y in enumerate(x):
        total += y
        if total < lim:
            total = 0
        result.append(total)
    return result

#without lists
@njit()
def cumli_2(x, lim):
    total = 0.
    result = np.empty_like(x)
    for i, y in enumerate(x):
        total += y
        if total < lim:
            total = 0.
        result[i]=total
    return result

Время

Без Numba (комментарий @ njit ()):

x=(np.random.rand(1_000)-0.5)*5

  %timeit a=cumli(x, 0.)
  220 µs ± 2.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
  %timeit a=cumli_2(x, 0.)
  227 µs ± 1.95 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Нет разницы между использованием списков или массивов. Но это не тот случай, если вы Jit-компилируете эту функцию.

С Numba:

  %timeit a=cumli(x, 0.)
  27.4 µs ± 210 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
  %timeit a=cumli_2(x, 0.)
  2.96 µs ± 32.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

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

1
max9111 16 Авг 2019 в 08:46