超越基础散点图:用Statsmodels的Lowess为你的数据添加平滑趋势线(附美国犯罪率案例)
2026/6/2 14:15:09 网站建设 项目流程

超越基础散点图:用Statsmodels的Lowess为你的数据添加平滑趋势线(附美国犯罪率案例)

当数据点像夜空中的繁星般散落在图表上时,我们常常需要一根"星座连线"来揭示隐藏的规律。这就是Lowess平滑技术的魅力所在——它不假设数据遵循某种特定数学模型,而是让数据自己"讲述"它的趋势故事。

1. 为什么散点图需要趋势线?

原始散点图就像未经加工的钻石原石,虽然包含宝贵信息,但需要专业切割才能展现真正价值。在美国犯罪率数据集中,每个州都是独立的点,谋杀率(X轴)与入室盗窃率(Y轴)的关系被数据噪声和离群值所掩盖。

关键痛点

  • 人眼难以准确判断密集点群的总体趋势
  • 异常值会扭曲对整体关系的判断
  • 非线性关系在原始散点中难以辨识

提示:当数据点超过50个时,添加趋势线几乎总是能提升图表的信息传达效率。

2. Lowess原理:让数据自述其故事

局部加权散点平滑(Locally Weighted Scatterplot Smoothing)是一种非参数回归方法,它不像传统线性回归那样强加全局假设。其核心思想可以用三个关键词概括:

  1. 局部:对每个点的估计只使用邻近数据
  2. 加权:距离越近的点权重越高
  3. 稳健:通过迭代降低异常值影响

技术参数对比表:

参数典型值作用调整建议
frac0.66平滑窗口比例数据噪声大时减小
it3稳健迭代次数异常值多时增加
delta0.01计算效率参数大数据集可提高
# Lowess核心算法伪代码 def lowess(x, y, frac=0.66, it=3): for iteration in range(it): for i in range(len(x)): # 1. 计算当前点的邻域半径 # 2. 根据距离计算权重(三次方函数) # 3. 加权最小二乘拟合局部多项式 # 4. 存储拟合值 # 异常值检测与权重调整 return smoothed_y

3. 实战:为犯罪率数据添加Lowess曲线

让我们用Python实现一个专业级的可视化方案。这个方案不仅完成基础绘图,还包含多个实用技巧:

import pandas as pd import matplotlib.pyplot as plt import statsmodels.api as sm from matplotlib import font_manager # 中文显示解决方案 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 数据准备 crime = pd.read_csv("crimeRatesByState2005.csv") clean_data = crime[~crime['state'].isin(['District of Columbia','United States'])] # 创建专业级图表 fig, ax = plt.subplots(figsize=(10, 6), dpi=120) ax.scatter(clean_data["murder"], clean_data["burglary"], marker="*", color="#00CC88", s=80, label="各州数据") # Lowess平滑 lowess = sm.nonparametric.lowess( clean_data["burglary"], clean_data["murder"], frac=0.6, # 更敏感的局部拟合 it=4 # 加强异常值抵抗 ) ax.plot(lowess[:, 0], lowess[:, 1], color="#FF6600", linewidth=3, label="Lowess趋势线") # 图表优化 ax.set_xlim(0, 10) ax.set_ylim(0, 1200) ax.set_xlabel("谋杀率(每十万人)", fontsize=12, labelpad=10) ax.set_ylabel("入室盗窃率(每十万人)", fontsize=12, labelpad=10) ax.set_title("美国各州谋杀率与入室盗窃率关系(2005)", fontsize=14, pad=20) ax.grid(True, linestyle="--", alpha=0.6) ax.legend(loc="upper right", framealpha=0.9) # 添加数据来源标注 fig.text(0.95, 0.02, "数据来源: 美国司法部2005年度报告", ha="right", va="bottom", fontsize=9) plt.tight_layout() plt.savefig("crime_with_lowess.png", bbox_inches="tight")

代码亮点解析

  • 使用dpi=120确保输出图像清晰度
  • 通过s参数调整散点大小增强可读性
  • 橙色趋势线与绿色散点形成视觉对比
  • 添加数据来源标注提升专业性

4. 高级应用:Lowess参数调优指南

不同的frac参数会产生截然不同的趋势线:

# 测试不同平滑参数 frac_values = [0.3, 0.5, 0.7, 0.9] colors = ["#FF9933", "#FF6600", "#CC3300", "#990000"] plt.figure(figsize=(12, 6)) for frac, color in zip(frac_values, colors): lowess = sm.nonparametric.lowess( clean_data["burglary"], clean_data["murder"], frac=frac ) plt.plot(lowess[:, 0], lowess[:, 1], color=color, linewidth=2, label=f"frac={frac}") plt.scatter(clean_data["murder"], clean_data["burglary"], color="#00CC88", alpha=0.7) plt.legend(title="平滑参数") plt.title("不同frac参数对Lowess曲线的影响", fontsize=14) plt.xlabel("谋杀率") plt.ylabel("入室盗窃率")

参数选择经验法则

  • 密集线性数据:0.6-0.8
  • 稀疏非线性数据:0.3-0.5
  • 强噪声数据:增加迭代次数(it=4-5)

5. 超越基础:复合可视化技巧

真正的数据分析高手往往通过图层叠加来增强信息密度。以下是三种进阶技巧:

技巧一:置信区间展示

from sklearn.utils import resample import numpy as np # 自助法计算置信区间 n_iterations = 100 frac = 0.6 smooth_curves = [] for _ in range(n_iterations): sample = resample(clean_data) lowess = sm.nonparametric.lowess( sample["burglary"], sample["murder"], frac=frac ) smooth_curves.append(lowess[:, 1]) mean_curve = np.mean(smooth_curves, axis=0) std_curve = np.std(smooth_curves, axis=0) plt.fill_between(lowess[:, 0], mean_curve - 1.96*std_curve, mean_curve + 1.96*std_curve, color="#FF6600", alpha=0.2)

技巧二:关键点标注

# 找出异常点 residuals = clean_data["burglary"] - np.interp( clean_data["murder"], lowess[:, 0], lowess[:, 1]) outliers = clean_data[abs(residuals) > 2*residuals.std()] for _, row in outliers.iterrows(): plt.annotate(row["state"], (row["murder"], row["burglary"]), textcoords="offset points", xytext=(5,5), ha="left")

技巧三:多趋势线对比

# 按地区分组拟合 regions = clean_data["region"].unique() colors = plt.cm.tab10.colors for region, color in zip(regions, colors): subset = clean_data[clean_data["region"] == region] lowess = sm.nonparametric.lowess( subset["burglary"], subset["murder"], frac=0.7) plt.plot(lowess[:, 0], lowess[:, 1], color=color, linewidth=2, label=region)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询