未记录事实核验 · 状态:现行内容
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 适用边界与维护说明
本文适合学习R²的定义、条件化的平方和分解及合成数据上的回归比较;代码片段依赖前文导入、数据和函数,应按章节顺序使用。平方和分解、相关系数平方以及“解释变异百分比”的表述须结合文中含截距、同一训练样本普通最小二乘拟合等条件理解,不能直接推广到任意预测,负R²应解释为平方误差超过评价样本的均值基准。负R²示例通过人为偏移预测构造,并非原模型的训练表现。房价与营销案例使用测试集选型且计算测试集调整R²,仅供比较演示,不能作为未参与选择的最终泛化评估;调整R²公式复用时须满足相应模型自由度条件及n>p+1。行业阈值、质量分档和交叉验证稳定性分档均为示意规则,营销系数与简化回报率不证明实际因果效果或ROI。本文仅作结构审阅,未执行代码或进行外部事实核验。
📊 决定系数(Coefficient of Determination, R²)
abstract 📋 本节概览
- 🎯 学习目标:深入理解R²的定义、计算和应用
- 📚 前置知识:线性回归|线性回归 | 相关分析|相关分析
- ⏱️ 预计用时:12分钟
🤔 什么是决定系数?
note 📖 **决定系数的定义**
决定系数(R²)衡量模型相对于均值预测基准的拟合表现。对非恒定目标,按R² = 1 - SS_res/SS_tot计算时,其上限为1,但可以为负;含截距的普通最小二乘回归在训练样本上的R²通常位于0到1。在同一评价数据和定义下,R²越高表示相对该基准的平方误差越小,但不保证泛化能力更强。
核心公式:$R^2 = 1 - \frac{SS_{res}}{SS_{tot}}$,其中要求$SS_{tot}>0$。对于含截距、在同一训练样本上拟合的普通最小二乘回归,平方和可分解,此时还可写为$R^2 = \frac{SS_{reg}}{SS_{tot}}$。
其中:
- $SS_{res}$ = 残差平方和(未解释的变异)
- $SS_{tot}$ = 总平方和(总变异)
- $SS_{reg}$ = 回归平方和(已解释的变异)
生活例子:
- R² = 0.8 意味着模型解释了80%的数据变异,还有20%无法解释
- 房价预测模型R² = 0.75,说明面积等因素解释了75%的房价差异
- 成绩预测模型R² = 0.6,说明学习时间解释了60%的成绩变化
📐 R²的数学原理
变异分解
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
# 生成示例数据
np.random.seed(42)
x = np.random.uniform(1, 10, 50)
y = 2 * x + 3 + np.random.normal(0, 2, 50) # y = 2x + 3 + 噪声
# 拟合线性回归
X = x.reshape(-1, 1)
model = LinearRegression()
model.fit(X, y)
y_pred = model.predict(X)
# 计算各种平方和
y_mean = np.mean(y)
SS_tot = np.sum((y - y_mean) ** 2) # 总平方和
SS_res = np.sum((y - y_pred) ** 2) # 残差平方和
SS_reg = np.sum((y_pred - y_mean) ** 2) # 回归平方和
# 计算R²
r2_manual = 1 - SS_res / SS_tot
r2_sklearn = r2_score(y, y_pred)
print(f"变异分解分析:")
print(f"总变异 (SS_tot): {SS_tot:.2f}")
print(f"回归解释的变异 (SS_reg): {SS_reg:.2f}")
print(f"残差变异 (SS_res): {SS_res:.2f}")
print(f"验证: SS_reg + SS_res = {SS_reg + SS_res:.2f}")
print(f"\nR²计算:")
print(f"R² = SS_reg / SS_tot = {SS_reg:.2f} / {SS_tot:.2f} = {SS_reg/SS_tot:.3f}")
print(f"R² = 1 - SS_res / SS_tot = 1 - {SS_res:.2f} / {SS_tot:.2f} = {r2_manual:.3f}")
print(f"sklearn计算的R²: {r2_sklearn:.3f}")
# 可视化变异分解
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
# 左图:回归拟合
ax1.scatter(x, y, alpha=0.6, label='实际数据', color='blue')
ax1.plot(x, y_pred, 'r-', label='回归线', linewidth=2)
ax1.axhline(y_mean, color='green', linestyle='--', label=f'均值 = {y_mean:.2f}')
# 标注一些点到回归线和均值的距离
selected_indices = [5, 15, 25, 35]
for i in selected_indices:
# 点到回归线的距离(残差)
ax1.plot([x[i], x[i]], [y[i], y_pred[i]], 'orange', linewidth=1, alpha=0.7)
# 回归线到均值的距离
ax1.plot([x[i], x[i]], [y_pred[i], y_mean], 'purple', linewidth=1, alpha=0.7)
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
ax1.set_title(f'线性回归拟合 (R² = {r2_manual:.3f})')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 右图:变异分解饼图
sizes = [SS_reg, SS_res]
labels = [f'解释变异\n({SS_reg/SS_tot:.1%})', f'残差变异\n({SS_res/SS_tot:.1%})']
colors = ['skyblue', 'lightcoral']
ax2.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
ax2.set_title('变异分解')
plt.tight_layout()
plt.show()
💻 R²的计算方法
方法1:基于相关系数
# 对于含截距、在同一训练样本上拟合的简单普通最小二乘线性回归,且x、y均非恒定时,R² = r²
from scipy.stats import pearsonr
# 使用之前的数据
correlation, p_value = pearsonr(x, y)
r2_from_correlation = correlation ** 2
print(f"相关系数方法计算R²:")
print(f"皮尔逊相关系数 r = {correlation:.3f}")
print(f"R² = r² = {correlation:.3f}² = {r2_from_correlation:.3f}")
print(f"与回归计算的R²对比: {r2_sklearn:.3f}")
print(f"差异: {abs(r2_from_correlation - r2_sklearn):.6f}")
# 注意:这种关系只对简单线性回归成立
print(f"\n💡 注意: R² = r² 只对简单线性回归成立!")
方法2:使用不同的Python库
import pandas as pd
import statsmodels.api as sm
from sklearn.metrics import r2_score, explained_variance_score
# 方法1: sklearn
r2_sklearn = r2_score(y, y_pred)
# 方法2: statsmodels
X_with_const = sm.add_constant(x)
model_stats = sm.OLS(y, X_with_const).fit()
r2_statsmodels = model_stats.rsquared
# 方法3: 手动计算
def calculate_r2_manual(y_true, y_pred):
ss_res = np.sum((y_true - y_pred) ** 2)
ss_tot = np.sum((y_true - np.mean(y_true)) ** 2)
return 1 - (ss_res / ss_tot)
r2_manual = calculate_r2_manual(y, y_pred)
# 方法4: pandas计算实际值与预测值的相关系数平方
# 在本例含截距的训练集普通最小二乘拟合中,它等于R²;对任意预测并不成立
df = pd.DataFrame({'actual': y, 'predicted': y_pred})
correlation_matrix = df.corr()
r2_pandas = correlation_matrix.loc['actual', 'predicted'] ** 2
print(f"不同方法计算R²的对比:")
print(f"sklearn: {r2_sklearn:.6f}")
print(f"statsmodels: {r2_statsmodels:.6f}")
print(f"手动计算: {r2_manual:.6f}")
print(f"pandas: {r2_pandas:.6f}")
# 解释方差得分(类似但略有不同)
explained_var = explained_variance_score(y, y_pred)
print(f"解释方差得分: {explained_var:.6f}")
📊 R²的解释和评判标准
R²值的含义解释
def interpret_r2(r2_value):
"""解释R²值的含义"""
percentage = r2_value * 100
unexplained = (1 - r2_value) * 100
if r2_value >= 0.9:
interpretation = "优秀"
description = "模型非常好地拟合了数据"
elif r2_value >= 0.8:
interpretation = "很好"
description = "模型较好地拟合了数据"
elif r2_value >= 0.7:
interpretation = "良好"
description = "模型有较好的解释能力"
elif r2_value >= 0.5:
interpretation = "一般"
description = "模型有一定的解释能力"
elif r2_value >= 0.3:
interpretation = "较差"
description = "模型解释能力有限"
else:
interpretation = "很差"
description = "模型几乎没有解释能力"
print(f"R² = {r2_value:.3f}")
print(f"解释: 模型解释了{percentage:.1f}%的因变量变异")
print(f" 还有{unexplained:.1f}%的变异无法解释")
print(f"评价: {interpretation} - {description}")
return interpretation
# 演示不同R²值的含义
r2_examples = [0.95, 0.85, 0.65, 0.45, 0.25, 0.05]
print("不同R²值的解释:")
print("=" * 50)
for r2 in r2_examples:
interpret_r2(r2)
print("-" * 30)
行业和应用场景的R²阈值示例
下列数值仅作为分档演示的假设阈值,未提供支持其作为行业标准的来源,不应据此直接判断真实项目是否合格。
def field_specific_r2_standards():
"""不同领域的R²评判标准"""
standards = {
"物理科学": {
"优秀": "> 0.95",
"良好": "0.90 - 0.95",
"可接受": "0.80 - 0.90",
"说明": "物理定律相对确定,期望高R²"
},
"工程技术": {
"优秀": "> 0.90",
"良好": "0.80 - 0.90",
"可接受": "0.70 - 0.80",
"说明": "工程问题相对可控,期望较高R²"
},
"经济金融": {
"优秀": "> 0.70",
"良好": "0.50 - 0.70",
"可接受": "0.30 - 0.50",
"说明": "经济数据波动大,R²要求相对较低"
},
"医学生物": {
"优秀": "> 0.80",
"良好": "0.60 - 0.80",
"可接受": "0.40 - 0.60",
"说明": "生物系统复杂,个体差异大"
},
"社会科学": {
"优秀": "> 0.60",
"良好": "0.40 - 0.60",
"可接受": "0.20 - 0.40",
"说明": "人类行为复杂,影响因素众多"
},
"市场营销": {
"优秀": "> 0.50",
"良好": "0.30 - 0.50",
"可接受": "0.15 - 0.30",
"说明": "消费者行为难以预测"
}
}
print("不同领域的R²评判标准:")
print("=" * 60)
for field, standard in standards.items():
print(f"\n{field}:")
print(f" 优秀: {standard['优秀']}")
print(f" 良好: {standard['良好']}")
print(f" 可接受: {standard['可接受']}")
print(f" 说明: {standard['说明']}")
field_specific_r2_standards()
⚠️ R²的常见误区和限制
误区1:R²越高越好
# 生成过拟合示例
np.random.seed(42)
x_simple = np.linspace(0, 10, 20)
y_simple = 2 * x_simple + 1 + np.random.normal(0, 2, 20)
# 简单线性回归
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
models = {}
r2_scores = {}
# 不同复杂度的模型
degrees = [1, 3, 5, 10, 15]
for degree in degrees:
if degree == 1:
# 线性回归
model = LinearRegression()
X_poly = x_simple.reshape(-1, 1)
else:
# 多项式回归
model = make_pipeline(PolynomialFeatures(degree), LinearRegression())
X_poly = x_simple.reshape(-1, 1)
model.fit(X_poly, y_simple)
y_pred = model.predict(X_poly)
r2 = r2_score(y_simple, y_pred)
models[degree] = model
r2_scores[degree] = r2
# 可视化不同复杂度模型的拟合效果
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()
x_plot = np.linspace(0, 10, 100).reshape(-1, 1)
for i, degree in enumerate(degrees):
ax = axes[i]
# 绘制数据点
ax.scatter(x_simple, y_simple, alpha=0.6, color='blue', label='训练数据')
# 绘制拟合曲线
y_plot = models[degree].predict(x_plot)
ax.plot(x_plot, y_plot, 'r-', label=f'拟合曲线')
ax.set_title(f'度数={degree}, R²={r2_scores[degree]:.3f}')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.legend()
ax.grid(True, alpha=0.3)
# 隐藏最后一个子图
axes[-1].axis('off')
plt.tight_layout()
plt.show()
print("不同模型复杂度的R²:")
for degree, r2 in r2_scores.items():
print(f"度数 {degree:2d}: R² = {r2:.3f}")
print(f"\n⚠️ 警告: 随着模型复杂度增加,R²会持续上升")
print(f"但这不意味着模型更好!高次多项式可能过拟合")
print(f"应该使用交叉验证等方法评估真实的泛化能力")
误区2:负R²的含义
# 创建一个极差的模型产生负R²
np.random.seed(42)
x_bad = np.random.uniform(0, 10, 50)
y_bad = np.random.normal(5, 2, 50) # y与x无关
# 强行拟合一个不合适的模型
X_bad = x_bad.reshape(-1, 1)
bad_model = LinearRegression()
bad_model.fit(X_bad, y_bad)
# 故意对预测值施加偏移,使其平方误差超过均值基准,演示负R²
# 这不是含截距普通最小二乘模型本身的训练集预测表现
y_bad_pred = bad_model.predict(X_bad) + 10
r2_bad = r2_score(y_bad, y_bad_pred)
# 与简单均值预测对比
y_mean_pred = np.full_like(y_bad, np.mean(y_bad))
ss_tot = np.sum((y_bad - np.mean(y_bad)) ** 2)
ss_res_bad = np.sum((y_bad - y_bad_pred) ** 2)
ss_res_mean = np.sum((y_bad - y_mean_pred) ** 2)
print(f"负R²示例:")
print(f"人为偏移预测的R²: {r2_bad:.3f}")
print(f"总平方和 (SS_tot): {ss_tot:.2f}")
print(f"回归模型残差平方和: {ss_res_bad:.2f}")
print(f"均值模型残差平方和: {ss_res_mean:.2f}")
print(f"\n解释: R² = 1 - {ss_res_bad:.2f}/{ss_tot:.2f} = {r2_bad:.3f}")
print(f"负R²意味着你的模型预测比简单使用均值还要差!")
# 可视化
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.scatter(x_bad, y_bad, alpha=0.6, label='实际数据')
plt.plot(x_bad, y_bad_pred, 'r-', label=f'人为偏移预测 (R²={r2_bad:.3f})')
plt.axhline(np.mean(y_bad), color='green', linestyle='--', label='均值预测')
plt.xlabel('X')
plt.ylabel('Y')
plt.title('人为偏移预测产生负R²')
plt.legend()
plt.subplot(1, 2, 2)
residuals_bad = y_bad - y_bad_pred
residuals_mean = y_bad - y_mean_pred
plt.scatter(y_bad_pred, residuals_bad, alpha=0.6, label='回归残差')
plt.axhline(0, color='red', linestyle='-', alpha=0.3)
plt.xlabel('预测值')
plt.ylabel('残差')
plt.title('残差图')
plt.legend()
plt.tight_layout()
plt.show()
🔧 调整R²(Adjusted R²)
调整R²的必要性
def adjusted_r2(r2, n, p):
"""
计算调整R²
r2: 普通R²
n: 样本量
p: 预测变量个数
"""
return 1 - (1 - r2) * (n - 1) / (n - p - 1)
# 生成多元回归数据
np.random.seed(42)
n_samples = 100
n_features_list = [1, 3, 5, 10, 15, 20]
# 基础数据
X_base = np.random.randn(n_samples, 20)
# 只有前3个特征真正有用
y_multi = (2 * X_base[:, 0] +
1.5 * X_base[:, 1] +
-1 * X_base[:, 2] +
np.random.normal(0, 0.5, n_samples))
results = []
for n_features in n_features_list:
X_subset = X_base[:, :n_features]
model = LinearRegression()
model.fit(X_subset, y_multi)
y_pred = model.predict(X_subset)
r2 = r2_score(y_multi, y_pred)
adj_r2 = adjusted_r2(r2, n_samples, n_features)
results.append({
'n_features': n_features,
'r2': r2,
'adj_r2': adj_r2,
'difference': r2 - adj_r2
})
# 展示结果
print("特征数量对R²和调整R²的影响:")
print("特征数\t普通R²\t调整R²\t差值")
print("-" * 40)
for result in results:
print(f"{result['n_features']:4d}\t{result['r2']:.3f}\t{result['adj_r2']:.3f}\t{result['difference']:.3f}")
# 可视化
features = [r['n_features'] for r in results]
r2_values = [r['r2'] for r in results]
adj_r2_values = [r['adj_r2'] for r in results]
plt.figure(figsize=(10, 6))
plt.plot(features, r2_values, 'bo-', label='普通R²', linewidth=2)
plt.plot(features, adj_r2_values, 'ro-', label='调整R²', linewidth=2)
plt.xlabel('特征数量')
plt.ylabel('R²值')
plt.title('特征数量对R²和调整R²的影响')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
print(f"\n💡 观察:")
print(f"- 普通R²随特征增加单调上升")
print(f"- 调整R²在特征过多时会下降")
print(f"- 调整R²对训练样本上的拟合优度加入参数数量惩罚,但不等于模型的泛化性能")
📈 实际应用案例
案例1:房价预测模型评估
# 生成房价数据
np.random.seed(42)
n_houses = 200
# 生成特征
area = np.random.uniform(50, 200, n_houses) # 面积
rooms = np.random.poisson(3, n_houses) + 1 # 房间数
age = np.random.uniform(0, 30, n_houses) # 房龄
location_score = np.random.uniform(1, 10, n_houses) # 位置评分
# 生成房价(有一定的真实关系)
price = (300 * area + # 面积影响最大
5000 * rooms + # 房间数影响
-200 * age + # 房龄负影响
2000 * location_score + # 位置影响
np.random.normal(0, 10000, n_houses)) # 噪声
# 创建DataFrame
house_data = pd.DataFrame({
'area': area,
'rooms': rooms,
'age': age,
'location': location_score,
'price': price
})
# 不同复杂度的模型
from sklearn.model_selection import train_test_split
X = house_data[['area', 'rooms', 'age', 'location']]
y = house_data['price']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 模型1:只用面积
model1 = LinearRegression()
model1.fit(X_train[['area']], y_train)
y1_train_pred = model1.predict(X_train[['area']])
y1_test_pred = model1.predict(X_test[['area']])
# 模型2:面积 + 房间数
model2 = LinearRegression()
model2.fit(X_train[['area', 'rooms']], y_train)
y2_train_pred = model2.predict(X_train[['area', 'rooms']])
y2_test_pred = model2.predict(X_test[['area', 'rooms']])
# 模型3:所有特征
model3 = LinearRegression()
model3.fit(X_train, y_train)
y3_train_pred = model3.predict(X_train)
y3_test_pred = model3.predict(X_test)
y_house_test = y_test.copy() # 保留房价测试标签,避免被营销章节覆盖
# 计算R²
models_comparison = [
{
'name': '仅面积',
'features': 1,
'train_r2': r2_score(y_train, y1_train_pred),
'test_r2': r2_score(y_test, y1_test_pred)
},
{
'name': '面积+房间数',
'features': 2,
'train_r2': r2_score(y_train, y2_train_pred),
'test_r2': r2_score(y_test, y2_test_pred)
},
{
'name': '所有特征',
'features': 4,
'train_r2': r2_score(y_train, y3_train_pred),
'test_r2': r2_score(y_test, y3_test_pred)
}
]
# 计算调整R²
for model in models_comparison:
model['adj_train_r2'] = adjusted_r2(model['train_r2'], len(y_train), model['features'])
model['adj_test_r2'] = adjusted_r2(model['test_r2'], len(y_test), model['features'])
print("房价预测模型比较:")
print("模型\t\t特征数\t训练R²\t测试R²\t调整训练R²\t调整测试R²")
print("-" * 70)
for model in models_comparison:
print(f"{model['name']:<12}\t{model['features']}\t{model['train_r2']:.3f}\t{model['test_r2']:.3f}\t{model['adj_train_r2']:.3f}\t\t{model['adj_test_r2']:.3f}")
# 可视化模型性能
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
# R²比较
model_names = [m['name'] for m in models_comparison]
train_r2s = [m['train_r2'] for m in models_comparison]
test_r2s = [m['test_r2'] for m in models_comparison]
x_pos = np.arange(len(model_names))
width = 0.35
ax1.bar(x_pos - width/2, train_r2s, width, label='训练R²', alpha=0.8)
ax1.bar(x_pos + width/2, test_r2s, width, label='测试R²', alpha=0.8)
ax1.set_xlabel('模型')
ax1.set_ylabel('R²值')
ax1.set_title('不同模型的R²比较')
ax1.set_xticks(x_pos)
ax1.set_xticklabels(model_names)
ax1.legend()
ax1.grid(True, alpha=0.3)
# 最佳模型的预测效果
ax2.scatter(y_test, y3_test_pred, alpha=0.6)
ax2.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
ax2.set_xlabel('实际房价')
ax2.set_ylabel('预测房价')
ax2.set_title(f'最佳模型预测效果 (R² = {r2_score(y_test, y3_test_pred):.3f})')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"\n模型选择建议:")
best_model = max(models_comparison, key=lambda x: x['test_r2'])
print(f"最佳模型: {best_model['name']}")
print(f"测试R²: {best_model['test_r2']:.3f}")
print(f"解释: 该模型能解释{best_model['test_r2']*100:.1f}%的房价变异")
案例2:营销效果分析
# 生成营销数据
np.random.seed(42)
n_campaigns = 150
# 广告投放特征
tv_spend = np.random.uniform(0, 50, n_campaigns) # 电视广告投入(万元)
online_spend = np.random.uniform(0, 30, n_campaigns) # 网络广告投入(万元)
print_spend = np.random.uniform(0, 20, n_campaigns) # 平面广告投入(万元)
# 销售额生成(有边际递减效应)
sales = (10 +
2.5 * np.sqrt(tv_spend) + # 电视广告效果递减
1.8 * np.sqrt(online_spend) + # 网络广告效果递减
0.8 * np.sqrt(print_spend) + # 平面广告效果最小
np.random.normal(0, 3, n_campaigns)) # 随机因素
marketing_data = pd.DataFrame({
'tv_spend': tv_spend,
'online_spend': online_spend,
'print_spend': print_spend,
'total_spend': tv_spend + online_spend + print_spend,
'sales': sales
})
# 不同的营销模型
models = {
'总投入模型': ['total_spend'],
'电视广告模型': ['tv_spend'],
'多媒体模型': ['tv_spend', 'online_spend'],
'全媒体模型': ['tv_spend', 'online_spend', 'print_spend']
}
marketing_results = {}
for model_name, features in models.items():
X = marketing_data[features]
y = marketing_data['sales']
# 训练测试分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 拟合模型
model = LinearRegression()
model.fit(X_train, y_train)
# 预测
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)
# 计算指标
train_r2 = r2_score(y_train, y_train_pred)
test_r2 = r2_score(y_test, y_test_pred)
adj_r2 = adjusted_r2(test_r2, len(y_test), len(features))
marketing_results[model_name] = {
'features': features,
'train_r2': train_r2,
'test_r2': test_r2,
'adj_r2': adj_r2,
'model': model,
'coefficients': model.coef_,
'intercept': model.intercept_
}
# 展示结果
print("营销效果模型比较:")
print("模型\t\t\t训练R²\t测试R²\t调整R²")
print("-" * 50)
for name, result in marketing_results.items():
print(f"{name:<15}\t{result['train_r2']:.3f}\t{result['test_r2']:.3f}\t{result['adj_r2']:.3f}")
# 分析最佳模型的系数
best_model = max(marketing_results.items(), key=lambda x: x[1]['adj_r2'])
best_name, best_result = best_model
print(f"\n最佳模型: {best_name}")
print(f"调整R²: {best_result['adj_r2']:.3f}")
print(f"模型方程: 销售额 = {best_result['intercept']:.2f}", end="")
for i, feature in enumerate(best_result['features']):
coef = best_result['coefficients'][i]
print(f" + {coef:.2f}×{feature}", end="")
print()
print(f"\n💡 业务洞察:")
print(f"- 该模型在此合成数据测试划分上的普通R²为{best_result['test_r2']:.3f};调整R²不直接表示变异解释百分比")
for i, feature in enumerate(best_result['features']):
coef = best_result['coefficients'][i]
print(f"- 在此合成数据的线性拟合中,保持其他特征不变时,{feature}每增加1万元对应预测销售额变化{coef:.2f}万元;这不是实际投放的因果收益")
# ROI分析
print(f"\n💰 ROI分析:")
for i, feature in enumerate(best_result['features']):
coef = best_result['coefficients'][i]
# 仅作假设演示:假设系数代表每万元投入带来的因果增量收入,且收入全部视为收益
# 本文的合成数据线性拟合并未建立这些条件,结果不是实际ROI
roi = (coef - 1) * 100
print(f"- {feature}在上述简化假设下的示意回报率: {roi:.1f}%,不可作为实际ROI")
🛠️ R²的改进和替代指标
1. 其他评估指标
from sklearn.metrics import mean_squared_error, mean_absolute_error, mean_absolute_percentage_error
def comprehensive_regression_evaluation(y_true, y_pred, model_name="模型"):
"""全面的回归模型评估"""
# R²相关指标
r2 = r2_score(y_true, y_pred)
explained_var = explained_variance_score(y_true, y_pred)
# 误差指标
mse = mean_squared_error(y_true, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_true, y_pred)
mape = mean_absolute_percentage_error(y_true, y_pred)
# 相对指标
mean_y = np.mean(y_true)
cv_rmse = rmse / mean_y * 100 # RMSE变异系数
print(f"{model_name}评估报告:")
print("=" * 40)
print(f"拟合优度指标:")
print(f" R² : {r2:.4f}")
print(f" 解释方差得分 : {explained_var:.4f}")
print(f"\n误差指标:")
print(f" MSE : {mse:.2f}")
print(f" RMSE : {rmse:.2f}")
print(f" MAE : {mae:.2f}")
print(f" MAPE : {mape:.2%}")
print(f"\n相对指标:")
print(f" RMSE/均值 : {cv_rmse:.2f}%")
return {
'r2': r2, 'explained_var': explained_var,
'mse': mse, 'rmse': rmse, 'mae': mae, 'mape': mape,
'cv_rmse': cv_rmse
}
# 使用之前的房价数据进行示例
evaluation = comprehensive_regression_evaluation(y_house_test, y3_test_pred, "房价预测模型")
2. 交叉验证R²
from sklearn.model_selection import cross_val_score, KFold
def cross_validated_r2_analysis(X, y, model, cv_folds=5):
"""交叉验证R²分析"""
# 执行交叉验证
cv_scores = cross_val_score(model, X, y, cv=cv_folds, scoring='r2')
# 计算统计量
mean_r2 = cv_scores.mean()
std_r2 = cv_scores.std()
min_r2 = cv_scores.min()
max_r2 = cv_scores.max()
print(f"交叉验证R²分析 ({cv_folds}折):")
print("-" * 30)
print(f"各折R²: {[f'{score:.3f}' for score in cv_scores]}")
print(f"平均R²: {mean_r2:.3f} ± {std_r2:.3f}")
print(f"范围: [{min_r2:.3f}, {max_r2:.3f}]")
# 稳定性评估
stability = std_r2 / mean_r2 if mean_r2 > 0 else float('inf')
if stability < 0.1:
stability_level = "很稳定"
elif stability < 0.2:
stability_level = "稳定"
elif stability < 0.3:
stability_level = "中等稳定"
else:
stability_level = "不稳定"
print(f"稳定性: {stability_level} (变异系数: {stability:.3f})")
return {
'cv_scores': cv_scores,
'mean_r2': mean_r2,
'std_r2': std_r2,
'stability': stability_level
}
# 使用房价数据进行交叉验证
X_house = house_data[['area', 'rooms', 'age', 'location']]
y_house = house_data['price']
cv_result = cross_validated_r2_analysis(X_house, y_house, LinearRegression())
# 可视化交叉验证结果
plt.figure(figsize=(10, 6))
plt.bar(range(1, len(cv_result['cv_scores'])+1), cv_result['cv_scores'], alpha=0.7)
plt.axhline(cv_result['mean_r2'], color='red', linestyle='--',
label=f'平均R² = {cv_result["mean_r2"]:.3f}')
plt.xlabel('交叉验证折数')
plt.ylabel('R²')
plt.title('交叉验证R²分布')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
🔗 相关知识
#统计学 #决定系数 #R平方 #回归分析 #模型评估 #基础