首页 > 解决方案 > 有没有办法使用 scipy.optimize.minimize 和 2D np 数组作为 x0?

问题描述

我是 python 提供的优化技术的新手,我找不到如何根据我的需要从 scipy 调整 optimize.minimize 的方法。

我想做的是通过改变利率来最小化给定债券集的市场价格和模型价格之间的平方差之和(所以利率是我的 x0)。这个总和是通过 price_eval 计算的,它使用几个列表或嵌套列表作为参数,例如每个债券的现金流量的嵌套列表。为了让后者更清楚——如果我有 3 只债券,它们有 3、2 和 4 条未来现金流量,例如 a、b、c;d,e; h;i;j;k 分别是,参数之一是列表 CF_list = [[a,b,c],[d,e],[h;i;j;kl]]。

这是一个函数:

def price_eval(rates_list, ttm_list, price_list, CF_list, CF_time_list, N_cf_list):

    price_model_list = []
    sum_squares = 0

    for i in range(0, len(price_list)):
        
        price = 0
        number_cf_per_bond = N_cf_list[i]
        
        for j in range(0, number_cf_per_bond):
            
            price += (CF_list[i][j]) / math.pow(1+rates_list[i][j],CF_time_list[i][j])
   
        diff = price_model_list[i] - price_list[i]
        sum_squares += math.pow(diff,2)

    return sum_squares

我试图将求解器定义为

res = scipy.optimize.minimize(price_eval, rates_guess, args=(ttm_list, price_list, CF_list, CF_time_list, N_cf_list))

我提供嵌套列表 rates_guess 作为求解器的 rates_list[i][j] 的初始猜测(是否将其更改为 numpy 数组并不重要,给出相同的结果)。整个问题可能来自 scipy.optimize.minimize 仅将一维数组作为 x0 的事实,而在我的情况下,x0 需要是二维的,因为每个债券每个现金流都存储了数据。我收到一个错误

...
grad[k] = (f(*((xk + d,) + args)) - f0) / d[k]

TypeError: can only concatenate list (not "float") to list

所以,我的问题是是否有人知道如何解决这个问题?或者是否有可能以某种方式调整求解器,我可以提供一个嵌套列表/2D np 数组作为 x0,它应该以相同格式返回新值?如果没有,我还能做什么?

非常感谢您的帮助!

标签: scipynested-listssolverscipy-optimizescipy-optimize-minimize

解决方案


两者都rates_list需要rates_guess是一维数组/列表。我通常处理这个问题的方法是使用将您喜欢的结构扁平化为列表的函数,并将其“膨胀”回所需的形状。所以你可以有,例如,像这样的东西

def to_flat(nested_list):
    '''
    flatten a list of lists
    '''
    flat_list = []
    for l in nested_list:
        for e in l:
            flat_list.append(e)
    return flat_list

# test
rates_nested_list = [[1,1,1],[2,2],[3,3,3,3]]
rates_flat_list = to_flat(rates_nested_list)
print('flat: ',rates_flat_list)

def from_flat(flat_list, list_to_copy_shape_from):
    ''' 
    here the second argument is a nested list in the desired shape -- values not relevant only shape
    '''
    nested_list = []
    idx = 0
    for l in list_to_copy_shape_from:
        nested_list.append([])
        for e in l:
            nested_list[-1].append(flat_list[idx])
            idx += 1
    return nested_list

# test
template = [[0,0,0],[0,0],[0,0,0,0]]
rates_nested_list2 = from_flat(rates_flat_list, template)
print('nested:',rates_nested_list2)

检查输出:

flat:  [1, 1, 1, 2, 2, 3, 3, 3, 3]
nested: [[1, 1, 1], [2, 2], [3, 3, 3, 3]]

现在在您的price_eval函数中,优化器将为您提供一个扁平的费率列表,您需要“膨胀”它,所以您的代码看起来像这样

def price_eval(rates_flat_list, ttm_list, price_list, CF_list, CF_time_list, N_cf_list):

    # create a nested list of rates from a flat one, using CF_list as a template
    rates_flat_list = from_flat(rates_flat_list,CF_list)

    price_model_list = []
    sum_squares = 0

    for i in range(0, len(price_list)):
        
        price = 0
        number_cf_per_bond = N_cf_list[i]
        
        for j in range(0, number_cf_per_bond):
            
            price += (CF_list[i][j]) / math.pow(1+rates_list[i][j],CF_time_list[i][j])

        # !!! I think your code is missing the following line !!!
        price_model_list.append(price)
        diff = price_model_list[i] - price_list[i]
        sum_squares += math.pow(diff,2)

    return sum_squares

现在,当您调用优化器时,x0您需要传递一个扁平列表,因此假设rates_guess您将调用一个嵌套列表

res = scipy.optimize.minimize(price_eval, x0 = to_flat(rates_guess), args=(ttm_list, price_list, CF_list, CF_time_list, N_cf_list))

最后,当你得到res它时,它会有一个平面列表的解决方案,所以你可能想from_flat再次调用它

最后,由于无论如何您都在进行最小二乘拟合,您可能想要使用scipy.optimize.least_squares而不是最小化 - 它具有几乎相同的输入,但应该更好地拟合


推荐阅读