两个有序变量之间的关系图

机器算法验证 数据可视化 分类数据 序数数据 散点图
2022-01-28 06:23:07

什么是合适的图表来说明两个序数变量之间的关系?

我能想到的几个选项:

  1. 添加随机抖动的散点图以停止点相互隐藏。显然是标准图形 - Minitab 将此称为“个体值图”。在我看来,这可能会产生误导,因为它在视觉上鼓励了序数级别之间的一种线性插值,就好像数据来自区间尺度一样。
  2. 散点图经过调整,点的大小(面积)表示该级别组合的频率,而不是为每个采样单元绘制一个点。我在实践中偶尔会看到这样的情节。它们可能难以阅读,但这些点位于规则间隔的格上,这在一定程度上克服了对抖动散点图的批评,即它在视觉上“间隔”了数据。
  3. 特别是如果其中一个变量被视为因变量,则按自变量的水平分组的箱线图。如果因变量的水平数不够高,可能看起来很糟糕(非常“平坦”,缺少胡须,甚至更糟糕的四分位数塌陷,这使得视觉识别中位数变得不可能),但至少引起了对中位数和四分位数的注意,它们是序数变量的相关描述性统计。
  4. 值表或带有热图的单元格空白网格以指示频率。视觉上不同但概念上类似于散点图,点区域显示频率。

还有其他想法,或关于哪些情节更可取的想法?是否有任何研究领域将某些序数与序数图视为标准?(我似乎记得频率热图在基因组学中很普遍,但怀疑这更常见于标称与标称。)关于良好标准参考的建议也非常受欢迎,我猜来自 Agresti。

如果有人想用图表来说明,则下面是虚假样本数据的 R 代码。

“运动对你来说有多重要?” 1 = 完全不重要,2 = 有点不重要,3 = 既不重要也不不重要,4 = 有点重要,5 = 非常重要。

“你多久跑一次 10 分钟或更长时间?” 1 = 从不,2 = 每两周少于一次,3 = 每一或两周一次,4 = 每周 2 或 3 次,5 = 每周 4 次或更多。

如果将“经常”视为因变量而将“重要性”视为自变量是很自然的,如果图表可以区分两者。

importance <- rep(1:5, times = c(30, 42, 75, 93, 60))
often <- c(rep(1:5, times = c(15, 07, 04, 03, 01)), #n=30, importance 1
           rep(1:5, times = c(10, 14, 12, 03, 03)), #n=42, importance 2
           rep(1:5, times = c(12, 23, 20, 13, 07)), #n=75, importance 3
           rep(1:5, times = c(16, 14, 20, 30, 13)), #n=93, importance 4
           rep(1:5, times = c(12, 06, 11, 17, 14))) #n=60, importance 5
running.df <- data.frame(importance, often)
cor.test(often, importance, method = "kendall") #positive concordance
plot(running.df) #currently useless

我发现一个关于连续变量的相关问题很有帮助,也许是一个有用的起点:在研究两个数值变量之间的关系时,散点图的替代方法是什么?

4个回答

这是对热图的快速尝试,我使用黑色单元格边框来分解单元格,但也许应该像 Glen_b 的回答那样将图块分开。

热图

library(ggplot2)
runningcounts.df <- as.data.frame(table(importance, often))
ggplot(runningcounts.df, aes(importance, often)) +
   geom_tile(aes(fill = Freq), colour = "black") +
   scale_fill_gradient(low = "white", high = "steelblue")

这是基于 Andy W 早期评论的波动图。正如他所描述的那样, “它们基本上只是分类数据的分箱散点图,点的大小映射到该分箱内的观察数量。” 参考见

威克姆、哈德利和海克霍夫曼。2011.产品图IEEE Transactions on Visualization and Computer Graphics (Proc. Infovis `11)预印本 PDF

波动图

theme_nogrid <- function (base_size = 12, base_family = "") {
  theme_bw(base_size = base_size, base_family = base_family) %+replace% 
    theme(panel.grid = element_blank())   
}

ggplot(runningcounts.df, aes(importance, often)) +
  geom_point(aes(size = Freq, color = Freq, stat = "identity", position = "identity"), shape = 15) +
  scale_size_continuous(range = c(3,15)) + 
  scale_color_gradient(low = "white", high = "black") +
  theme_nogrid()

这是数据的脊椎图的示例。我在 Stata 中很快就做到了,但是有一个R 实现我认为在 R 中它应该只是:

spineplot(factor(often)~factor(importance))

如果您提供 R 分类变量,spineplot 实际上似乎是默认值:

plot(factor(often)~factor(importance))

为每个重要性类别显示经常类别的部分细分。堆叠条用垂直尺寸绘制,显示通常给定重要性类别的分数。水平维度显示每个重要性类别中的分数。因此,所形成的瓦片区域表示频率,或更一般地表示每个重要性和经常的交叉组合的总数。

在此处输入图像描述

脊柱图(马赛克图)适用于此处的示例数据,但如果某些类别组合很少或不存在,则可能难以阅读或解释。自然地,低频率用小块表示,零表示完全没有块,这是合理的,也是意料之中的,但心理上的困难仍然存在。喜欢spineplots的人选择适合他们的论文或演示文稿的示例也是很自然的,但我经常制作的示例过于凌乱而无法在公共场合使用。相反,spineplot 确实很好地利用了可用空间。

一些实现预设了交互式图形,以便用户可以询问每个图块以了解更多信息。

另一种也可以很好地工作的替代方法是双向条形图(存在许多其他名称)。

参见例如tabplothttp://www.surveydesign.com.au/tipsusergraphs.html

对于这些数据,一个可能的图(tabplot在 Stata 中使用,但在任何体面的软件中都应该很容易)是

在此处输入图像描述

这种格式意味着很容易将各个条形与行和列标识符相关联,并且您可以使用频率、比例或百分比进行注释(如果您认为结果太忙,自然不要这样做)。

一些可能性:

  1. 如果可以将一个变量对另一个变量的响应视为预测变量,那么值得考虑像往常一样将其绘制在垂直轴上。在这里,我认为“重要性”是衡量一种态度,然后是它是否会影响行为(“经常”)。即使对于这些虚构的数据,因果问题也往往更加复杂,但重点仍然存在。

  2. 如果相反的效果更好,意思是更容易思考和解释,建议#1总是被击败。

  3. 百分比或概率细分通常是有意义的。原始频率图也很有用。(当然,这个图缺乏马赛克图同时显示两种信息的优点。)

  4. 您当然可以尝试分组条形图或堆叠条形图(或 WS Cleveland 意义上的仍然相当不常见的分组点图)的(更常见的)替代方案。在这种情况下,我认为它们效果不佳,但有时它们效果更好。

  5. 有些人可能希望对不同的响应类别进行不同的着色。我没有反对意见,如果你愿意,你不会认真对待反对意见。

混合图形和表格的策略可能更普遍有用,或者根本不是你想要的。一个经常重复的论点是,图形和表格的分离只是印刷术的发明及其产生的分工的副作用。这再一次是不必要的,就像手稿作者将插图准确地放在他们喜欢的方式和位置上一样。

我这样做的方式有点软糖,但它可以很容易地修复。

这是抖动方法的修改版本。

移除坐标轴减少了将比例解释为连续的诱惑;在抖动的组合周围画框强调存在“比例中断”之类的东西 - 间隔不一定相等

理想情况下,应该将 1..5 标签替换为类别名称,但我暂时将其留给想象;我认为它传达了它的意义。

 plot(jitter(often)~jitter(importance),data=running.df,bty="n",
    ylim=c(0.5,5.5),xlim=c(0.5,5.5),cex=0.5,pty="s",xaxt="n",yaxt="n") 
 axis(1,tick=TRUE,col=0)
 axis(2,tick=TRUE,col=0)
 rect(rep(seq(0.75,4.75,1),5),rep(seq(0.75,4.75,1),each=5),
       rep(seq(1.25,5.25,1),5),rep(seq(1.25,5.25,1),each=5),
       border=8)

抖动的序数图


可能的改进:

i)使休息时间更小(我个人更喜欢更大的休息时间),以及

ii) 尝试使用准随机序列来减少框内明显模式的发生率。虽然我的尝试有所帮助,但您可以看到,在点数较少的单元格中,仍然存在具有或多或少相关外观的子序列(例如,第一行第二列中的框)。为了避免这种情况,可能必须为每个子框初始化准随机序列。(另一种可能是拉丁超立方体采样。)一旦整理出来,就可以将其插入到一个与抖动完全相同的函数中。

准随机抖动和更大的盒子

library("fOptions")

 hjit <- runif.halton(dim(running.df)[1],2) 
 xjit <- (hjit[,1]-.5)*0.8
 yjit <- (hjit[,2]-.5)*0.8  

 plot(I(often+yjit)~I(importance+xjit),data=running.df,bty="n",
    ylim=c(0.5,5.5),xlim=c(0.5,5.5),cex=0.5,pty="s",xaxt="n",yaxt="n") 
 axis(1,tick=TRUE,col=0)
 axis(2,tick=TRUE,col=0)
 rect(rep(seq(0.55,4.55,1),5),rep(seq(0.55,4.55,1),each=5),
       rep(seq(1.45,5.45,1),5),rep(seq(1.45,5.45,1),each=5),
       border=8)