Pandas cumsum с условным продуктом запаздывания?

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

Вот пример установки:

import pandas as pd df = pd.DataFrame([1,1,1.004878,1,1.043394],columns=['xx']) df['n'] = 1000000.0 

Кто собирается:

  xx n 0 1.000000 1000000 1 1.000000 1000000 2 1.004878 1000000 3 1.000000 1000000 4 1.043394 1000000 

Теперь нам нужно умножить xx на отстающее значение n , итеративно, а затем взять кумулятивную сумму этого значения:

 cs = pd.Series([0.0] * len(df)) cs[0] = df.ix[0]['n'] for i,e in enumerate(df.iterrows()): if i == 0: continue cs[i] = df.ix[i]['xx'] * cs[(i - 1)] 

Это дает следующее:

 0 1000000.000000 1 1000000.000000 2 1004878.000000 3 1004878.000000 4 1048483.675932 dtype: float64 

Вопрос: Есть ли способ сделать это в pandas / numpy, который не требует итерации по каждой строке? Если нет, есть ли какие-либо трюки в торговле для оптимизации кода, как указано выше, когда вы вынуждены итерации? Может ли креативно созданный индекс помочь в этом случае? Производительность – это проблема со 10000+ строками, через несколько наборов данных.

Во-первых, ваш цикл for может быть упрощен до:

 for i in xrange(1, len(df)): cs[i] = df.ix[i]['xx'] * cs[(i - 1)] 

(больше math gibberish) Каждый элемент в cs[1:] является произведением всех предыдущих элементов в df['xx'] (кумулятивный продукт), умноженный на первый элемент в n столбце df

 >>> df xx n 0 1.000000 1000000 1 1.000000 1000000 2 1.004878 1000000 3 1.000000 1000000 4 1.043394 1000000 >>> a = df['xx'] >>> a 0 1.000000 1 1.000000 2 1.004878 3 1.000000 4 1.043394 Name: xx, dtype: float64 >>> a = a.cumprod() >>> a 0 1.000000 1 1.000000 2 1.004878 3 1.004878 4 1.048484 Name: xx, dtype: float64 >>> a = a * df['n'][0] >>> a 0 1000000.000000 1 1000000.000000 2 1004878.000000 3 1004878.000000 4 1048483.675932 Name: xx, dtype: float64 >>> np.all(a == cs) True >>> a = df['xx'].cumprod() * df['n'][0] 

Это не трюк. Это работает только потому, что df['xx'][0] равно 1. Если это было какое-то другое значение, AND cs[0] = df.ix[0]['n'] был не просто ярлыком, а cumprod would not Работа.

Расширение каждого элемента cs дает

 cs[0] = df['n'][0] cs[1] = df['xx'][1] * df['n'][0] cs[2] = df['xx'][2] * df['xx'][1] * df['n'][0] cs[3] = df['xx'][3] * df['xx'][2] * df['xx'][1] * df['n'][0] cs[4] = df['xx'][4] * df['xx'][3] * df['xx'][2] * df['xx'][1] * df['n'][0] 

Поскольку df['xx'][0] равно одному и df['xx'][0] * df['n'][0] == df['n'][0] то:

 cs[0] = df['xx'][0] * df['n'][0] cs[1] = df['xx'][1] * df['xx'][0] * df['n'][0] cs[2] = df['xx'][2] * df['xx'][1] * df['xx'][0] * df['n'][0] cs[3] = df['xx'][3] * df['xx'][2] * df['xx'][1] * df['xx'][0] * df['n'][0] cs[4] = df['xx'][4] * df['xx'][3] * df['xx'][2] * df['xx'][1] * df['xx'][0] * df['n'][0] 

Если бы вы немного изменили условия проблемы, где после каждой итерации мне нужно было вычесть 0,05% от последнего вычисленного значения n (до следующей итерации), работает ли cumprod?

Если вы выполнили упражнение по расширению элемента, вы должны были увидеть, что новое условие приводит к умножению на кумулятивный продукт массива масштабирующего коэффициента. Два способа сделать это – оба результата приводят к некоторым незначительным ошибкам с плавающей запятой из вычисленного в цикле вычисления. Опять же, вам нужно учитывать первый элемент в df['xx'] являющийся одним.

 for i in xrange(1, len(df)): cs[i] = df.ix[i]['xx'] * (.9995 * cs[(i - 1)]) >>> k array([ 1. , 0.9995, 0.9995, 0.9995, 0.9995]) >>> z = df['xx'] * k >>> z 0 1.000000 1 0.999500 2 1.004376 3 0.999500 4 1.042872 Name: xx, dtype: float64 >>> z = z.cumprod() * df['n'][0] >>> cs - z 0 0.000000e+00 1 0.000000e+00 2 0.000000e+00 3 0.000000e+00 4 -1.164153e-10 dtype: float64 >>> >>> z = df['xx'].cumprod() * df['n'][0] >>> z *= k.cumprod() >>> cs - z 0 0.000000e+00 1 0.000000e+00 2 -1.164153e-10 3 0.000000e+00 4 0.000000e+00 dtype: float64 >>> 

Я не уверен, что понимаю, что «n» предполагается делать (это всегда = 1 000 000?), Но это довольно просто, чтобы соответствовать вашим результатам выше с помощью cumprod:

 In [60]: df.xx.cumprod() * 1e6 Out[60]: 0 1000000.000000 1 1000000.000000 2 1004878.000000 3 1004878.000000 4 1048483.675932