附相关系数的配对图
四个学区变量的散点图矩阵,对角线为分组密度,上方为合并相关与组内相关。

GGally::ggpairs() 的 Python 版本。对角线以下,每对变量都有一张按组着色的散点图;对角线展示每个变量在各组中的密度;对角线以上的对称单元格则给出 Pearson r,包括总体(粗体)和各组内的值,并附显著性星号。比较二者正是关键:当各组位于图中不同区域时,合并相关与组内相关可能差异悬殊——甚至符号相反。
数据为 statsmodels 自带的 STAR98 测试中加州 303 个学区(sm.datasets.star98):数学成绩高于全国中位数的学生比例、西班牙裔学生比例、修读大学预备(UC/CSU)课程的学生比例,以及教师平均从教年数。学区按低收入学生比例分为三等份(pd.qcut,每组 101 个),因此组内系数大致控制了贫困水平:西班牙裔比例与数学成绩之间的关联总体为 −0.71,而在每个贫困组内都更弱。密度使用 scipy.stats.gaussian_kde,相关系数使用 scipy.stats.pearsonr;网格是简单的 plt.subplots(4, 4)。
代码及图中文字均为英文。
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import statsmodels.api as sm
from scipy import stats
# Data: 303 California school districts (STAR98), grouped by thirds of low-income pupils
raw = sm.datasets.star98.load_pandas().data
df = pd.DataFrame({"Math above median (%)": 100 * raw.NABOVE / (raw.NABOVE + raw.NBELOW),
"Hispanic pupils (%)": raw.PERHISP, "College-prep (%)": raw.PCTAF,
"Teacher experience (yrs)": raw.AVYRSEXP})
groups = ["Low poverty", "Middle", "High poverty"]
df["group"] = pd.qcut(raw.LOWINC, 3, labels=groups).astype(str)
cols = df.columns[:4]
palette = ["#007AFF", "#FF9500", "#34C759"]
stars = lambda p: "***" if p < .001 else "**" if p < .01 else "*" if p < .05 else ""
fig, axes = plt.subplots(4, 4, figsize=(9.5, 8.6), sharex="col")
lims = {c: (df[c].min() - 0.05 * np.ptp(df[c]), df[c].max() + 0.05 * np.ptp(df[c])) for c in cols}
fig.get_layout_engine().set(rect=(0, 0, 1, 0.92), w_pad=0.04, h_pad=0.04)
for i, yv in enumerate(cols):
for j, xv in enumerate(cols):
ax = axes[i, j]
ax.grid(False)
if i == j: # diagonal: KDE per group
grid = np.linspace(*lims[xv], 200)
for s, c in zip(groups, palette):
dens = stats.gaussian_kde(df.loc[df.group == s, xv])(grid)
ax.fill_between(grid, dens, color=c, alpha=0.25, lw=0)
ax.plot(grid, dens, color=c, lw=1.3)
ax.set_yticks([])
elif i > j: # lower: scatter by group
for s, c in zip(groups, palette):
d = df[df.group == s]
ax.scatter(d[xv], d[yv], s=12, color=c, alpha=0.65, lw=0)
ax.set_ylim(lims[yv])
else: # upper: correlation coefficients
r, p = stats.pearsonr(df[xv], df[yv])
ax.text(0.5, 0.78, f"r = {r:.2f}{stars(p)}", ha="center", fontsize=11,
fontweight="semibold", transform=ax.transAxes)
for k, (s, c) in enumerate(zip(groups, palette)):
d = df[df.group == s]
rs, ps = stats.pearsonr(d[xv], d[yv])
ax.text(0.5, 0.52 - k * 0.17, f"{s}: {rs:.2f}{stars(ps)}", ha="center",
fontsize=9, color=c, transform=ax.transAxes)
ax.tick_params(labelbottom=False, labelleft=False)
for sp in ax.spines.values():
sp.set_visible(False)
if i < 3:
ax.tick_params(labelbottom=False)
if j > 0 or i == 0:
ax.tick_params(labelleft=False)
if i == 3:
ax.set_xlabel(xv)
ax.set_xlim(lims[xv])
if j == 0:
ax.set_ylabel(yv)
fig.text(0.01, 0.985, "Part of the pooled correlations runs through poverty", fontsize=13,
fontweight="semibold", va="top")
fig.text(0.01, 0.945, f"Pearson r, overall (bold) and within thirds of low-income pupils · n = {len(df)} "
"districts, 101 per group · * p<.05 ** p<.01 *** p<.001", fontsize=9.5, color="#6E6E73", va="top")
plt.show()
图表使用了可选的 PyStatlas 样式:添加 plt.style.use("pystatlas.mplstyle") 即可完全还原其外观。
数据来源
- California STAR test results, 1998 (303 school districts). Gill, J. (2001). Generalized Linear Models: A Unified Approach (Quantitative Applications in the Social Sciences, No. 134). Sage. https://www.statsmodels.org/stable/datasets/generated/star98.html
Original data from the California Department of Education's Standardized Testing and Reporting (STAR) program, 1998. Subset distributed with statsmodels (sm.datasets.star98) with the author's permission.
这些示例用于说明统计方法。图表标题描述的是该图在这些特定数据中呈现的内容——这些数据往往样本较小、年代较早或并不完整——而不是作者对实质问题的立场。关于解释、背景和数据质量,请以此处引用的原始来源为准。
如何引用
PyStatlas 由 Thierry Warin 创建并维护。如果它对你的工作有所帮助,敬请引用。
Warin, T. (2026). Pairs plot with correlations. In PyStatlas: A Python Gallery of Statistical Graphics for the Social Sciences. https://warint.github.io/pystatlas/correlation/pairs-plot/
@misc{warin2026statlas,
author = {Warin, Thierry},
title = {PyStatlas: A Python Gallery of Statistical Graphics for the Social Sciences},
year = {2026},
howpublished = {\url{https://warint.github.io/pystatlas/}},
note = {Source code: https://github.com/warint/pystatlas}
}