统计问题|绘制任意分布的 QQ 图

简介: 统计问题|绘制任意分布的 QQ 图

简介

最近科研论文的审稿意见,需要对数据的拟合情况进行说明。分位数-分位数图[1](Quantile-Quantile plot,简称 QQ 图)是一种不错的选择。

研究动机

在 R 语言中常用 qqnorm() 绘制 QQ 图的散点图,qqline() 用于在图上添加一条参考直线。可支持与一些常见的理论分布进行比较,例如:正态分布 ("norm")、指数分布 ("exp")、伽马分布 ("gamma")、威布尔分布 ("weibull")、对数正态分布 ("lnorm")等。

但是绘制出来的图形可能不是很美观,如下所示:

本文贡献点

当然小编以前使用 ggplot 绘制过:绘制不同分布的 QQ 图,但内容不包括逆高斯分布以及其他比较“新”的分布。

基于这个痛点,本文小编将根据 QQ 图的定义,通过 ggplot2[2] 复现自己常用的三个分布(正态、伽马和逆高斯分布)的 QQ 图。

题外话:读者可以根据本文思路,拓展到任何自己感兴趣的分布。

教程

理论介绍

介绍:一种用于检查数据是否符合某个理论分布的统计图。

基本思想:比较观察数据的分位数与理论分布的分位数。

判断依据:如果数据符合理论分布,那么 QQ 图上的点将近似落在一条直线上。

绘制步骤

  1. 排序数据:将观察数据按照大小进行排序。
  2. 计算分位数:计算每个观察数据点对应的分位数。这可以通过排名除以总数据点的数量来实现。
  3. 计算理论分位数:使用所选择的理论分布的累积分布函数,计算对应于每个观察数据点的理论分位数。
  4. 绘制 QQ 图:将观察数据的分位数作为横坐标,理论分位数作为纵坐标,绘制散点图。如果数据符合理论分布,散点图将近似为一条直线。

代码实现

根据上面的理论结果,我们依次进行代码实现。假设数据来自于 gamma 分布。

y <- rgamma(200,10,1)
  1. 排序数据
sorted_data <- sort(y)
  1. 计算样本分位数
sample_quantiles <- (1:length(sorted_data) - 0.5) / length(sorted_data)
  1. 计算理论分位数

首先,对数据进行参数估计,得到估计之后,产生理论分位数。

3.1 参数估计

这里使用极大似然方法估计参数,首先定义对数似然函数:

library(statmod)
fn_ig <- function(par= c(mean, dispersion), data) {
  ll <- -sum(dinvgauss(data, mean= par[1], dispersion= par[2], log= TRUE))   
  return(ll)
}

3.2 参数估计,并计算理论分位数:

pp <- optim(par= c(1,1), fn_ig, data = y)
mq <- pp$par[1]; disp <- pp$par[2] 
theoretical_quantiles <- qinvgauss(sample_quantiles, mean= mq, dispersion= disp)
  1. 绘制 QQ 图

数据整理并绘制

#数据整理
data_ig = data.frame("x" = theoretical_quantiles, "y" = sorted_data)
#绘制
library(ggplot2)
ggplot(data_ig,aes(x=x, y=y)) +
  geom_point(color="#4CA6A3",size=1.2) + 
  geom_abline(intercept = 0, slope = 1,color="#693476",size=1) + 
  # facet_wrap(vars(factor(pc)),scales = "free") + 
  theme_bw() + theme(legend.position = "none") + 
  xlab("Theoretical") + ylab("Sample")

此时,给出了该数据使用逆高斯分布拟合的情况,结果还算可以!读者可以通过一些检验(Kolmogorov–Smirno[3]等),进行进一步检测。

拓展

根据这个思路,小编写了正态和 Gamma 的结果,并通过一个函数将三种分布的整合在一起。最后绘制出三种分布拟合该数据的结果。

还计算了三种分布拟合的 KS 检验的 p 值:

p 值均大于 0.05,接受原假设,即三种分布拟合该数据均可。读者可以将合并的图放到自己论文中,仅需将 y 修改成自己的数据即可!

汇总代码

y <- rgamma(200,10,1)
library(statmod)
fn_ig <- function(par= c(mean, dispersion), data) {
  ll <- -sum(dinvgauss(data, mean= par[1], dispersion= par[2], log= TRUE))   
  return(ll)
}
fn_normal <- function(par = c(mean, sd), data) {
  # 正态分布的对数似然函数
  ll <- -sum(dnorm(data, mean = par[1], sd = par[2], log = TRUE))
  return(ll)
}
fn_gamma <- function(par = c(shape, rate), data) {
  # 伽马分布的对数似然函数
  ll <- -sum(dgamma(data, shape = par[1], rate = par[2], log = TRUE))
  return(ll)
}
qqdat = function(den = "ig", y = diff_y_all, par= c(1, 1)){
  # 排序数据
  sorted_data <- sort(y)
  # 计算样本分位数
  sample_quantiles <- (1:length(sorted_data) - 0.5) / length(sorted_data)
  if(den == "ig"){
    # 参数估计
    pp <- optim(par= par, fn_ig, data = y)
    mq <- pp$par[1]; disp <- pp$par[2] 
    # 计算理论分位数
    theoretical_quantiles <- qinvgauss(sample_quantiles, mean= mq, dispersion= disp)
  } else if(den == "norm"){
    pp <- optim(par= par, fn_normal, data = y)
    mq <- pp$par[1]; disp <- pp$par[2] 
    theoretical_quantiles <- qnorm(sample_quantiles, mq, disp)
  } else if(den == "gamma"){
    pp <- optim(par= par, fn_gamma, data = y)
    mq <- pp$par[1]; disp <- pp$par[2] 
    theoretical_quantiles <- qgamma(sample_quantiles, shape = mq, rate = disp)
  }
  data_ig = data.frame("x" = theoretical_quantiles, "y" = sorted_data)
  return(list(data_ig,"Para"= c(mq,disp)))
}
# 数据处理
qqplot_den = function(den = "norm",data6 = y,par= c(10, 1)){
  u1 = qqdat(den = den, data6, par= par)
  ig_g = data.frame(u1[[1]])
  w1 = ggplot(ig_g,aes(x=x, y=y)) +
    geom_point(color="#4CA6A3",size=1.2) + 
    geom_abline(intercept = 0, slope = 1,color="#693476",size=1) + 
    theme_bw() + theme(legend.position = "none") + 
    xlab("Theoretical") + ylab("Sample")
  if(den=="norm"){
    kstest = ks.test(data6, "pnorm", mean = u1$Para[1], u1$Para[2])$p.value
  } else if(den=="ig"){
    library(fitdistrplus)
    kstest = ks.test(data6, "pinvgauss", mean = u1$Para[1], dispersion = u1$Para[2])$p.value
  } else if(den=="gamma"){
    kstest = ks.test(data6, "pgamma", u1$Para[1], u1$Para[2])$p.value
  }
  return(list(w1,kstest))
}
w1 = qqplot_den(den = "norm",data6 = y,par= c(10, 1))
w2 = qqplot_den(den = "ig",data6 =  y,par= c(1, 1))
w3 = qqplot_den(den = "gamma",data6 =  y,par= c(1, 1))
cowplot::plot_grid(w1[[1]],w2[[1]],w3[[1]],labels=c("N","IG","G"),nrow=1)
# ggsave("qqplot.pdf", width = 8,height = 4)

参考资料

[1]

分位数-分位数图: https://haomenghit.github.io/2019/08/04/Q-Q%E5%9B%BE%E4%BB%8B%E7%BB%8D/

[2]

ggplot2: https://ggplot2.tidyverse.org/

[3]

Kolmogorov–Smirno: https://en.wikipedia.org/wiki/Kolmogorov%E2%80%93Smirnov_test

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
6月前
|
XML 存储 数据处理
python绘制热力图-数据处理-VOC数据类别标签分布及数量统计(附代码)
python绘制热力图-数据处理-VOC数据类别标签分布及数量统计(附代码)
|
数据可视化
R绘图 | 包含/比例关系环图
R绘图 | 包含/比例关系环图
155 0
|
30天前
|
Python
将NC栅格表示时间维度的数据提取出来的方法
【10月更文挑战第20天】本文介绍了如何使用 Python 和 R 语言以及 ArcGIS 软件提取 netCDF 文件中的时间维度数据。首先,通过安装和导入必要的库(如 Python 的 `netCDF4` 和 `numpy`,R 的 `ncdf4`),打开 netCDF 文件并读取时间变量。接着,详细展示了 Python 和 R 的示例代码,说明了如何读取和处理时间数据。最后,介绍了在 ArcGIS 中添加 netCDF 文件、启用时间属性并提取时间维度数据的方法。
|
6月前
GEE图表——趋势线图表的加载和展示包含纵坐标间隔的设定(以某区域年均降水总量为例)
GEE图表——趋势线图表的加载和展示包含纵坐标间隔的设定(以某区域年均降水总量为例)
84 0
跟着 Cell 学作图 | 分组蜂群图+均值线+差异分析(组间+组内)
跟着 Cell 学作图 | 分组蜂群图+均值线+差异分析(组间+组内)
154 0
|
数据挖掘
跟着 NC 学作图 | 多组散点图+配对连线+差异分析
跟着 NC 学作图 | 多组散点图+配对连线+差异分析
210 0
|
数据挖掘
跟着 Cell 学作图 | 分组抖动散点图+差异分析
跟着 Cell 学作图 | 分组抖动散点图+差异分析
106 0
|
传感器 编解码 计算机视觉
使用星凸随机超曲面模型对扩展对象和分组目标进行形状跟踪(Matlab代码实现)
使用星凸随机超曲面模型对扩展对象和分组目标进行形状跟踪(Matlab代码实现)
138 0
使用星凸随机超曲面模型对扩展对象和分组目标进行形状跟踪(Matlab代码实现)
R语言绘制组间比较散点图并自动添加P值信息
查询ggprism包使用时候发现官网给出的一示例图比较常用,这里记录学习一下。
220 3
|
数据可视化
scRNA分析|自定义你的箱线图-统计检验,添加p值,分组比较p值
scRNA分析|自定义你的箱线图-统计检验,添加p值,分组比较p值
253 0