相关矩阵的可视化及其新方法探究

相关系数阵对于分析多元数据时非常有用,然而当变量较多时,我们很难从一堆庞大的数字中快速获取信息。正因为如此,相关阵的可视化应运而生。的确,活泼生动的图形对我们的眼球更有诱惑力。已有的相关阵可视化技巧有颜色图、椭圆图、钟表图(参见Deepayan Sarkar所著的《Multivariate Data Visualization with R》中的Fig13.6)等,其思想都非常直观。本文在阐述了颜色图和椭圆图的机理后,又提出了一种新的相关阵的可视化技术——圆圈图,并与颜色图、椭圆图进行了比较。

##2010-4-11更新:本文及扩展工作对应的包corrplot可从CRAN下载。

颜色图

颜色图就是将一个网格矩阵映射到指定的颜色序列上,恰当地选取颜色来展示数据。在相关阵中,所有的数据都在 -1 到 1 之间,我们不仅要关注相关系数的绝对值大小,同时更加看重它们的正负号。因此,相关阵的颜色图和一般矩阵的颜色图应该有所区别:即应当选取两种色差较大的颜色序列来展示不同符号的相关系数, Gregor Gorjanc 建议用蓝色表示正相关系数,红色表示负相关系数。

R 中,image()函数可以轻松绘制颜色图,利用 mcars 数据,下面代码可以得到其加了相关系数(乘以100,是为了节省空间)的颜色图(图1)。

data(mtcars)
fit = lm(mpg ~ ., mtcars)
cor = summary(fit, correlation = TRUE)$correlation
#相关阵上下倒转再转置,是为了让画出的图和相关阵方向一致
cor2 = t(cor[11:1, ])
#下面颜色取自ellipse包中的plotcorr函数的示例
colors = c("#A50F15", "#DE2D26", "#FB6A4A", "#FCAE91", "#FEE5D9",
	"white", "#EFF3FF", "#BDD7E7", "#6BAED6", "#3182BD", "#08519C")
image(1:11, 1:11, cor2, axes = FALSE, ann = F, col = colors)
text(rep(1:11, 11), rep(1:11, each = 11), round(100 * cor2))
图1 相关阵的颜色图
图1 相关阵的颜色图

观察图1,通过颜色的比较,可以直观看出相关系数的符号和大小:深色区域表示较强相关性,浅色区域表示较弱的相关性。

椭圆图

椭圆图是利用椭圆的形状来表示相关系数:离心率越大,即椭圆越扁,对应绝对值较大的相关系数;离心率越小,即椭圆越圆,对应绝对值较小的相关系数。椭圆长轴的方向来表示相关系数的正负:右上-左下方向对应正值,左上-右下方向对应负值。当然

R 中,ellipse包中的plotcorr() 可以实现这一功能,同样利用 mcars 数据,来展示椭圆图(图2)。他山之石,可以攻玉,椭圆图当然可以利用色彩来增强表现力。

library(ellipse)
col = colors[as.vector(apply(corr, 2, rank))]
plotcorr(cor, col = col, mar = rep(0, 4))
图2. 相关阵的椭圆图
图2. 相关阵的椭圆图

观察图2,可以发现尽管所有椭圆披红挂蓝,但该图并不是非常形象生动(本文前一个版本中所有椭圆皆为灰色,表现力更差)。

圆圈图

考虑到前两种方法的局限性,本人设计了一种新的相关阵的可视化方法——圆圈图。具体做法是:

1. 用圆的面积来表示相关矩阵的绝对值大小。

2.用实心圆(圆内填充颜色)和空心圆来表示相关系数的正负号。

下面编写函数(最终版见R Graph Gallery)绘图,仍基于mcars数据,得到圆圈图(图3)。

circle.cor=function(cor,axes=FALSE, xlab='', ylab='', asp=1,
		title="Taiyun's cor-matrix circles",...){
     n=nrow(cor)
     cor=t(cor[n:1,]) ##先上下倒转,再转置
     par(mar = c(0, 0, 2, 0), bg = "white")
     plot(c(0,n+0.8),c(0,n+0.8),axes=axes, xlab='', ylab='', asp=1, type='n')
     segments(rep(0.5,n+1),0.5+0:n, rep(n+0.5,n+1),0.5+0:n,col='gray')
     segments(0.5+0:n, rep(0.5,n+1), 0.5+0:n, rep(n+0.5,n),col='gray')
     for(i in 1:n){
	for(j in 1:n){
		c=cor[i, j]
		bg=switch(as.integer(c>0)+1,'white','black')
		symbols(i,j,circles=sqrt(abs(c))/2, add=TRUE, inches=F, bg=bg)
	}
     }
     text(rep(0,n),1:n,n:1,col='red')
     text(1:n,rep(n+1),1:n,col='red')
     title(title)
}
circle.cor(cor)
相关阵的圆圈图
图3. 相关阵的圆圈图

图3 中,黑色实心圆表示正相关系数,空心圆表示负相关系数。观察图3 ,不难看出相关系数的大小、正负都空前清楚明了。

实际上,图3 还有发展空间,比如可以在图旁添加图例,用颜色信息辅助面积大小,或者将相关系数标在图上,还可以将该图分为上下两三角,留其一添加其他信息。但需要注意的是不可喧宾夺主。

讨论

  1. 人们往往更为关注相关性较强的数据,从这一方面来看,椭圆图比较失败,因为它将最大的面积留给了相关性最弱的数据,给其他信息的获取造成了一定的干扰。而颜色图和圆圈图则较为成功,尤其是圆圈图。
  2. 相关系数的正负号非常有价值,在从这方面来看,颜色图和椭圆图都比较失败。所谓“五色令人目盲”,当色彩过于纷呈时,我们往往会眼花缭乱,正负号各仅有其一,而颜色图中的颜色却远非如此(这对于色盲更为不利)。而椭圆图中,反映正负号的信息本来就是两个方向,不如圆圈图那样“黑白分明”,更糟的是这些信息受到了过多的干扰。
  3. 综上,圆圈图的优点是:1.黑白分明,正负号一清二楚。2.圆的大小表示相关性强度,具有很好的可比性。3.照顾最广大人群(包括颜色不敏感者以及色盲患者),方便打印,信息不易失真。4.将表现力的最强图形元素留给最有价值的数据(最大的圆留给相关系数最强的数据),资源配置良好。5.简单明了,干扰信息最少。

相关矩阵的可视化及其新方法探究》有42个想法

  1. 你是不鸣则已,一鸣惊人啊。你这两篇文章我都非常喜欢,哈哈

    话说《统计研究》上极少发表关于图形的文章,某年我们学院某教授发了一篇闪电图,让我极其无语,其实就是把折线图旋转90°……你的文章比那种论文强多了。

    关于圆圈图,我想对角线上的大黑点最好去掉,以免喧宾夺主、影响视觉感受,或者效仿plotcorr()函数给出diag = TRUE/FALSE参数。另外我不知道你的png图形是怎么生成的,感觉不太清晰,所以建议用png()设备指定宽高生成,例:

    library(ellipse) # 原文此处有笔误
    fit = lm(mpg ~ ., mtcars)
    png("corr-ellipse2.png", width = 480, height = 480)
    # 用mar设置作图区域边界为0
    plotcorr(summary(fit, correlation = TRUE)$correlation, 
    	mar = rep(0, 4))
    dev.off()
    

    圆圈图中的代码也有可改进之处,如:

    bg=switch(as.integer(c>0)+1,'white','black')
    

    我觉得不如直接用:

    bg = c('white', 'black')[c <= 0]
    

    而整个函数也可以矩阵式操作,避免循环(作[latex]n^2[/latex]个symbols图可能会很慢):

    bg = cor
    bg[cor > 0] = 'black'
    bg[cor <= 0] = 'white'
    # 以下1个symbols()调用完成全图
    # ...
    
    1. 多谢夸奖:)我觉得主对角线上的大黑点还是要保留(或者用黑点表示负系数?),虽然它们都是1,但画出来可以方便别的图形进行比较,这些大黑点算是尺子,正方形的边长也是。png图直接保存的,的确不太清晰,图三中的大黑圆右下角有些瑕疵。
      代码先放着,慢慢改进,谢谢指正。不知哪个相关的R包能收录一下:)

      谢兄漏打了的画全图的代码是不是这个:
      symbols(rep(1:n, each = n), rep(1:n, n), add = TRUE,
      circles = as.vector(sqrt(abs(cor))/2), bg = as.vector(bg), inches = F)

  2. 想起08年3月这个很初级的帖子
    在R中进行类型聚类的讨论
    那么相关矩阵可视化了以后,是不是聚类的过程也可以可视化?

    比如对相关矩阵重新排序,然后同一类别的元素靠在一起,形成类似波浪的图形

    1. 可以看看这个例子,将相关矩阵重新排了序再画图:

      library(ellipse)
      corr.mtcars < - cor(mtcars)
      ord <- order(corr.mtcars[1,])
      xc <- corr.mtcars[ord, ord]
      colors <- c("#A50F15","#DE2D26","#FB6A4A","#FCAE91","#FEE5D9","white",
                  "#EFF3FF","#BDD7E7","#6BAED6","#3182BD","#08519C")   
      plotcorr(xc, col=colors[5*xc + 6])
      
      1. 为啥代码中会有中文引号呢……

        library(ellipse)
        corr.mtcars = cor(mtcars)
        ord = order(corr.mtcars[1, ])
        xc = corr.mtcars[ord, ord]
        colors = c("#A50F15", "#DE2D26", "#FB6A4A", "#FCAE91", 
            "#FEE5D9", "white", "#EFF3FF", "#BDD7E7", "#6BAED6", "#3182BD", 
            "#08519C")
        plotcorr(xc, col = colors[5 * xc + 6]) 
    2. 麻烦之处就在于对距离矩阵的排序,因为它不是一个向量,排序的时候行列都要考虑,如何排才能把相似的元素排到一起?我觉得不妨参考hclust()函数的结果,它的最底层的排序其实正是你这个帖子想要的排序,把hclust()的结果与本文的圆圈图结合起来使用,应该就能达到你要的效果了,不过本文的圆圈图考虑的是相关系数的正负,而距离矩阵只有大小,没有正负,因此黑白颜色的选择可以去掉,只是用圆圈表示距离的大小即可。

  3. 代码改进了一下:

    circle.cor = function(cor, axes = FALSE, xlab = "",
    ylab = "", asp = 1, title = "Taiyun's cor-matrix circles",
    ...) {
    n = nrow(cor)
    par(mar = c(0, 0, 2, 0), bg = "white")
    plot(c(0, n + 0.8), c(0, n + 0.8), axes = axes, xlab = "",
    ylab = "", asp = 1, type = "n")
    ##add grid
    segments(rep(0.5, n + 1), 0.5 + 0:n, rep(n + 0.5, n + 1),
    0.5 + 0:n, col = "gray")
    segments(0.5 + 0:n, rep(0.5, n + 1), 0.5 + 0:n, rep(n + 0.5,
    n), col = "gray")
    ##define circles' background color.
    ##black for positive correlation and white for negative
    bg = cor
    bg[cor > 0] = "black"
    bg[cor <= 0] = "white"
    ##plot n*n circles using vector language, suggested by Yihui Xie
    ##the area of circles denotes the absolute value of coefficient
    symbols(rep(1:n, each = n), rep(n:1, n), add = TRUE, inches = F,
    circles = as.vector(sqrt(abs(cor))/2), bg = as.vector(bg))
    text(rep(0, n), 1:n, n:1, col = "red")
    text(1:n, rep(n + 1), 1:n, col = "red")
    title(title)
    }
    ## an example
    data(mtcars)
    fit = lm(mpg ~ ., mtcars)
    cor = summary(fit, correlation = TRUE)$correlation
    circle.cor(cor)

    1. 这个代码应该就快多了吧?

      关于写代码,我有个建议,你好像不太在乎变量名的命名,例如cor、c这样的变量名最好避免使用,因为它们本身就是R的函数。虽然在R里面随意命名一般来说不会有大的问题,但是不排除有时候当你把T/F弄成变量名的时候,a == T/F就不再是a == TRUE/FALSE的意思了。这种bug也不太容易找出来。

  4. 请问如果不是矩阵,而只是根据一个参数画了若干椭圆,例如根据数据中所有变量的四种不同的属性,画了四个椭圆,如何能让他们颜色不同?而且是实心的?
    用col = “color”只能改变所有椭圆线条的颜色。
    急切盼望不吝赐教
    我用的是Vegan包里的ordiellipse函数

  5. 刚发现永久链接correlation-matrix-isualization最后一个单词漏了一个v。另外恭喜你认识了Romain Francois!

  6. 很好!我建议你再考虑一下颜色参数,使得你的这种图形能够被扩展到可以展示任意矩阵(如聚类的距离矩阵)。用户可以自定义颜色时,相关系数矩阵则可以用ifelse(corr > 0, "black", "white")把颜色参数传递进去——它只是矩阵的特例而已。

  7. 太云的创意很好,但我个人认为还可以进一步改进

    正如太云指出的,原来的彩色圈图存在色彩杂乱,把最引起关注的大圆给了相关性最小的格子。因此采用有大小的黑白圈图。

    但我想原来的彩色圈图至少有1个优点:保留了相关性是反映“X、Y向直线集中的趋势”这个概念

    而黑白圈图至少有1个缺点:用实心还是空心表达正负往往让人容易忽视空心圈。

    因此我建议不采用黑白圈图而是采用“斜线图”,即格子中的图形采用\和/,大小用斜线的粗细来表达。
    这样有以下好处:
    1、强调了相关性是反映“X、Y向直线集中的趋势”这个特点。
    2、用正斜和反斜表达正负相关
    3、保留了原来黑白圈图把最显眼的图形留给相关性最大的格子的特点。

    我不会用R,因此无法给出图例,希望我的描述足够清楚。

    1. 但我想原来的彩色圈图至少有1个优点:保留了相关性是反映“X、Y向直线集中的趋势”这个概念。

      说得太好了,我之前还没有这样想过。

      而黑白圈图至少有1个缺点:用实心还是空心表达正负往往让人容易忽视空心圈。

      这个我倒不很觉得,可能是主对角线上的一排大黑圆影响的吧,要是不喜欢黑白,别的颜色也可以选择。但是,我觉得还是黑白色最为妥当。

      格子中的图形采用\和/,大小用斜线的粗细来表达。

      很新颖的方法,不过我觉得粗细对视觉影响肯定没有面积明显。

      As Hills (1969) said, “The first and sometimes only impression gained from looking at a large correlation matrix is its largeness.”

      多谢你的建议,你的描述非常清楚了。

      1. 我没有完全表达清楚,其实如果线段足够粗,不也是在表达一种面积的概念吗。我用Visio试验了一下,效果较好。

  8. 1、只要交待清楚数据来自于(Pearson)相关系数矩阵,那么“线性”就不用再强调了,圆圈越大说明越有直线趋势。
    2、至于正负,/和\恐怕在图中就不那么清晰了,看着一团短线。
    3、黑白的感受可能取决于个人,我看这幅图觉得简洁,黑的地方表示正相关,剩下“空”的地方表示负相关,还是挺清楚的,如果样本能适当排序,那么前面 linkinbird 说的那个主意就能很好和圆圈图结合使用了,展示聚类现象肯定效果很好。

    不过我上面说的都是针对数据量比较大的情况,在少数几个变量之间,用scatterplot matrix就很好展示了,关于这一点,太云兄弟可能会在他下一篇作品中展现。

    1. 我想上传一张我用Viso做的实例图,但没有办法上传,能帮我吗?

      1. 目前我最满意的是围棋图,配了个围棋盘的鹅黄底色。

        此外,发现外国佬可喜欢挑肥拣瘦了:
        David Smith,An Introduction to R的作者,SPLUS+的员工,ESS的最初开发者之一。
        Andrew Gelman,哥伦比亚大学的一个教授,应用统计中心的主管,看其简历似乎比较有名。
        Bob O’Hara,芬兰的家伙,University of Helsinki。
        以及Romain francois,Fëanor 等等。

        还有,比较纳闷的是这么一副图受到这么多人的关注,更奇怪的是几乎都是外国佬,中国的估计不会有教授关注。

      2. 国内还没有用图的意识,或者说图形还没有成为统计的流派之一。

        Andrew Gelman可是个大牛,你如果有打算出国的话,到他手下可就发达了 :mrgreen:

      3. 是打算出国,不过一直没有准备,估计只能在读研期间了,现在也筹备着和这些教授套套近乎。但愿到时能钓到一只大牛:)

  9. 好文章
    我也跟踪过这一方面
    有个叫friendly,m 的人写过不少新paper
    最近的一篇好像发在jrsa上
    叫order variables in data analysis
    那篇文章也很赞
    但是没时间专研太深
    请问楼主或者哪位
    有没有matlab的工具包呢。。。
    有什么好书或者好网站推荐呢?
    谢谢哈~

    另外,
    我一直好奇
    这些图跟聚类分析里的某种叫correlation map的有什么区别呢?
    correlation map 主要用在高纬方面,是为了看清楚数据中的variables是否存在聚类
    如果过你的这些图是为了看variables之间的关系
    那么为什么不用直接的graphical modeling?

    这是我对这些图的疑问

    不过第二个图在item response models上的应用已经开始popular。。
    他们用这个图来同时展示很多个假设检验
    这样的图太赞了

    1. 感谢您如此深刻的见解。

      我倒是没有怎么跟踪过,就是自己瞎整一下,惭愧。有个corrgram(作者就是Michael Friendly),也是关于相关阵的可视化的,在MATLAB、R、SPLUS、SAS上实现了的,不过都没有多少新奇之处。

      correlation map就是个颜色图加上个聚类,颜色图在高维方面是最好用的,椭圆图、前面提到的corrgram、我的圆圈图其实在较高维(比如大于40*40)时几乎没有什么表现力了,因为此时椭圆(或圆)太多了,这些本想给人带来信息的东西(椭圆的形状、圆的面积)在高维时不仅自己表现不好(因为圈太多不利于肉眼观察),反而影响到了颜色的表现力。

      其实可以这么说,表现相关阵的颜色、形状两个元素不是独立的,它们会有交互影响,应该在具体情况下采用什么色彩、几何架构是一门很深的学问,这个要涉及到数据本身的特点、色彩学,还和人的感官息息相关,而人的感官又是有差异的。我觉得这个应该是可视化看重的一个问题。所谓可视化就是把大量的信息用我们肉眼能迅速获取的方式展现出来,而什么东西最容易被我们的肉眼所接受。其中一个无法逃避的问题就是基础作图元素的选择,最起码的有:颜色、几何形状等,它们又应该如何搭配。

      在中维数据(比如10~40左右)中,椭圆图、圆圈图还是有一定的市场。因为图就是想给人们最充分的信息,而颜色图仅仅利用了颜色,椭圆图、圆圈图还利用了形状或面积,此时维数不高,这些形状、面积本身可以带给人们不少信息,又不足以对色彩造成很大的冲击。还有,我觉得相关阵的可视化的目的不总是看variables之间的关系,同时在中维时,圆圈图的表现力也不错。此外,咨询一下大家:大家都想从相关阵中得到什么?不一定总是想聚类吧?

      不过第二个图在item response models上的应用已经开始popular。他们用这个图来同时展示很多个假设检验

      可否提供一下相关资料,我还没有注意到,如果有案可稽就好了。我觉得只要椭圆图能做的,圆圈图就一定能做好,并且圆圈图天生突出相关系数较大元素。这个版上的圆圈图不太好看,我又整了些变种,可以看看MyPicasa

  10. 脑袋都大了
    好多编码啊
    。。。。。。。。。
    但愿在对R软件学习一段时间后,能够看懂学长的文章

    。。。。
    感觉,强人好多,自己太笨。。。
    加油。。。。

    1. 你那么冰雪聪明,这点东西肯定不在话下,加油。最新版的函数代码见:http://cos.name/bbs/read.php?tid=15158&page=1&toread=1#tpc

  11. 关于相关矩阵的可视化,用这种矩阵图的表示法固然比较直观,但是之间的关系还是不能一眼就看出来,特别是变量比较多的情况下。我的一个想法是能不能做出一个关系图,比如将矩阵中的每个变量用一个点表示并围成一个圈,然后用直线将其连接起来,直线的粗细和颜色表示相关性的大小和正负(正负也可以用虚实线表示),这样变量与变量的关系可以直接看连线就好了,而且可以人为的设定一个阈值,小于阈值的线条可以不画出来,这样就可以更加清晰的看出变量间的相关性了。这个好像用Pajek可以实现,不过不知道用R能不能实现。

    1. R里面也可以画网络,像sna之类的包,不知道能不能方便地画出线的粗细。线的粗细是不是不如线的长短来得更直观更容易比较?
      而且正文介绍的这个展示方法其实也可以加入阈值的作用,还是挺直观的。

      1. 线条长短很直观,但是几乎没法用,举个最简单的例子:A,B,C,D间的相似系数一样,那么平面中是没有解的,正四面体才行。实际中的问题比这个复杂多多了。

      2. 网络关系图在变量少的情况下很直观,上千了就没辙了。corrplot里面的图都可以推广到上千的情况。事实上,这也是相关矩阵可视化的一个重要的动机。

    2. 我称两种方式为网络式和网格式,它们之间没有绝对的优劣。

      此外,当线条较多时,线条颜色、粗细看起来很不清楚。R中igraph包画这图很nice。

  12. 为啥出现:
    Error in plot.new() : figure margins too large
    哪里出问题了?

发表评论

电子邮件地址不会被公开。 必填项已用*标注