Python 加快计算回报

Python 加快计算回报,python,pandas,Python,Pandas,我正在使用python 2.7。我希望从每日收益计算复合收益,而我目前的代码在计算收益方面相当慢,所以我一直在寻找可以提高效率的领域 我想做的是将两个日期和一个证券传递到一个价格表中,并使用给定证券计算这些日期之间的复合收益 我有一个价格表(prices\u df): 我还有一个带有两个日期和安全性的表(events\u df): 使用此表中的两个日期,我想使用价格表来计算回报 我正在使用的两个功能: import pandas as pd # compounds returns def cum

我正在使用python 2.7。我希望从每日收益计算复合收益,而我目前的代码在计算收益方面相当慢,所以我一直在寻找可以提高效率的领域

我想做的是将两个日期和一个证券传递到一个价格表中,并使用给定证券计算这些日期之间的复合收益

我有一个价格表(
prices\u df
):

我还有一个带有两个日期和安全性的表(
events\u df
):

使用此表中的两个日期,我想使用价格表来计算回报

我正在使用的两个功能:

import pandas as pd
# compounds returns
def cum_rtrn(df):
    df_out = df.add(1).cumprod()
    df_out['return'].iat[0] = 1
    return df_out

# calculates compound returns from prices between two dates
def calc_comp_returns(price_df, start_date=None, end_date=None, security=None):
    df = price_df[price_df.security_id == security]
    df = df.set_index(['asof'])
    df = df.loc[start_date:end_date]
    df['return'] = df.px_last.pct_change()
    df = df[['return']]
    df = cum_rtrn(df)
    return df.iloc[-1][0]
然后,我用
迭代
事件df
。iterrows
每次传递
calc\u comp\u返回
函数。然而,这是一个非常缓慢的过程,因为我有10个以上的迭代,所以我正在寻求改进。解决方案不需要基于
pandas

# example of how function is called
start = datetime.datetime.strptime('2015-01-05', '%Y-%m-%d').date()
end = datetime.datetime.strptime('2015-01-09', '%Y-%m-%d').date()
calc_comp_returns(prices_df, start_date=start, end_date=end, security=1)

我对熊猫不是很熟悉,但我会试试看

你的解决方案有问题吗 您的解决方案目前会进行大量不必要的计算。这主要是因为:

    df['return'] = df.px_last.pct_change()
这一行实际上是在计算开始和结束之间每个日期的百分比变化。只要解决这个问题就可以大大加快速度。你应该得到起始价和结束价,然后比较两者。这两个价格之间的价格与你的计算完全无关。再说一遍,我对熊猫的熟悉度为零,但你应该这样做:

def calc_comp_returns(price_df, start_date=None, end_date=None, security=None):
    df = price_df[price_df.security_id == security]
    df = df.set_index(['asof'])
    df = df.loc[start_date:end_date]
    return 1 + (df['px_last'].iloc(-1) - df['px_last'].iloc(0)
请记住,此代码依赖于price_df是按日期排序的这一事实,因此请小心确保您只传递
calc_comp_\u returns
a date sorted price_df。

这里有一个解决方案(在我的计算机上,使用一些虚拟数据时,速度要快100倍)

或者,作为一个班轮:

 def calc_comp_returns_fast(price_df, start_date, end_date, security):
    return np.prod(price_df[price_df.security_id == security].loc[start_date:end_date].px_last.pct_change() + 1)
并不是说我事先调用了
set_index
方法,它只需要在整个
price_df
数据帧上执行一次

它更快,因为它不会在每一步重新创建数据帧。在您的代码中,
df
几乎在每一行都被一个新的数据帧覆盖。init进程和垃圾收集(从内存中擦除未使用的数据)都需要大量时间

在我的代码中,
是原始数据的切片或“视图”,它不需要复制或重新初始化任何对象。此外,我还直接使用了numpy product函数,这与使用最后一个cumprod元素相同(pandas在内部使用
np.cumprod


建议:如果您正在使用IPython、Jupyter或Spyder,您可以使用magic
%prun-calc\u-comp\u-returns(…)
查看哪个部分花费的时间最多。我在你的代码上运行了它,它是垃圾收集器,占用了总运行时间的50%以上

我们将使用
pd.merge\u asof
prices\u df
中获取价格。然而,当我们这样做时,我们需要按照我们正在使用的日期列对相关的数据帧进行排序。此外,为了方便起见,我将在字典中聚合一些
pd.merge\u asof
参数,用作关键字参数

prices_df = prices_df.sort_values(['asof'])

aed = events_df.sort_values('asof')
ded = events_df.sort_values('disclosed_on')

aokw = dict(
    left_on='asof', right_on='asof',
    left_by='security_ref_id', right_by='security_id'
)

start_price = pd.merge_asof(aed, prices_df, **aokw).px_last

dokw = dict(
    left_on='disclosed_on', right_on='asof',
    left_by='security_ref_id', right_by='security_id'
)

end_price = pd.merge_asof(ded, prices_df, **dokw).px_last

returns = end_price.div(start_price).sub(1).rename('return')
events_df.join(returns)

        asof        disclosed_on  security_ref_id    return
0 2015-01-05 2015-01-09 16:31:00                1  0.040816
1 2018-03-22 2018-03-27 16:33:00             3616       NaN
2 2017-08-03 2018-03-27 12:13:00             2591       NaN
3 2018-03-22 2018-03-27 11:33:00             3615       NaN
4 2018-03-22 2018-03-27 10:51:00             3615       NaN

从不知道
%prun
的神奇功能。这真的很有用。谢谢
import numpy as np

price_df = price_df.set_index('asof')

def calc_comp_returns_fast(price_df, start_date, end_date, security):
    rows = price_df[price_df.security_id == security].loc[start_date:end_date]
    changes = rows.px_last.pct_change()
    comp_rtrn = np.prod(changes + 1)
    return comp_rtrn
 def calc_comp_returns_fast(price_df, start_date, end_date, security):
    return np.prod(price_df[price_df.security_id == security].loc[start_date:end_date].px_last.pct_change() + 1)
prices_df = prices_df.sort_values(['asof'])

aed = events_df.sort_values('asof')
ded = events_df.sort_values('disclosed_on')

aokw = dict(
    left_on='asof', right_on='asof',
    left_by='security_ref_id', right_by='security_id'
)

start_price = pd.merge_asof(aed, prices_df, **aokw).px_last

dokw = dict(
    left_on='disclosed_on', right_on='asof',
    left_by='security_ref_id', right_by='security_id'
)

end_price = pd.merge_asof(ded, prices_df, **dokw).px_last

returns = end_price.div(start_price).sub(1).rename('return')
events_df.join(returns)

        asof        disclosed_on  security_ref_id    return
0 2015-01-05 2015-01-09 16:31:00                1  0.040816
1 2018-03-22 2018-03-27 16:33:00             3616       NaN
2 2017-08-03 2018-03-27 12:13:00             2591       NaN
3 2018-03-22 2018-03-27 11:33:00             3615       NaN
4 2018-03-22 2018-03-27 10:51:00             3615       NaN