PAYATHON 2026

如何在 Python 中拟合线性回归并正确计算指标?

数据小陈

结论

计算指标时,将观测值 HumOb 作为真实值 y_true,将预测值 HumFc 作为预测值 y_pred,再计算 MAE、RMSE 和 R²。mean_absolute_error 返回的结果不会是负数。因此,如果输出中出现 MAE: -18.1903,这个数值就不是该函数计算出的 MAE。当前代码还把 r2_score 的参数顺序写反了,算出的 R² 因而反映了另一个方向的拟合效果。

如果要查看 HumOb 和 HumFc 之间的线性关系,可以绘制散点图和回归线。日期折线图展示的是数值随时间的变化,表达的是另一类信息。下面的代码把两种图放在同一张图的两个子图中。

修正数据读取和指标计算

调用 r2_score(y_true, y_pred) 时,第一个参数应为观测值,第二个参数应为模型预测值。MAE 对参数顺序不敏感,因为绝对误差会消除符号;R² 则会受参数顺序影响。

原代码用不同格式对 Date 转换了两次。数据中的日期格式是 YYYYMMDD,按 %Y%m%d 解析一次即可。原脚本里的 quit() 也会在绘图代码运行前结束程序,应将其删除。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

# 将路径替换为 CSV 文件的实际位置
 df = pd.read_csv("Hum_h5d_06.csv", skipinitialspace=True)

上面的读取语句行首多了一个空格。去掉后,完整代码如下:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

# 将路径替换为 CSV 文件的实际位置
df = pd.read_csv("Hum_h5d_06.csv", skipinitialspace=True)
df["Date"] = pd.to_datetime(df["Date"].astype(str), format="%Y%m%d")

# 评估现有预测值:真实值是 HumOb,预测值是 HumFc
y_true = df["HumOb"]
y_pred = df["HumFc"]
mae = mean_absolute_error(y_true, y_pred)
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
r2 = r2_score(y_true, y_pred)

print(f"MAE: {mae:.4f}")
print(f"RMSE: {rmse:.4f}")
print(f"R²: {r2:.4f}")

# 拟合 HumOb 与 HumFc 的线性关系:HumFc = slope * HumOb + intercept
X = df[["HumOb"]]
y = df["HumFc"]
model = LinearRegression()
model.fit(X, y)

# 按 x 值排序后绘制拟合线,避免线段来回连接
plot_df = df.sort_values("HumOb")
fit_values = model.predict(plot_df[["HumOb"]])

fig, (ax_time, ax_fit) = plt.subplots(1, 2, figsize=(12, 5))

# 左图:按日期查看观测值和现有预测值
ax_time.plot(df["Date"], y_true, color="b", marker="o", label="HumOb")
ax_time.plot(df["Date"], y_pred, color="r", marker="o", label="HumFc")
ax_time.set_title("Humidity by Date")
ax_time.set_xlabel("Date")
ax_time.set_ylabel("Humidity")
ax_time.xaxis.set_major_formatter(mdates.DateFormatter("%b %d"))
ax_time.legend()
ax_time.grid(True)
fig.autofmt_xdate()

metrics_text = f"MAE: {mae:.3f}\nRMSE: {rmse:.3f}\nR²: {r2:.3f}"
ax_time.annotate(
    metrics_text,
    xy=(0.03, 0.97),
    xycoords="axes fraction",
    va="top",
    bbox=dict(boxstyle="round,pad=0.5", fc="white", alpha=0.8),
)

# 右图:散点及 HumOb 到 HumFc 的线性回归拟合
ax_fit.scatter(df["HumOb"], df["HumFc"], label="Data")
ax_fit.plot(plot_df["HumOb"], fit_values, color="green", label="Linear fit")
ax_fit.set_title("Forecast vs. Observation")
ax_fit.set_xlabel("HumOb (observation)")
ax_fit.set_ylabel("HumFc (forecast)")
ax_fit.legend()
ax_fit.grid(True)

plt.tight_layout()
plt.show()

LinearRegression 拟合的是 HumFc = slope × HumOb + intercept,用于描述预测值与观测值的线性关系。它不是按日期拟合的趋势线,也不会自动替换现有预测值。图中标注的 MAE、RMSE 和 R² 衡量的是现有 HumFc 与 HumOb 之间的预测误差。

指标为何会出现负值

MAE 是绝对误差的平均值,因此不会小于零。使用 mean_absolute_error(y_true, y_pred) 时,结果应为非负数。如果要看预测值总体偏高还是偏低,可以另行计算带符号的平均误差:

bias = (y_pred - y_true).mean()
print(f"Bias: {bias:.4f}")

Bias 可以为负数。负值表示预测值的平均值低于观测值。Bias 和 MAE 是不同的指标。

R² 则可能为负数。负值表示当前预测结果比“始终预测观测值均值”的基准更差,这本身不能说明计算出错。计算时应使用 r2_score(y_true, y_pred),不要交换参数。如果要评估回归线对样本的拟合程度,可以查看 model.score(X, y)。它衡量的是 HumFc 对 HumOb 的回归拟合,与时间序列预测指标含义不同。

注意事项

提供的数据中,20260201 出现了两次。如果两行是重复记录,是否删除应由数据来源决定;如果它们代表不同观测,则应保留。重复记录会影响回归系数和指标。

示例假设 CSV 文件包含列名 Date,HumFc,HumOb,日期格式为 %Y%m%d。如果列名、日期格式或文件路径不同,需要相应调整读取和解析代码。

备注:内容仅供参考。