Error in user YAML: (<unknown>): could not find expected ':' while scanning a simple key at line 3 column 1
---
- oeasy Python 0811
- 这是 oeasy 系统化 Python 教程,从基础一步步讲,扎实、完整、不跳步。愿意花时间学,就能真正学会。
本教程同步发布在:
个人网站: `https://oeasy.org`
蓝桥云课: `https://www.lanqiao.cn/courses/3584`
GitHub: `https://github.com/overmind1980/oeasy-python-tutorial`
Gitee: `https://gitee.com/overmind1980/oeasypython`
---- 上次研究的是 分段线性回归
| 中文术语 | 英文标准对应 | 核心概念/定义说明 |
|---|---|---|
| 分段线性回归 | Piecewise Linear Regression | 将数据按分界点分成多个区间 每个区间单独拟合一条直线 贴合数据的分段变化规律 |
| 分段点 / 分界点 | Breakpoint | 划分数据区间的临界值 是分段线性回归的关键参数 |
| 分段拟合公式 | Piecewise Fitting Formula | 各区间对应的线性公式 不同区间的斜率$k$、截距$b$各不相同 |
- 分段数量越多
- 均方根误差越小
- 决定系数越趋向于1
- 可是目前是伽利略的8条数据
- 如果数据集大一些的话
- 也不能这么无限的切分啊
- 有什么更好的办法吗?🤔
| Number | Time | Distance |
|---|---|---|
| 1 | 1 | 33 |
| 2 | 2 | 130 |
| 3 | 3 | 298 |
| 4 | 4 | 526 |
| 5 | 5 | 824 |
| 6 | 6 | 1192 |
| 7 | 7 | 1620 |
| 8 | 8 | 2123 |
- 33 130 298...
- 数列 不是 线性的等差数列
- 有什么规律吗?
- 差距越来越大
| Serial Number | Time ( |
Distance ( |
First-Order Difference ( |
|---|---|---|---|
| 1 | 1 | 33 | - |
| 2 | 2 | 130 | |
| 3 | 3 | 298 | |
| 4 | 4 | 526 | |
| 5 | 5 | 824 | |
| 6 | 6 | 1192 | |
| 7 | 7 | 1620 | |
| 8 | 8 | 2123 |
- 97,168,228,298,368...
- 差值 趋近于 等差数列
| 时间 | 距离 |
一阶差值 |
二阶差值 |
|---|---|---|---|
| 1 | 33 | - | - |
| 2 | 130 | - | |
| 3 | 298 | ||
| 4 | 526 | ||
| 5 | 824 | ||
| 6 | 1192 | ||
| 7 | 1620 | ||
| 8 | 2123 |
- 伽利略从 通过定量测量+数学分析+逻辑推理
- 归纳出了匀加速直线运动的核心规律
- 并进一步推导得到自由落体定律
$$S \propto t^2$$
- 这是关于t二次函数
# 伽利略 斜面实验 原始数据 二次回归 (完美修正警告 + 直接保存PNG + y = a*t² + bt + c)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import r2_score, mean_squared_error
# ===================== 1. 构造伽利略原始真实数据集【纯三列 序号、时间、距离】=====================
data = pd.DataFrame({
'Serial': [1,2,3,4,5,6,7,8],
'Time': [1,2,3,4,5,6,7,8],
'Distance': [33,130,298,526,824,1192,1620,2123]
})
# 特征/目标值 提取
X = data[['Time']] # 保持DataFrame格式+特征名,彻底解决警告
y = data['Distance']
# ===================== 2. 构造二次特征 + 二次回归建模 核心步骤 =====================
poly = PolynomialFeatures(degree=2, include_bias=False)
X_2 = poly.fit_transform(X) # 生成 [t, t²] 二次特征,带特征名拟合
# 训练二次回归模型
model = LinearRegression()
model.fit(X_2, y)
# ===================== 3. 模型预测 + 拟合效果评估 =====================
y_pred = model.predict(X_2)
r2 = r2_score(y, y_pred)
rmse = np.sqrt(mean_squared_error(y, y_pred))
# ===================== 4. 输出核心结果 =====================
print("="*70)
print("✅ 伽利略原始数据 二次回归结果 (y = a*t² + b*t + c)")
print(f"二次回归方程:Distance = {model.coef_[1]:.2f}*Time² + {model.coef_[0]:.2f}*Time + {model.intercept_:.2f}")
print(f"拟合优度 R² = {r2:.6f} | 均方根误差 RMSE = {rmse:.2f}")
print("="*70)
print("✅ 原始实测值 vs 二次回归预测值")
compare = pd.DataFrame({
'Time':X['Time'],
'True_Distance':y,
'Pred_Distance':np.round(y_pred,1)
})
print(compare)
print("="*70)
# ===================== 5. 可视化:原始数据+二次拟合曲线 ✔️直接保存PNG 无plt.show() ✔️=====================
plt.rcParams['font.sans-serif']=['WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(8,5),dpi=120)
# 伽利略原始实测数据点
plt.scatter(X['Time'], y, color='darkred', s=120, label='Galileo Raw Data', edgecolors='white', linewidth=2)
# 生成平滑的时间序列+二次拟合曲线【修正警告关键:保持DataFrame+同名特征】
t_range = pd.DataFrame({'Time':np.linspace(0,9,100)})
t_range_2 = poly.transform(t_range)
s_pred = model.predict(t_range_2)
# 绘制二次拟合曲线
plt.plot(t_range['Time'], s_pred, color='darkblue', lw=3, label='Quadratic Fit (S ∝ t²)')
plt.xlabel('Time (equal intervals)')
plt.ylabel('Distance (scale divisions)')
plt.title('Galileo Inclined Plane Experiment | Quadratic Regression')
plt.legend(loc='upper left')
plt.grid(alpha=0.3, linestyle='--')
# ✅ 直接保存高清PNG到实验楼路径,无弹窗,无plt.show()
plt.savefig('/home/project/伽利略二次回归拟合图.png', dpi=120, bbox_inches='tight')
- 二次曲线 刚好拟合 这个数据集
- 距离
- 和 下落时间 有关
- 距离还和什么有关系呢?
- 距离跟角度(θ)有关系
- 角度(θ)
- 范围:2°~10°
- 小倾角,延长运动时间
- 适配水钟精度
- 范围:2°~10°
| Serial | Angle (°) | Distance (scale divisions) |
|---|---|---|
| 1 | 10 | 2282 |
| 2 | 20 | 4480 |
| 3 | 30 | 6550 |
| 4 | 40 | 8380 |
| 5 | 50 | 9960 |
| 6 | 60 | 11290 |
| 7 | 70 | 12220 |
| 8 | 80 | 12830 |
- 先试试线性回归
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error
# ===================== 1. 纯净数据集 =====================
data = pd.DataFrame({
'Serial': [1,2,3,4,5,6,7,8],
'Angle': [10, 20, 30, 40, 50, 60, 70, 80],
'Distance': [2282, 4480, 6550, 8380, 9960, 11290, 12220, 12830]
})
# 自变量+因变量
X_angle = data[['Angle']]
y = data['Distance']
# ===================== 2. 仅保留:角度 一次线性回归 =====================
model_angle_linear = LinearRegression()
model_angle_linear.fit(X_angle, y)
y_pred_angle_linear = model_angle_linear.predict(X_angle)
r2_angle_linear = r2_score(y, y_pred_angle_linear)
rmse_angle_linear = np.sqrt(mean_squared_error(y, y_pred_angle_linear))
# ===================== 3. 评估指标输出 =====================
print("="*80)
print("✅ 伽利略斜面实验-大角度(10°~80°) 角度→距离 一次线性回归评估")
print("="*80)
print(f"角度一次线性回归 | R² = {r2_angle_linear:.4f} | RMSE = {rmse_angle_linear:.1f}")
print(f"拟合方程:Distance = {model_angle_linear.coef_[0]:.1f}*Angle + {model_angle_linear.intercept_:.0f}")
print("="*80)
# 实测值vs预测值对比表
compare_table = pd.DataFrame({
'Angle(°)': data['Angle'],
'实测Distance': y,
'角度线性预测': np.round(y_pred_angle_linear,1)
})
print(compare_table)
print("="*80)
# ===================== 4. 残差分析 =====================
data['残差_角度线性'] = np.round(y - y_pred_angle_linear,1)
print("📈 残差分析(残差越小越好,随机分布最优)")
print(data[['Angle','Distance','残差_角度线性']].rename(columns={'Angle':'Angle(°)','Distance':'实测Distance'}))
print("="*80)
# ===================== 5. 可视化拟合图 自动保存无弹窗 =====================
plt.rcParams['font.sans-serif'] = ['WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(10,6),dpi=120)
plt.scatter(data['Angle'], y, color='darkred', s=180, label='Galileo Raw Data', edgecolors='white', linewidth=2)
angle_range = pd.DataFrame({'Angle': np.linspace(9,85,200)})
# 仅绘制一次线性拟合线
plt.plot(angle_range, model_angle_linear.predict(angle_range), color='darkblue', lw=3, label=f'Angle Linear Fit (R²={r2_angle_linear:.4f})')
plt.xlabel('Angle (°)')
plt.ylabel('Distance (scale divisions)')
plt.title('Large Angle Linear Regression | Galileo Inclined Plane Experiment (10°~80°)')
plt.legend(loc='upper left')
plt.grid(alpha=0.3)
plt.savefig('/home/project/伽利略_大角度纯角度回归.png', dpi=120, bbox_inches='tight')
=========================================================
✅ 伽利略斜面实验-大角度(10°~80°) 角度→距离 一次线性回归评估
==========================================================
角度一次线性回归 | R² = 0.9693 | RMSE = 623.2
拟合方程:Distance = 152.8*Angle + 1624
===========================================================
Angle(°) 实测Distance 角度线性预测
0 10 2282 3151.7
1 20 4480 4679.5
2 30 6550 6207.3
3 40 8380 7735.1
4 50 9960 9262.9
5 60 11290 10790.7
6 70 12220 12318.5
7 80 12830 13846.3
========================================================
📈 残差分析(残差越小越好,随机分布最优)
Angle(°) 实测Distance 残差_角度线性
0 10 2282 -869.7
1 20 4480 -199.5
2 30 6550 342.7
3 40 8380 644.9
4 50 9960 697.1
5 60 11290 499.3
6 70 12220 -98.5
7 80 12830 -1016.3
=====================================================
- 这看起来 拟合的不太好
- 尝试二阶回归
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import r2_score, mean_squared_error
# ===================== 1. 纯净数据集 =====================
data = pd.DataFrame({
'Serial': [1,2,3,4,5,6,7,8],
'Angle': [10, 20, 30, 40, 50, 60, 70, 80],
'Distance': [2282, 4480, 6550, 8380, 9960, 11290, 12220, 12830]
})
# 自变量+因变量
X_angle = data[['Angle']]
y = data['Distance']
# ===================== 2. 角度线性回归 + 角度二次回归 =====================
# 模型1 角度线性回归
model_angle_linear = LinearRegression()
model_angle_linear.fit(X_angle, y)
y_pred_angle_linear = model_angle_linear.predict(X_angle)
r2_angle_linear = r2_score(y, y_pred_angle_linear)
rmse_angle_linear = np.sqrt(mean_squared_error(y, y_pred_angle_linear))
# 模型2 角度二次回归
poly = PolynomialFeatures(degree=2, include_bias=False)
X_angle_2 = poly.fit_transform(X_angle)
model_angle_quad = LinearRegression()
model_angle_quad.fit(X_angle_2, y)
y_pred_angle_quad = model_angle_quad.predict(X_angle_2)
r2_angle_quad = r2_score(y, y_pred_angle_quad)
rmse_angle_quad = np.sqrt(mean_squared_error(y, y_pred_angle_quad))
# ===================== 3. 评估指标输出 =====================
print("="*80)
print("✅ 伽利略斜面实验-大角度(10°~80°) 角度→距离 回归评估")
print("="*80)
print(f"角度线性回归 | R² = {r2_angle_linear:.4f} | RMSE = {rmse_angle_linear:.1f}")
print(f"方程:Distance = {model_angle_linear.coef_[0]:.1f}*Angle + {model_angle_linear.intercept_:.0f}")
print("-"*80)
print(f"角度二次回归 | R² = {r2_angle_quad:.4f} | RMSE = {rmse_angle_quad:.1f}")
print(f"方程:Distance = {model_angle_quad.coef_[1]:.1f}*Angle² + {model_angle_quad.coef_[0]:.1f}*Angle + {model_angle_quad.intercept_:.0f}")
print("="*80)
# 实测值vs预测值对比表
compare_table = pd.DataFrame({
'Angle(°)': data['Angle'],
'实测Distance': y,
'角度线性预测': np.round(y_pred_angle_linear,1),
'角度二次预测': np.round(y_pred_angle_quad,1)
})
print(compare_table)
print("="*80)
# ===================== 4. 残差分析 =====================
data['残差_角度线性'] = np.round(y - y_pred_angle_linear,1)
data['残差_角度二次'] = np.round(y - y_pred_angle_quad,1)
print("📈 残差分析(残差越小越好,随机分布最优)")
print(data[['Angle','Distance','残差_角度线性','残差_角度二次']].rename(columns={'Angle':'Angle(°)','Distance':'实测Distance'}))
print("="*80)
# ===================== 5. 可视化拟合图 自动保存无弹窗 =====================
plt.rcParams['font.sans-serif'] = ['WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(10,6),dpi=120)
plt.scatter(data['Angle'], y, color='darkred', s=180, label='Galileo Raw Data', edgecolors='white', linewidth=2)
angle_range = pd.DataFrame({'Angle': np.linspace(9,85,200)})
# 线性拟合线
plt.plot(angle_range, model_angle_linear.predict(angle_range), color='darkblue', lw=3, label=f'Angle Linear (R²={r2_angle_linear:.4f})')
# 二次拟合线
plt.plot(angle_range, model_angle_quad.predict(poly.transform(angle_range)), color='orange', lw=3, linestyle='--', label=f'Angle Quadratic (R²={r2_angle_quad:.4f})')
plt.xlabel('Angle (°)')
plt.ylabel('Distance (scale divisions)')
plt.title('Large Angle Regression | Galileo Inclined Plane Experiment (10°~80°)')
plt.legend(loc='upper left')
plt.grid(alpha=0.3)
plt.savefig('/home/project/伽利略_大角度纯角度回归.png', dpi=120, bbox_inches='tight')- 最佳工程实践!!!
- 参数里面的
- include_bias=False
- 一定要这样写清楚
poly = PolynomialFeatures(degree=2, include_bias=False)
- 否则的话 会出问题
========================================================================
✅ 伽利略斜面实验-大角度(10°~80°) 角度→距离 回归评估
========================================================================
角度线性回归 | R² = 0.9693 | RMSE = 623.2
方程:Distance = 152.8*Angle + 1624
--------------------------------------------------------------------------------
角度二次回归 | R² = 0.9997 | RMSE = 61.6
方程:Distance = -1.4*Angle² + 274.6*Angle + -406
==========================================================================
Angle(°) 实测Distance 角度线性预测 角度二次预测
0 10 2282 3151.7 2204.3
1 20 4480 4679.5 4544.1
2 30 6550 6207.3 6613.3
3 40 8380 7735.1 8411.8
4 50 9960 9262.9 9939.6
5 60 11290 10790.7 11196.7
6 70 12220 12318.5 12183.2
7 80 12830 13846.3 12899.0
==========================================================================
📈 残差分析(残差越小越好,随机分布最优)
Angle(°) 实测Distance 残差_角度线性 残差_角度二次
0 10 2282 -869.7 77.7
1 20 4480 -199.5 -64.1
2 30 6550 342.7 -63.3
3 40 8380 644.9 -31.8
4 50 9960 697.1 20.4
5 60 11290 499.3 93.3
6 70 12220 -98.5 36.8
7 80 12830 -1016.3 -69.0
=========================================================================
- 这次研究了
- 非线性回归
- 二次回归
- 二次回归是线性回归的「扩展形态」
- 线性回归是二次回归的特例
- 二次项系数为0
二次回归是线性回归通过特征工程- 拓展出的非线性拟合能力
- 前者适合线性关系
- 后者适合可被抛物线近似的非线性关系
- 线性回归是二次回归的特例
| 对比维度 | 线性回归(一次回归) | 二次回归(二次多项式回归) |
|---|---|---|
| 模型公式 | ||
| 特征形态 | 仅用原始特征 |
用原始特征 |
| 拟合曲线 | 一条直线,斜率固定 | 一条抛物线,斜率随 |
| 适用关系 | 自变量和因变量呈 严格线性相关 | 自变量和因变量呈 单调非线性相关(如大角度下 |
| 模型复杂度 | 低,参数少(2个:$w_1、w_0$) | 高,参数多(3个:$w_2、w_1、w_0$) |
| 过拟合风险 | 低,泛化能力强(线性假设简单) | 高,若数据量少易过拟合(需正则化约束) |
| 伽利略实验表现(大角度) | 完全失效,$R^2$ 低,残差呈递增趋势,系统性高估位移 | 拟合精准,$R^2$ 接近1,残差随机分布,能捕捉位移饱和趋势 |
根据真实的物理规律
生成[0,10],[70-89]部分真实数据
并验证模型
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import r2_score, mean_squared_error
# 1. 完整保留原训练集+验证集 无任何修改 (仅保留角度+距离,剔除sin列)
data = pd.DataFrame({'Serial': [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18],
'Angle': [0,1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80],
'Distance': [0,225,450,675,900,1125,1350,1575,1800,2025,2270,4455,6520,8360,9940,11275,12200,12810]})
data_val = pd.DataFrame({'Serial': [19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],
'Angle':[70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89],
'Distance':[12200,12278,12351,12419,12482,12540,12593,12641,12684,12722,12810,12844,12874,12900,12922,12940,12955,12966,12975,12980]})
X_train, y_train = data[['Angle']], data['Distance']
X_val, y_val = data_val[['Angle']], data_val['Distance']
# 2. 双模型训练+预测 (角度线性回归 + 角度二次回归)
model_linear = LinearRegression().fit(X_train, y_train)
y_pred_linear = model_linear.predict(X_train)
y_val_pred_linear = model_linear.predict(X_val)
poly = PolynomialFeatures(degree=2, include_bias=False) # 修复实验楼兼容报错
X_train_poly = poly.fit_transform(X_train)
model_quad = LinearRegression().fit(X_train_poly, y_train)
y_pred_quad = model_quad.predict(X_train_poly)
y_val_pred_quad = model_quad.predict(poly.transform(X_val))
# 3. 计算评估指标 R² + RMSE
r2_l, rmse_l = r2_score(y_train,y_pred_linear), np.sqrt(mean_squared_error(y_train,y_pred_linear))
r2_q, rmse_q = r2_score(y_train,y_pred_quad), np.sqrt(mean_squared_error(y_train,y_pred_quad))
r2_l_val, rmse_l_val = r2_score(y_val,y_val_pred_linear), np.sqrt(mean_squared_error(y_val,y_val_pred_linear))
r2_q_val, rmse_q_val = r2_score(y_val,y_val_pred_quad), np.sqrt(mean_squared_error(y_val,y_val_pred_quad))
# 4. 输出评估报告 + 0-10°数据对比表
print("="*80)
print("伽利略斜面实验 双模型评估报告 (纯角度版 | 无sin)")
print("="*80)
print(f"【角度线性回归】训练集 R²={r2_l:.4f} | RMSE={rmse_l:.1f} | 验证集 R²={r2_l_val:.4f} | RMSE={rmse_l_val:.1f}")
print(f"【角度二次回归】训练集 R²={r2_q:.4f} | RMSE={rmse_q:.1f} | 验证集 R²={r2_q_val:.4f} | RMSE={rmse_q_val:.1f}")
print("="*80)
print("📊 0°~10° 实测值 & 双模型预测值对比表")
print(pd.DataFrame({
'斜面倾角(°)': data['Angle'][:11],
'实测滚动距离': y_train[:11],
'角度线性预测值': np.round(y_pred_linear[:11],1),
'角度二次预测值': np.round(y_pred_quad[:11],1)
}))
# 5. 可视化:双模型拟合曲线+训练/验证散点 完整绘图+保存
plt.rcParams['font.sans-serif']=['WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus']=False
plt.figure(figsize=(12,7),dpi=120)
plt.scatter(X_train, y_train, c='darkred', s=150, label='训练集(0°~80°)', edgecolors='white', lw=2, zorder=5)
plt.scatter(X_val, y_val, c='navy', s=120, label='验证集(70°~89°)', edgecolors='white', lw=2, zorder=5)
# 绘制0-90°拟合曲线
angle_range = pd.DataFrame({'Angle':np.linspace(0,90,200)})
plt.plot(angle_range, model_linear.predict(angle_range), c='darkblue', lw=3, label=f'角度线性 R²={r2_l:.4f}', zorder=3)
plt.plot(angle_range, model_quad.predict(poly.transform(angle_range)), c='orange', lw=3, ls='--', label=f'角度二次 R²={r2_q:.4f}', zorder=4)
plt.xlabel('斜面倾角 (°)', fontsize=12)
plt.ylabel('小球滚动距离 (刻度)', fontsize=12)
plt.title('伽利略斜面实验 - 角度线性回归 vs 角度二次回归 (0°~90°)', fontsize=14)
plt.legend(loc='upper left', fontsize=11)
plt.grid(alpha=0.3, linestyle='--')
plt.savefig('/home/project/伽利略_纯角度双模型拟合图.png', dpi=120, bbox_inches='tight')
- 0°~10° 实测值与双模型预测值对比
- 二阶多项式回归 明显比 线性 拟合效果好
| 模型类型 | 训练集 R² | 训练集 RMSE | 验证集 R² | 验证集 RMSE |
|---|---|---|---|---|
| 角度线性回归 | 0.9831 | 575.9 | -45.4451 | 1681.2 |
| 角度二次多项式回归 | 0.9996 | 84.2 | 0.0782 | 236.8 |
- 但问题就是
- 训练用的是 红色部分
- 测试用的是 蓝色部分
- 这个模型还可以调优吗?
- 训练集包括角度
- [0,1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80]
# 1. 完整保留原训练集+验证集 无任何修改 (仅保留角度+距离,剔除sin列)
data = pd.DataFrame({'Serial': [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18],
'Angle': [0,1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80],
'Distance': [0,225,450,675,900,1125,1350,1575,1800,2025,2270,4455,6520,8360,9940,11275,12200,12810]})
X_train, y_train = data[['Angle']], data['Distance']
- 测试集包括
- [70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89]
data_val = pd.DataFrame({'Serial': [19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],
'Angle':[70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89],
'Distance':[12200,12278,12351,12419,12482,12540,12593,12641,12684,12722,12810,12844,12874,12900,12922,12940,12955,12966,12975,12980]})
X_val, y_val = data_val[['Angle']], data_val['Distance']
- 集合分布不合理
- 而且相互覆盖
- 随机拆分 训练集/测试集
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import r2_score, mean_squared_error
from sklearn.model_selection import train_test_split
# ✅ 核心要求全部满足:Serial用list(range)生成 + 直接赋值all_data + 无任何拼接
all_data = pd.DataFrame({
'Serial': list(range(1, 39)), # 完美实现:Serial从1到38 自动生成
'Angle': [0,1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89],
'Distance':[0,225,450,675,900,1125,1350,1575,1800,2025,2270,4455,6520,8360,9940,11275,12200,12810,12200,12278,12351,12419,12482,12540,12593,12641,12684,12722,12810,12844,12874,12900,12922,12940,12955,12966,12975,12980]
})
# 提取自变量和因变量 【和你原代码写法完全一致】
X = all_data[['Angle']]
y = all_data['Distance']
# 分层随机拆分:70%训练集,30%验证集 【你原代码参数完全不变】
X_train, X_val, y_train, y_val = train_test_split(
X, y,
test_size=0.3, # 验证集占30%
random_state=42, # 随机种子,结果可复现
stratify=pd.cut(all_data['Angle'], bins=3)
)
# 2. 双模型训练+预测 (原代码完全不变)
model_linear = LinearRegression().fit(X_train, y_train)
y_pred_linear = model_linear.predict(X_train)
y_val_pred_linear = model_linear.predict(X_val)
poly = PolynomialFeatures(degree=2, include_bias=False)
X_train_poly = poly.fit_transform(X_train)
model_quad = LinearRegression().fit(X_train_poly, y_train)
y_pred_quad = model_quad.predict(X_train_poly)
y_val_pred_quad = model_quad.predict(poly.transform(X_val))
# 3. 计算评估指标 R² + RMSE (原代码完全不变)
r2_l, rmse_l = r2_score(y_train,y_pred_linear), np.sqrt(mean_squared_error(y_train,y_pred_linear))
r2_q, rmse_q = r2_score(y_train,y_pred_quad), np.sqrt(mean_squared_error(y_train,y_pred_quad))
r2_l_val, rmse_l_val = r2_score(y_val,y_val_pred_linear), np.sqrt(mean_squared_error(y_val,y_val_pred_linear))
r2_q_val, rmse_q_val = r2_score(y_val,y_val_pred_quad), np.sqrt(mean_squared_error(y_val,y_val_pred_quad))
# 4. 输出评估报告 (原代码完全不变,保留所有打印内容)
print("="*80)
print("伽利略斜面实验 双模型评估报告 (纯角度版 | 分层随机拆分)")
print("="*80)
print(f"【角度线性回归】训练集 R²={r2_l:.4f} | RMSE={rmse_l:.1f} | 验证集 R²={r2_l_val:.4f} | RMSE={rmse_l_val:.1f}")
print(f"【角度二次回归】训练集 R²={r2_q:.4f} | RMSE={rmse_q:.1f} | 验证集 R²={r2_q_val:.4f} | RMSE={rmse_q_val:.1f}")
print("="*80)
# 补充:打印训练集/验证集的角度分布
print("📌 训练集角度范围:", X_train['Angle'].min(), "° ~", X_train['Angle'].max(), "°")
print("📌 验证集角度范围:", X_val['Angle'].min(), "° ~", X_val['Angle'].max(), "°")
print("="*80)
# 5. 可视化:双模型拟合曲线+训练/验证散点 (原代码完全不变,保存路径也不变)
plt.rcParams['font.sans-serif']=['WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus']=False
plt.figure(figsize=(12,7),dpi=120)
plt.scatter(X_train, y_train, c='darkred', s=150, label='训练集(随机70%)', edgecolors='white', lw=2, zorder=5)
plt.scatter(X_val, y_val, c='navy', s=120, label='验证集(随机30%)', edgecolors='white', lw=2, zorder=5, alpha=0.8)
# 绘制0-90°拟合曲线 (原代码完全不变)
angle_range = pd.DataFrame({'Angle':np.linspace(0,90,200)})
plt.plot(angle_range, model_linear.predict(angle_range), c='darkblue', lw=3, label=f'角度线性 R²={r2_l:.4f}', zorder=3)
plt.plot(angle_range, model_quad.predict(poly.transform(angle_range)), c='orange', lw=3, ls='--', label=f'角度二次 R²={r2_q:.4f}', zorder=4)
plt.xlabel('斜面倾角 (°)', fontsize=12)
plt.ylabel('小球滚动距离 (刻度)', fontsize=12)
plt.title('伽利略斜面实验 - 角度线性回归 vs 角度二次回归 (0°~90°)', fontsize=14)
plt.legend(loc='upper left', fontsize=11)
plt.grid(alpha=0.3, linestyle='--')
plt.savefig('/home/project/伽利略_纯角度双模型_随机拆分.png', dpi=120, bbox_inches='tight')
- R²=0.9997 已经很厉害了!
=============================================================
伽利略斜面实验 双模型评估报告 (纯角度版 | 分层随机拆分)
====================================================
【角度线性回归】训练集 R²=0.9856 | RMSE=615.3 | 验证集 R²=0.9690 | RMSE=928.3
【角度二次回归】训练集 R²=0.9997 | RMSE=91.0 | 验证集 R²=0.9993 | RMSE=135.0
=============================================
📌 训练集角度范围: 0 ° ~ 86 °
📌 验证集角度范围: 3 ° ~ 89 °
=================================================
- 还可以有更好的拟合方式吗?🤔
- 我们下次再说👋
- 本文来自 oeasy Python 系统教程。
- 想完整、扎实学 Python,
- 搜索 oeasy 即可。










