箱线图等效于重尾分布?

机器算法验证 r 分布 数据可视化 数据转换 ggplot2
2022-03-20 20:19:06

对于近似正态分布的数据,箱线图是快速可视化数据的中位数和分布以及任何异常值的存在的好方法。

然而,对于更多的重尾分布,很多点都显示为异常值,因为异常值被定义为超出 IQR 的固定因子,而且这种情况在重尾分布中当然更频繁地发生。

那么人们用什么来可视化这种数据呢?有更适合的吗?如果这很重要,我在 R 上使用 ggplot。

4个回答

OP 似乎存在的核心问题是它们有非常重的尾数据 - 我认为目前的大多数答案实际上根本没有解决这个问题所以我正在推广我之前对答案的评论。

如果您确实想保留箱线图,下面列出了一些选项。我在 R 中创建了一些显示基本问题的数据:

 set.seed(seed=7513870)
 x <- rcauchy(80)
 boxplot(x,horizontal=TRUE,boxwex=.7)

不满意的箱线图

数据的中间一半被缩减为几毫米宽的小条。同样的问题困扰着大多数其他建议 - 包括 QQ 图、条形图、蜂箱/蜂群图和小提琴图。

现在一些潜在的解决方案:

1)转换

如果对数或倒数产生可读的箱线图,它们可能是一个非常好的主意,并且原始比例仍然可以显示在轴上。

最大的问题是有时没有“直观”的转换。还有一个较小的问题,虽然分位数本身可以很好地进行单调变换,但栅栏却没有。如果您只是对转换后的数据进行箱线图绘制(就像我在这里所做的那样),则晶须的 x 值将与原始图中的不同。

转换值的箱线图

这里我使用了一个反双曲sin(asinh);它在尾部有点像对数,类似于接近零的线性,但人们通常不觉得它是一种直观的转换,所以一般我不会推荐这个选项,除非像对数这样的相当直观的转换很明显。代码:

xlab <- c(-60,-20,-10,-5,-2,-1,0,1,2,5,10,20,40)
boxplot(asinh(x),horizontal=TRUE,boxwex=.7,axes=FALSE,frame.plot=TRUE)
axis(1,at=asinh(xlab),labels=xlab)

2) 比例中断 - 采用极端异常值并将它们压缩到两端的窄窗口中,其压缩比例比中心要大得多。如果你这样做,我强烈建议你在整个规模上完全打破。

带有比例中断的箱线图

opar <- par()
layout(matrix(1:3,nr=1,nc=3),heights=c(1,1,1),widths=c(1,6,1))
par(oma = c(5,4,0,0) + 0.1,mar = c(0,0,1,1) + 0.1)
stripchart(x[x< -4],pch=1,cex=1,xlim=c(-80,-5))
boxplot(x[abs(x)<4],horizontal=TRUE,ylim=c(-4,4),at=0,boxwex=.7,cex=1)
stripchart(x[x> 4],pch=1,cex=1,xlim=c(5,80))
par(opar)

3)修剪极端异常值(如果没有非常清楚地表明这一点,我通常不会建议这样做,但它看起来像下一个情节,两端没有“<5”和“2>”),以及

4)我称之为极端异常值的“箭头” - 类似于修剪,但在每一端指示修剪的值的计数

带有极值计数和指向极值的箭头的箱线图

xout <- boxplot(x,range=3,horizontal=TRUE)$out
xin <- x[!(x %in% xout)]
noutl <- sum(xout<median(x))
nouth <- sum(xout>median(x))
boxplot(xin,horizontal=TRUE,ylim=c(min(xin)*1.15,max(xin)*1.15))
text(x=max(xin)*1.17,y=1,labels=paste0(as.character(nouth)," >"))
text(x=min(xin)*1.17,y=1,labels=paste0("< ",as.character(noutl)))

就我个人而言,我喜欢使用带抖动的带状图,至少可以感受一下数据。下面的图是 R 中的格子(抱歉不是 ggplot2)。我喜欢这些情节,因为它们很容易解释。正如您所说,其中一个原因是没有任何转换。

df <- data.frame(y1 = c(rnorm(100),-4:4), y2 = c(rnorm(100),-5:3), y3 = c(rnorm(100),-3:5))
df2 <- stack(df)
library(lattice)
stripplot(df2$values ~ df2$ind, jitter=T)

在此处输入图像描述

beeswarm包为stripplot提供了一个很好的替代方案(感谢@January 的建议)。

beeswarm(df2$values ~ df2$ind)

在此处输入图像描述

使用您的数据,因为它大致呈正态分布,在这种情况下,另一件要尝试的事情可能是 qqplot,qqnorm

par(mfrow=c(1,3))
for(i in 1:3) { qqnorm(df[,i]); abline(c(0,0),1,col="red") }

在此处输入图像描述

您可以坚持使用箱线图。定义晶须有不同的可能性。根据尾部厚度、样本数量和对异常值的容忍度,您可以选择两个或多或少的极端分位数。鉴于您的问题,我会避免通过 IQR 定义胡须。
当然,除非你想转换你的数据,在这种情况下,这会使理解变得更加困难。

我认为这个问题是关于理解数据(不是“管理”
数据geom_violingeom_jitter