Рекурсия: значение учетной записи с распределением

Обновление : не уверен, что это возможно без какой-либо формы цикла , но np.where не будет работать здесь. Если ответ: «вы не можете», то пусть будет так. Если это можно сделать, он может использовать что-то из scipy.signal .


Я хотел бы векторизовать цикл в коде ниже, но не уверен, как из-за рекурсивного характера вывода.

Прогулка, хотя из моей текущей настройки:

Возьмите стартовую сумму (1 миллион долларов США) и ежеквартальное распределение доллара (5000 долларов США):

 dist = 5000. v0 = float(1e6) 

Сгенерируйте случайную защиту / возврат учетной записи (десятичная форма) на ежемесячной частоте:

 r = pd.Series(np.random.rand(12) * .01, index=pd.date_range('2017', freq='M', periods=12)) 

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

 value = pd.Series(np.empty_like(r), index=r.index) 

Добавьте значение «начальный месяц». Эта метка будет содержать v0 .

 from pandas.tseries import offsets value = (value.append(Series(v0, index=[value.index[0] - offsets.MonthEnd(1)])) .sort_index()) 

Цикл, который я бы хотел избавиться, здесь:

 for date in value.index[1:]: if date.is_quarter_end: value.loc[date] = value.loc[date - offsets.MonthEnd(1)] \ * (1 + r.loc[date]) - dist else: value.loc[date] = value.loc[date - offsets.MonthEnd(1)] \ * (1 + r.loc[date]) 

Комбинированный код:

 import pandas as pd from pandas.tseries import offsets from pandas import Series import numpy as np dist = 5000. v0 = float(1e6) r = pd.Series(np.random.rand(12) * .01, index=pd.date_range('2017', freq='M', periods=12)) value = pd.Series(np.empty_like(r), index=r.index) value = (value.append(Series(v0, index=[value.index[0] - offsets.MonthEnd(1)])).sort_index()) for date in value.index[1:]: if date.is_quarter_end: value.loc[date] = value.loc[date - offsets.MonthEnd(1)] * (1 + r.loc[date]) - dist else: value.loc[date] = value.loc[date - offsets.MonthEnd(1)] * (1 + r.loc[date]) 

В psuedocode то, что делает цикл, это просто:

 for each date in index of value: if the date is not a quarter end: multiply previous value by (1 + r) for that month if the date is a quarter end: multiply previous value by (1 + r) for that month and subtract dist 

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

2 Solutions collect form web for “Рекурсия: значение учетной записи с распределением”

Вы можете использовать следующий код:

 cum_r = (1 + r).cumprod() result = cum_r * v0 for date in r.index[r.index.is_quarter_end]: result[date:] -= cum_r[date:] * (dist / cum_r.loc[date]) 

Вы бы сделали:

  • 1 совокупный продукт для всех ежемесячных возвратов.
  • 1 умножение вектора со скаляром v0
  • n векторное умножение со скалярным dist / cum_r.loc[date]
  • n векторных вычитаний

где n – количество концов квартала.

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

 cum_r = (1 + r).cumprod() t = (r.index.is_quarter_end / cum_r).cumsum() result = cum_r * (v0 - dist * t) 

который

  • 1 кумулятивный продукт (1 + r).cumprod()
  • 1 разделение между двумя рядами r.index.is_quarter_end / cum_r
  • 1 кумулятивная сумма вышеуказанного дивизиона
  • 1 умножение указанной суммы со скалярным dist
  • 1 вычитание скалярного v0 с dist * t
  • 1 точечное умножение cum_r с v0 - dist * t

Хорошо … Я принимаю удар.

 import numpy as np import pandas as pd #Define a generator for accumulating deposits and returns def gen(lst): acu = 0 for r, v in lst: yield acu * (1 + r) +v acu *= (1 + r) acu += v dist = 5000. v0 = float(1e6) random_returns = np.random.rand(12) * 0.1 #Create the index. index=pd.date_range('2016-12-31', freq='M', periods=13) #Generate a return so that the value at i equals the return from i-1 to i r = pd.Series(np.insert(random_returns, 0,0), index=index, name='Return') #Generate series with deposits and withdrawals w = [-dist if is_q_end else 0 for is_q_end in index [1:].is_quarter_end] d = pd.Series(np.insert(w, 0, v0), index=index, name='Movements') df = pd.concat([r, d], axis=1) df['Value'] = list(gen(zip(df['Return'], df['Movements']))) 

теперь, ваш код

 #Generate some random security/account returns (decimal form) at monthly freq: r = pd.Series(random_returns, index=pd.date_range('2017', freq='M', periods=12)) #Create an empty Series that will hold the monthly account values: value = pd.Series(np.empty_like(r), index=r.index) #Add a "start month" to value. This label will contain v0. from pandas.tseries import offsets value = (value.append(pd.Series(v0, index=[value.index[0] - offsets.MonthEnd(1)])).sort_index()) #The loop I'd like to get rid of is here: def loopy(value) : for date in value.index[1:]: if date.is_quarter_end: value.loc[date] = value.loc[date - offsets.MonthEnd(1)] \ * (1 + r.loc[date]) - dist else: value.loc[date] = value.loc[date - offsets.MonthEnd(1)] \ * (1 + r.loc[date]) return value 

и сравнение и выбор времени

 (loopy(value)==list(gen(zip(r, d)))).all() Out[11]: True 

возвращает тот же результат

 %timeit list(gen(zip(r, d))) %timeit loopy(value) 10000 loops, best of 3: 72.4 µs per loop 100 loops, best of 3: 5.37 ms per loop 

и выглядит несколько быстрее. Надеюсь, поможет.

  • Почему некоторый код детерминирован в Python2 и не является детерминированным в Python 3?
  • Кортеж для распаковки
  • Python 3 - Быстрая печать и ввод-вывод
  • Все еще нет wxPython для Python 3 (или 3.3)?
  • Python3 argparse set_defaults не принимает строку как имя параметра?
  • Как реализовать выбор Сортировка в списке?
  • Есть ли стандартный способ убедиться, что скрипт python будет интерпретироваться python2, а не python3?
  • <__ main__. объект в 0x02C08790> PYTHON
  • Приложение веб-сервера Bokeh в файле localhost для html
  • pyvenv-3.4 возвратил ненулевой статус выхода 1
  • tarfile сжатый модуль bz2 недоступен
  • Python - лучший язык программирования в мире.