如何用Python生成与目标列表回归后R-squared最高的数值列表?
问题分析与解决方案
问题描述
给定一组数值列表,希望生成另一组数值,对其取自然对数后与原列表做线性回归能得到最高的R²。现有代码运行结果不符合预期,具体如下:
- 预期的最优列表(存在多种可能解):
Best List = [0.258288996, 0.29055507, 0.15215332, 0.395691366, 0.106867761, 0.096624222, 0.114833083, 0.239799601] - 实际运行结果:
Best List: [1. 1. 1. 0.99999991 0.99999991 0.99999991 1. 1. ]
原代码如下:
import pandas as pd import numpy as np from scipy import stats from scipy.optimize import minimize, LinearConstraint def objective_function(values, df): df['Risk Not Optimal'] = values df['Log Risk Not Optimal'] = np.log(df['Risk Not Optimal']) slope, intercept, r_value, p_value, std_err = stats.linregress(df['Returns'], df['Log Risk Not Optimal']) r_squared = r_value ** 2 return -r_squared data = [0.147490447, 0.15589857, 0.109691117, 0.177958696, 0.084455669, 0.077258315, 0.089590471, 0.142185032] df = pd.DataFrame(data, columns=['Returns']) # Define the bounds for the values of 'Risk Not Optimal' bounds = [(0, 1)] * len(df) # Use the length of the DataFrame # Define the linear constraint for the length constraint = LinearConstraint(np.ones(len(df)), lb=len(df), ub=len(df)) # Perform optimization to maximize the negative R-squared result = minimize(objective_function, x0=[1] * len(df), args=(df,), bounds=bounds, constraints=constraint) best_list = result.x best_r_squared = -result.fun # Convert back to positive print("Best List:", best_list) print("Best R-squared:", best_r_squared)
问题原因与改进方案
1. 核心错误分析
- 约束条件完全错误:你设置的
LinearConstraint要求所有变量的和等于len(df)(即8),但每个变量上限是1,8个变量的最大总和就是8,所以优化器只能返回全1(或接近1)的结果,对数后的值全部为0,线性回归的R²趋近于0,完全偏离需求。 - 目标函数非光滑性:
stats.linregress计算的R²是一个非光滑函数,scipy.minimize默认的L-BFGS-B优化器对这类函数优化效果差,容易陷入局部最优。 - DataFrame副作用:在目标函数中直接修改传入的
df会产生不可控的副作用,干扰后续计算。
2. 修正后的代码
调整约束条件(若无需总和约束则直接移除,若有实际需求可按需添加),更换优化器,同时重构目标函数避免副作用:
import numpy as np from scipy import stats from scipy.optimize import minimize def objective_function(values, returns): # 直接计算对数,避免修改外部数据结构 log_vals = np.log(values) # 提取R²值并返回其负值(转为最小化问题) _, _, r_value, _, _ = stats.linregress(returns, log_vals) return -(r_value ** 2) # 原始数据转为numpy数组 returns = np.array([0.147490447, 0.15589857, 0.109691117, 0.177958696, 0.084455669, 0.077258315, 0.089590471, 0.142185032]) # 变量边界:设置极小值下限避免log(0)报错 bounds = [(1e-8, 1.0) for _ in range(len(returns))] # 初始值设为随机小数值,避免陷入局部最优 x0 = np.random.uniform(0.1, 0.5, size=len(returns)) # 使用SLSQP优化器,适配非光滑目标与边界约束 result = minimize(objective_function, x0=x0, args=(returns,), bounds=bounds, method='SLSQP', options={'maxiter': 1000}) best_list = result.x best_r_squared = -result.fun print("Best List:", best_list) print("Best R-squared:", best_r_squared)
3. 更合适的库推荐
scipy.optimize:调整优化器(如SLSQP、Nelder-Mead)和初始值后,完全满足需求,无需额外依赖。pymoo:适合添加复杂约束或多目标优化场景,灵活性更高。optuna:基于贝叶斯优化的框架,对非光滑、非凸目标函数的优化效率优于传统梯度方法,更容易找到全局最优。
关键补充
如果需要总和约束(比如总和为1),可添加以下代码并传入minimize的constraints参数:
from scipy.optimize import LinearConstraint constraint = LinearConstraint(np.ones(len(returns)), lb=1.0, ub=1.0)
内容的提问来源于stack exchange,提问作者Sylvester
相关产品推荐
相关产品推荐

