【问题标题】:Recursive formula is slow with a loop, is there a way to make this code run faster?递归公式循环很慢,有没有办法让这段代码运行得更快?
【发布时间】:2020-03-19 20:14:29
【问题描述】:

我有以下数据集:

危险率的计算公式为:

For Year = 1: Hazard_rate(Year) = PD(Year)

For Year > 1: Hazard_rate(Year) = (PD(Year) + Hazard_rate(Year - 1) * (Year - 1)) / (Year)

假设: 按customer_ID,年份单调且严格> 0

由于这个公式是递归的,并且需要前一年的危险率,我的下面的代码很慢,并且在大型数据集下变得难以管理,有没有办法可以向量化这个操作或至少使循环更快?

#Calculate the hazard rates
#Initialise an array to collect the hazard rate for each calculation, particularly useful for the recursive nature 
#of the formula
hr = []

#Loop through the dataframe, executing the hazard rate formula
    #If time_period (year) = 1 then the hazard rate is equal to the pd
for index, row in df.iterrows():
    if row["Year"] == 1:
        hr.append(row["PD"])
    elif row["Year"] > 1:
        #Create a row_num variable to indicate what the index is for each unique customer ID
        row_num = int(row["Year"])
        hr.append((row["PD"] + hr[row_num - 2] * (row["Year"] - 1)) / (row["Year"]))
    else:
        raise ValueError("Index contains negative or zero values")

#Attach the hazard_rates array to the dataframe
df["hazard_rate"] = hr

【问题讨论】:

  • 只是为了澄清一下:你一开始说的数据集就是你想要计算的,而你的数据框只有yearPD列开始?
  • 使用df.loc[index, 'hazard_rate'] = *formula results*而不是使用列表会有所帮助吗?
  • FBruzzesi,正确 - 我添加了危险率列以供人们验证他们的结果
  • Aryerez,我过去曾尝试使用 .loc。但是,由于公式需要以前的结果,我无法让它工作。能给我看看吗?
  • 数据将按年份排序,年份之间严格没有间隔,并且严格没有 0 或负年份,因为这些是预测年份

标签: python pandas loops for-loop vectorization


【解决方案1】:

此函数将计算第 n 个危险率

computed = {1: 0.05}
def func(n, computed = computed):
    '''
    Parameters:
        @n: int, year number
        @computed: dictionary with hazard rate already computed
    Returns:
        computed[n]: n-th hazard rate
    '''

    if n not in computed:
        computed[n] = (df.loc[n,'PD'] + func(n-1, computed)*(n-1))/n

    return computed[n]

现在让我们计算每年的危险率:

df.set_index('year', inplace=True)
df['Hazard_rate'] = [func(i) for i in df.index]

请注意,该函数不关心数据帧是否按year 排序,但是我假设数据帧由year 索引。

如果您想恢复该列,只需重置索引:

df.reset_index(inplace=True)

随着Customer_ID的引入,流程复杂度更高:

#Function depends upon dataframe passed as argument
def func(df, n, computed):

    if n not in computed:
        computed[n] = (df.loc[n,'PD'] + func(n-1, computed)*(n-1))/n

    return computed[n]

#Set index
df.set_index('year', inplace=True)

#Initialize Hazard_rate column
df['Hazard_rate']=0

#Iterate over each customer
for c in df['Customer_ID']:

    #Create a customer mask
    c_mask = (df['Customer_ID'] == c)

    # Initialize computed dictionary for given customer
    c_computed = {1: df.loc[c_mask].loc[1,'PD']}

    df.loc[c_mask]['Hazard_rate'] = [func(df.loc[c_mask], i, c_computed ) for i in df.loc[c_mask].index]

【讨论】:

  • 由于您现在引入了一个新变量Customer_ID,因此上述代码将无法按预期工作
  • 如果不自己检查,看起来你的功能会比 OP 的差很多,因为你从头开始重新计算每个 year 的整个路径,而他使用上一年的计算结果。
  • 我可以在一个 ID 上运行并遍历每个 ID
  • @Aryerez 每年计算一次,它不再计算它。这是一种典型的递归方式(例如,这是在 python 中计算斐波那契数的最快方式,参见link
  • @78282219 只需在每个循环中重新初始化函数,因为该函数还会初始化存储已计算内容的字典
猜你喜欢
  • 2021-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-19
  • 1970-01-01
  • 2020-08-22
相关资源
最近更新 更多