Повышение скорости определения конечной стоимости портфеля после расходования фиксированной ставки - PullRequest
0 голосов
/ 26 ноября 2018

У меня есть pd.DataFrame ряд возврата, соответствующий годам с фиксированной ставкой расходов 5%.Я ищу, чтобы найти конечную стоимость портфеля после расходов на каждый год.val_after_spending в год t равно среднему году t val_before_spending с годом t-1 val_after_spending, умноженным на норму расходов.В первый год val_after_spending в t-1 предполагается равным 1.

У меня сейчас есть рабочая реализация (ниже), но она невероятно медленная.Есть ли более быстрый способ реализовать это?

import pandas as pd
import numpy as np   
port_rets = pd.DataFrame({'port_ret': [.10,-.25,.15]})

spending_rate = .05

for index, row in port_rets.iterrows():
    if index != 0:
        port_rets.at[index, 'val_before_spending'] = port_rets['val_after_spending'][index - 1] * (1 + port_rets['port_ret'][index])
        port_rets.at[index, 'spending'] = np.mean([port_rets['val_after_spending'][index - 1], port_rets['val_before_spending'][index]]) * spending_rate 
    else:
        port_rets.at[index, 'val_before_spending'] = 1 * (1 + port_rets['port_ret'][index])
        port_rets.at[index, 'spending'] = np.mean([1, port_rets['val_before_spending'][index]]) * spending_rate

    port_rets.at[index, 'val_after_spending'] = port_rets['val_before_spending'][index] - port_rets['spending'][index]

#   port_ret    val_before_spending spending    val_after_spending
#0  0.100000    1.100000            0.052500    1.047500
#1  -0.250000   0.785625            0.045828    0.739797
#2  0.150000    0.850766            0.039764    0.811002

1 Ответ

0 голосов
/ 26 ноября 2018

Вы очень сильно взаимодействуете с пандами в своем коде, что кажется плохой идеей с точки зрения производительности.Чтобы сделать его таким же простым в использовании, как и сейчас, pandas должен вести большой бухгалтерский учет, что приводит к снижению производительности.

Мы выполняем все расчеты по порядку, а затем, собрав все строительные блоки, в конце строим фрейм данных.Таким образом, код преобразуется в:

def get_vals(rates, spending_rate):
    n = len(rates)
    vals_after_spending = np.zeros((n+1, ))
    vals_before_spending = np.zeros((n+1, ))

    vals_after_spending[0] = 1.0

    for i in range(n):
        vals_before_spending[i+1] = vals_after_spending[i] * (1 + rates[i])

        spending = np.mean(np.array([vals_after_spending[i], vals_before_spending[i+1]])) * spending_rate
        vals_after_spending[i+1] = vals_before_spending[i+1] - spending

    return vals_before_spending[1:], vals_after_spending[1:]

rates = np.array(port_rets["port_ret"].tolist())
vals_before_spending, vals_after_spending = get_vals(rates, spending_rate)
port_rets = pd.DataFrame({'port_ret': rates, "val_before_spending": vals_before_spending, "val_after_spending": vals_after_spending})

Мы можем еще больше улучшить с помощью JIT-компиляции кода, поскольку петли Python работают медленно.Ниже я использую numba:

import numba as nb
@nb.njit(cache=True)  # as easy as putting this decorator
def get_vals(rates, spending_rate):
    n = len(rates)
    vals_after_spending = np.zeros((n+1, ))
    vals_before_spending = np.zeros((n+1, ))

    # ... code remains same, we are just compiling the function

Если мы рассмотрим случайный список показателей, подобный этому:

port_rets = pd.DataFrame({'port_ret': np.random.uniform(low=-1.0, high=1.0, size=(100000,))})

Мы получим сравнение производительности:

Вашкод: 15.758 с

get_vals: 1.407 с

JITed get_vals: 0,093 с (при втором запуске со скидкой на время компиляции)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...