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


46

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

我能想到的几种选择:

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

还有其他想法,或关于哪种情节更可取的想法?是否有某些研究领域将某些序数对纵坐标图视为标准?(我似乎还记得频率热图在基因组学中很普遍,但我怀疑频率相对于标称值与标称值更常见。)我也很希望从Agresti那里获得有关好的标准参考的建议。

如果有人想用图解来说明,则可以使用伪造样本数据的R代码。

“锻炼对您有多重要?” 1 =一点都不重要,2 =一点都不重要,3 =既不重要也不重要,4 =有点重要,5 =非常重要。

“您多长时间定期跑步10分钟或更长时间?” 1 =永不,2 =每两周少于一次,3 =每1或2周一次,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

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


1
那么旋转纺丝呢?
Dimitriy V. Masterov

在多个组中显示单变量序数数据的相关问题也可能是相关的:显示序数数据-均值,中位数和均值等级
Silverfish

Answers:


15

自旋曲线图(马赛克图)适用于此处的示例数据,但如果某些类别的组合很少或不存在,可能很难阅读或解释。自然地,合理的并且可以预料的是,低频由一个小图块表示,而零则根本没有图块,但是心理上的困难仍然存在。喜欢自发发展的人们也很自然地选择了对自己的论文或演讲非常有用的例子,但是我经常制作出太乱而无法在公共场合使用的例子。相反,自旋蚀刻确实很好地利用了可用空间。

一些实现以交互图形为前提,因此用户可以查询每个图块以了解更多信息。

双向条形图(也可以使用许多其他名称)也可以很好地工作。

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

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

在此处输入图片说明

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

一些可能性:

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

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

  3. 百分比或概率细分通常很有意义。原始频率图也可能有用。(自然地,该图缺少同时显示两种信息的镶嵌图的优点。)

  4. 当然,您可以尝试(更常见的)分组条形图或堆叠条形图(或在WS Cleveland上仍然相当不常见的分组点图)的替代方法。在这种情况下,我认为它们不能很好地工作,但有时它们可​​以更好地工作。

  5. 有些人可能想为不同的响应类别着色不同。我没有异议,而且如果您希望您不会以任何方式认真对待异议。

图形和表格的混合策略可能更有用,或者根本没有用。一个经常重复的论点是,图形和表格的分离只是印刷发明及其产生的劳动分工的副作用;就像手稿作者将插图精确地放置在他们喜欢的位置和位置一样,它不再是必须的。


感谢您添加图形。这就提出了图形和文本数据如何结合的问题-我知道有些人不喜欢将数字放在条形图的顶部(因为这会使条形图看起来比实际的要高;我没有引述的内容)这个,但我认为这是一个众所周知的观点。
银鱼

另一方面,固定数字的位置似乎会导致两个问题之一:数字可能最终叠加在条形上,从而使它们模糊不清;或者固定数字在条形之上可以使它们与下部条形“断开”连接尤其是。是否在某个地方对这些问题进行了很好的讨论?
银鱼

我认为您不需要参考;这是一种普遍的态度。我看到了其他变体:(1)针对特定显示的建议,即显示过于忙碌,不整洁等。(2)吸引人的观点是数字文本是多余的,因为相同的信息是隐式的(或根据某些显式信息) )以任何方式在图表中显示(3)“男孩是蓝色,女孩是粉红色”的态度是,数字是数字,表是表,并且两个子都必须符合。(3)纯粹是对我的偏见;(2)原则上是正确的,但数字仍然可以提供帮助;(1)必须通过示例进行思考。
尼克·考克斯

我不知道具体权衡的讨论。留下不带颜色的条以使数字可以放入其中通常是一个好主意。有时酒吧可能太小而无法始终做到这一点。
尼克·考克斯

30

这是快速绘制热图的尝试,我使用黑色的单元格边界来分解单元格,但也许应该按照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")

这是基于安迪·W(Andy W)先前评论的波动图正如他描述的那样, “它们基本上只是分类数据的分类散点图,一个点的大小映射到该分类内的观察数。” 有关参考,请参阅

Wickham,Hadley和Heike Hofmann。2011。产品图IEEE可视化和计算机图形学事务(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()

1
也许应该像Glen_b的答案中那样,将图块分开更多 ” –我不确定在这种情况下是否有必要,将类别视为连续的诱惑更少了。
Glen_b

18

这是一个数据旋转图形的示例。我很快在Stata中完成了此操作,但是有一个R实现。我认为在R中应该只是:

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

如果给定R分类变量,则自旋绘制实际上似乎是默认设置:

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

对于每个重要类别,显示经常类别的分数分解。以垂直尺寸绘制堆叠的条形图,其中显示了经常给出的重要性类别的分数。水平维度显示每个重要性类别中的分数。因此,形成的图块的面积代表重要性的每个交叉组合的频率,或更通常是总的频率。

在此处输入图片说明


1
我改变了它。
Dimitriy V. Masterov

1
引用尼克·考克斯(Nick Cox,Stata的spineplot的作者):对两个变量的限制比实际更为明显。可以通过两个或多个类别变量的交叉组合来创建复合变量。...通常在y轴上更好地显示响应变量。如果一个变量是二进制的,通常最好将其绘制在y轴上。当然,这些建议之间可能会有些紧张。
Dimitriy V. Masterov

3
我同意以上所述。但是对于序数变量,Stata的默认颜色方案相当糟糕。几种不错的选择是红色和/或蓝色的深浅不同,或者只是灰度选择。
Nick Cox

3
@Dimitriy我发现在相同情况下使用任意颜色混合非常奇怪!我并不暗示或推断出确切的颜色,无论它们是量化的。但是,重点仅在于,渐变的刻度与渐变的颜色序列完全匹配。热图的着色以及许多专题制图的着色也都具有任意性。
Nick Cox

2
只要颜色是不同的,我就看不到分级配色方案的问题。为什么有人会插值?我看不到任意颜色的逻辑。彩虹序列在物理学上是有意义的,但是在人们如何看待颜色方面(例如,黄色和红色太不同了),却没有意义。我有足够的证据说服许多学生进行选择,当他们看到彩虹或水果沙拉上的细微分级序列时,我会说80%的人真诚地说“好得多”。蓝色到浅蓝色到浅红色到红色效果很好。确保您对男人和女人都尝试一下。
Nick Cox

13

我执行此操作的方法有些不正确,但是可以很容易地将其修复。

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

移开轴可减少将刻度解释为连续的诱惑;抖动的组合周围的绘图框强调了类似“比例尺断裂”的现象-间隔不一定相等

理想情况下,应将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)尝试使用准随机序列来减少盒子内外观图案的发生率。尽管我的尝试有所帮助,但您可以看到在点数较少的单元格中,仍然存在具有或多或少相关外观的子序列(例如,第一行的第二列中的框)。为了避免这种情况,可能必须为每个子框初始化准随机序列。(另一种方法可能是Latin Hypercube采样。)将其解决后,可以将其插入功能完全类似于抖动的函数中。

准随机抖动和更大的盒子

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)

1
我喜欢这样,对我而言,分隔确实强调了数据的序数性质!不幸的是,人眼自然会被抖动中的明显图案吸引住,例如面板(4,5)和(5,3)的“上升趋势”。从好的方面来说,“算点数”对我来说比按点数判断频率更自然。为了避免分散“抖动趋势”,是否有变体将点均匀分布或以规则的方式在中心集中成簇?
Silverfish

1
@Silverfish是地理上类似的概念,是点密度图。地理学家发现了一些证据,表明规则的图案或填充一定数量空白的图案(因此,间距较远,然后是随机的)往往会在观察者中产生更准确的感知。
Andy W

IMO这是一个好主意,但是在此示例中,面板之间的间距很大,因此很难直观地看到任何趋势。治愈方法比疾病要差(但使面板彼此靠近应该很容易)。
Andy W

1
@silverfish 准随机抖动可能是解决该问题的方法。你关心的就是我自己。
Glen_b

1
非常好!在这种情况下,IMO比自旋图更好(脊柱图或镶嵌图更适合评估任何类别对的条件分布-这种抖动的点图更易于评估趋势-利用数据的序数性质并假设单调关系的类型)。
安迪W

7

使用R包riverplot:

  data$importance <- factor(data$importance, 
                            labels = c("not at all important",
                                       "somewhat unimportant",
                                       "neither important nor unimportant",
                                       "somewhat important",
                                       "very important"))
  data$often <- factor(data$often, 
                       labels = c("never",
                                  "less than once per fortnight",
                                  "once every one or two weeks",
                                  "two or three times per week",
                                  "four or more times per week"))

  makeRivPlot <- function(data, var1, var2, ...) {

    require(plyr)
    require(riverplot)
    require(RColorBrewer)

    names1 <- levels(data[, var1])
    names2 <- levels(data[, var2])

    var1 <- as.numeric(data[, var1])
    var2 <- as.numeric(data[, var2])

    edges <- data.frame(var1, var2 + max(var1, na.rm = T))
    edges <- count(edges)

    colnames(edges) <- c("N1", "N2", "Value")

    nodes <- data.frame(ID     = c(1:(max(var1, na.rm = T) +
                                      max(var2, na.rm = T))),
                        x      = c(rep(1, times = max(var1, na.rm = T)),
                                   rep(2, times = max(var2, na.rm = T))),
                        labels = c(names1, names2) ,
                        col    = c(brewer.pal(max(var1, na.rm = T), "Set1"),
                                   brewer.pal(max(var2, na.rm = T), "Set1")),
                        stringsAsFactors = FALSE)

    nodes$col <- paste(nodes$col, 95, sep = "")

    return(makeRiver(nodes, edges))

  }

a <- makeRivPlot(data, "importance", "often")

riverplot(a, srt = 45)

在此处输入图片说明


1
(+1)我喜欢为此使用平行坐标的想法!我认为,如果颜色从左到右流动,则更容易跟踪图中的路径,并查看“常”答案是如何分解的(该方案实际上将“常”显示为因变量,而“重要性”作为解释变量)。在此类绘图的某些交互式实现中,您可以单击某个轴以使用该变量进行着色,这很有用。
银鱼

1
为了进行比较,Robert Kosara的“并行集”可视化设计用于分类数据,其颜色在图表中流动。
银鱼

6

我本来没想到的一个不同的想法是筛子积

在此处输入图片说明

每个图块的大小与预期频率成正比;矩形内的小方块代表实际频率。因此,更大的正方形密度表示高于预期的频率(蓝色阴影);较低的正方形密度(红色)表示低于预期的频率。

我想如果颜色代表残留物的大小而不只是符号,我会更喜欢。对于预期和观察到的频率相似且残差接近零的边缘情况尤其如此。一分为二的红色/蓝色方案似乎过分强调了小的偏差。

在R中的实现:

library(vcd)
runningcounts.df <- as.data.frame(table(importance, often))
sieve(Freq ~ often + importance, data=runningcounts.df, shade= TRUE)

1
关于您偏爱颜色代表大小和符号的偏好,一种可能性是,当与预期的差异相对较小时,使颜色更灰。
Glen_b 2015年

6

R中的多面条形图。它非常清楚地显示了“经常”在每个“重要性”级别上的分布。但是,如果最大数量在“重要性”级别之间变化更大,那将不会很好。可以很容易地scales="free_y"在ggplot中进行设置(请参见此处),以避免大量的空白空间,但是在低频的“重要性”水平上,很难分辨出分布的形状,因为条形是如此之小。也许在这些情况下,最好在垂直轴上使用相对频率(条件概率)。

条形图

它不像尼克·考克斯(Nick Cox)链接到的Stata中的制表图那样“干净” ,但是传达了类似的信息。

R代码:

library(ggplot)
running2.df <- data.frame(often = factor(often, labels = c("never", "less than once per fortnight", "once every one or two weeks", "two or three times per week", "four or more times per week")), importance = factor(importance, labels = c("not at all important", "somewhat unimportant", "neither important nor unimportant", "somewhat important", "very important")))
ggplot(running2.df, aes(often)) + geom_bar() +
  facet_wrap(~ importance, ncol = 1) +
  theme(axis.text.x=element_text(angle = -45, hjust = 0)) +
  theme(axis.title.x = element_blank())
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.