如何用probplot绘制多组不同颜色数据的QQ图(Henry直线)
多组数据的彩色QQ图(Henry直线)绘制方案
问题背景
处理服从正态分布的多组数据(子列表对应一组样本),需要绘制QQ图(Henry直线)验证正态性,要求同一张图上用不同颜色区分各组数据,同时保留整体拟合的Henry直线。
现有单颜色合并数据QQ图代码(可运行):
import numpy as np import matplotlib.pyplot as plt from scipy import stats Listes = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11,12,13,14,15]] Liste_cumulee = np.array([]) for i in range(len(Listes)): Liste_cumulee = np.concatenate(( Liste_cumulee, Listes[i] ), axis = None) plt.figure() probplot(Liste_cumulee, dist="norm", plot=plt) plt.title("Droite de Henry") plt.xlabel("Quantiles théoriques") plt.ylabel("Quantiles observés") plt.grid() plt.show()
尝试分组绘制时失败,错误代码如下:
import numpy as np import matplotlib.pyplot as plt from scipy import stats Listes = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11,12,13,14,15]] Liste_cumulee = np.array([]) for i in range(len(Listes)): Liste_cumulee = np.concatenate(( Liste_cumulee, Listes[i] ), axis = None) # Ajustement de la droite de Henry aux données concaténées (_, _), (_, intercept, r) = stats.probplot(Liste_cumulee, dist="norm", plot=None) # Tracé de la droite de Henry plt.figure() plt.title(f"Droite de Henry (r={r:.2f})") plt.xlabel("Quantiles théoriques") plt.ylabel("Quantiles observés") plt.grid() # Tracé des points pour chaque lot de données avec des couleurs différentes for i, lot in enumerate(Listes): plt.plot(np.sort(lot), stats.norm.ppf(np.linspace(0.01, 0.99, len(lot))), marker='o', linestyle='', label=f'Lot {i+1}', color=plt.cm.tab10(i)) # Affichage du graphique plt.legend() plt.show()
错误原因
- 坐标轴顺序颠倒:QQ图要求x轴是理论分位数,y轴是观测分位数,错误代码中把观测分位数放在x轴、理论分位数放在y轴,导致图像逻辑完全错误。
- 理论分位数计算方式不符:
stats.probplot默认使用Blom变换计算分位数位置,而非简单的linspace(0.01, 0.99, len(lot)),这会导致分位数点和拟合直线不匹配。
正确实现方案
核心思路:
- 先通过合并数据计算整体的Henry拟合直线参数
- 循环处理每组数据,用
stats.probplot获取该组的理论分位数和观测分位数,再绘制彩色散点 - 最后绘制拟合直线,添加图例和标注
完整代码:
import numpy as np import matplotlib.pyplot as plt from scipy import stats Listes = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11,12,13,14,15]] Liste_cumulee = np.concatenate(Listes) # 简化合并操作 # 1. 计算整体拟合的Henry直线参数 (quantiles_theoriques_tot, quantiles_observes_tot), (slope, intercept, r) = stats.probplot( Liste_cumulee, dist="norm", plot=None ) # 2. 初始化绘图 plt.figure(figsize=(8, 6)) plt.title(f"Droite de Henry (r={r:.2f})") plt.xlabel("Quantiles théoriques") plt.ylabel("Quantiles observés") plt.grid(True, alpha=0.3) # 3. 循环绘制每组数据的彩色散点 colors = plt.cm.tab10(np.arange(len(Listes))) # 获取一组区分度高的颜色 for idx, (lot, color) in enumerate(zip(Listes, colors)): # 获取当前组的理论分位数和观测分位数 qq_data = stats.probplot(lot, dist="norm", plot=None) q_theo, q_obs = qq_data[0] # 绘制散点 plt.scatter(q_theo, q_obs, marker='o', color=color, label=f'Lot {idx+1}') # 4. 绘制Henry拟合直线 x_min, x_max = plt.xlim() y_min = slope * x_min + intercept y_max = slope * x_max + intercept plt.plot([x_min, x_max], [y_min, y_max], color='k', linestyle='--', label='Droite de Henry') # 5. 添加图例并展示 plt.legend() plt.show()
代码说明
- 用
np.concatenate(Listes)简化数据合并操作 - 每组数据通过
stats.probplot(..., plot=None)获取对应的理论/观测分位数,保证和拟合直线的分位数计算逻辑一致 - 使用
plt.scatter替代plt.plot更适合绘制散点,避免不必要的连线 - 拟合直线通过计算x轴两端点对应的y值绘制,确保直线贯穿整个图表范围
内容的提问来源于stack exchange,提问作者Hélène BRNT
相关产品推荐
相关产品推荐

