【r<-包|绘图】使用lattice进行高级绘图

使用lattice进行高级绘图是我之前在学习《R实战》这本书少有涉及的章节之一,在R里面,主要有两大底层图形系统,一是base,二是grid。lattice与ggplot2正是基于grid构建,它们都有自己独特的图形语法。我当前使用的主要是ggplot2,base画图基本不用,这里学习下lattice包并想后续深入学习绘图的底层原理与实现。

内容:

  • lattice包介绍
  • 分组与调节
  • 在面板函数中添加信息
  • 自定义lattice图形的外观

这几年开始学习R语言的估计对ggplot2要比lattice包熟悉很多,后者虽然在初学者中名声不显,但就凭lattice内置于R,而ggplot2要额外安装来看,前者毫无疑问是被前辈们所广泛使用的,这篇文章来正式学习一下。

我们将一起学习Deepayan Sarkar(2008)编写的lattice包,它实现了Cleveland(1983,1993)提出的网格图形。lattice包已经超越了Cleveland的初始可视化数据的方法,并且提供了一系列创建统计图形的复杂方法。

像ggplot2一样,lattice图形有它自己的语法,提供了对基础图形的替代方案,并且擅长绘制复杂数据。

lattice包

网格图形能够展示变量的分布或变量之间的关系,每幅图代表一个或多个变量的各个水平。思考下面一个问题:纽约合唱团各声部的歌手身高是如何变化的

lattice包在singer数据集中提供了这两个变量的数据:

library(lattice)

histogram(~height | voice.part, data = singer, main = "Distribution of Heights by Voice Pitch",
          xlab = "Height (inches)")
img

从这幅图可以看出,似乎男高音和男低音比女低音和女高音的身高更高。

在网格图中,调节变量的每个水平生成一个独立的面板。如果指定多个调节变量,这些变量因子水平的每个组合都会生成一个面板(类似于ggplot2中的分面概念)。面板被分配到数组中以便于比较。在每个面板名为条带的区域中会提供一个标签。用户可以控制每个面板上的图形,条板的格式与放置的位置,面板的安排,图例的放置和内容以及其他许多的图形特征。

lattice包提供了大量的函数来产生单因素图(点图、核密度图、直方图、条形图与箱线图),二元图(散点图、条形图、平行箱线图)和多元图(3D图、散点图矩阵)。每个高水平的画图函数都服从下面的格式:

graph_function(formula, data=, options)
  • graph_function 是下表的一个函数;
  • formula 指定要展示的变量和任意的调节变量;
  • data 指定数据框;
  • options 是用逗号分隔的参数,用来调整图形的内容、安排和注释。

我们假定小写字母代表数值型变量,大写字母代表分类型变量(因子)。高水平的画图函数通常采用的格式是:

y ~ x | A * B

其中竖线左侧称为主要变量,右边的变量称为调节变量

图类型 函数 公式例子
3D等高线图 contourplot() z~x*y
3D水平图 levelplot() z~y*x
3D散点图 cloud() z~x*y|A
3D线框图 wireframe() z~y*x
条形图 barchart() xA或Ax
箱线图 bwplot() xA或Ax
点图 dotplot() ~x|A
柱状图/直方图 histogram() ~x
核密度图 densityplot() ~x|A*B
平行坐标曲线图 parallelplot() dataframe
散点图 xyplot() y~x|A
散点图矩阵 splom() dataframe
线框图 stripplot() xA或Ax

为了尽快对lattice图有一个认识,我们尝试运行下面的代码。里面的图基于mtcars数据框中的汽车数据(里程、车重、挡数、汽缸数等)。

library(lattice)
attach(mtcars)

gear <- factor(gear, levels = c(3, 4, 5), 
               labels = c("3 gears", "4 gears", "5 gears"))
cyl <- factor(cyl, levels = c(4, 6, 8), 
              labels = c("4 cylinders", "6 cylinders", "8 cylinders"))

# 开始画图

densityplot(~mpg, main = "Density Plot", 
            xlab = "Miles per Gallon")
img
densityplot(~mpg | cyl,
            main = "Density Plot by Number of Cylinders",
            xlab = "Miles per Gallon")
img
bwplot(cyl ~ wt | gear,
       main = "Box Plots by Cylinders and Gears",
       xlab = "Miles per Gallon",
       ylab = "Cylinders")
img
xyplot(mpg ~ wt | cyl * gear, 
       main = "Scatter Plots by Cylinders and Gears",
       xlab = "Car Weight", ylab = "Miles per Gallon")

[图片上传失败...(image-81350d-1528447963024)]

cloud(mpg ~ wt * qsec | cyl,
      main = "3D Scatter Plots by Cylinders")
img
dotplot(cyl ~ mpg | gear,
        main = "Dot Plots by Number of Gears and Cylinders",
        xlab = "Miles Per Gallon")
img
splom(mtcars[c(1:6)],
      main = "Scatter Plot Matrix for mtcars Data")
img
detach(mtcars)

其中ggplot2不能画3D图形,而lattice可以。

lattice包中的高水平画图函数能产生可保存和修改的图形对象,例如:

library(lattice)
mygraph <- densityplot(~height|voice.part, data=singer)

创建一个网格密度图,并把它保存为对象mygraph,但是没有图形展示。使用plot(mygraph)将会显示该图。

通过调整选项很容易更改lattice图形,常见的选项列在下表中

选项 描述
aspect 指定每个面板图形的纵横比(高度/宽度)的一个数字
col、pch、lty、lwd 分别指定在图中使用的颜色、符号、线类型和线宽度的向量
group 分组变量(因子)
index.coord 列出展示面板顺序的列表
key (auto.key) 支持分组变量中图例的函数
layout 指定面板设置(列数与行数)的二元素数值向量,如果需要,可以增加一个元素来表示页面数
main、sub 指定主标题和副标题的字符向量
panel 在每个面板中生成图的函数
scales 列出提供坐标轴注释信息的列表
strip 用于自定义面板条带的函数
split、position 数值型向量,在一页上绘制多幅图像
type 指定一个或多个散点图绘图选项(p=点、l=线、r=回归线、smooth=局部多项式回归拟合、g=网格图形)的字符向量
xlab、ylab 指定横轴和纵轴标签的字符向量
xlim、ylim 指定横轴和纵轴最小值、最大值的二元数值向量

我们可以在高级函数内部或者后面讨论的面板函数内使用这些选项

我们也可以使用update()函数来调整lattice图形对象

继续歌手的例子:

plot(mygraph)
img
update(mygraph, col="red", pch=16, cex=.8, jitter=.05, lwd=2)
img

调节变量

通常情况下,调节变量是因子。但是对于连续的变量该如何操作呢?一种方式是使用R的cut()函数将连续的变量转换为离散的变量,另一种方法是lattice包提供可以将连续变量转换为名为shingle的数据结构。具体来说,连续变量被分成一系列(可能)重叠的范围

例如函数

myshingle <- equal.count(x, number=n, overlap=proportion)

将连续的变量x分成n个间隔,重叠的比例是proportion,每个间隔里的观测值个数相同,并将其返回为变量myshingle。

一旦变量转化为shingle,就可以用它作为一个调节变量。例如mtcars中的发动机排量是一个连续的变量,我们可以首先把它转换为3水平的shingle变量:

displacement <- equal.count(mtcars$disp, number=3, overlap=0)

接下来把它应用到xyplot()函数中:

xyplot(mpg ~ wt|displacement, data = mtcars,
       main = "Miles per Gallon vs. Weight by Engine Displacement",
       xlab = "Weight", ylab = "Miles per Gallon",
       layout = c(3, 1), aspect = 1.5)
img

这里我们使用了选项来调整面板的布局(一行3列)和纵横比(高/宽)来让三组的对比变得更容易。

下面我们学习面板函数来进一步自定义输出。

面板函数

每一个高水平的画图函数都采用了默认的函数来绘制面板图。默认函数遵循命名规则panel.graph_function,例如

xyplot(mpg~wt|displacement, data=mtcars)

也可以写成:

xyplot(mpg~wt|displacement, data=mtcars, panel=panel.xyplot)

这是一个强大的功能,因为它可以让我们用自己设计的默认函数来代替默认的面板函数。我们也可以将lattice包50多个默认函数中的一个或者多个集成到我们自定义的函数中,自定义的面板函数在设计满足我们需求的输出时给了我们很大的灵活性。

下面我们来看些例子。

在前面,我们画出了以汽车发动机排量为条件的汽车重量的油耗,如果你想要加上回归线、地毯图和网格线,我们需要怎么做呢?

library(lattice)

displacement <- equal.count(mtcars$disp, number=3, overlap=0)

mypanel <- function(x, y){
    panel.xyplot(x, y, pch=19)
    panel.rug(x,y)
    panel.grid(h=-1, v=-1) # 添加水平和垂直的网格线
    panel.lmline(x, y, col="red", lwd=1, lty=2) # 添加回归曲线
}


xyplot(mpg ~ wt|displacement, data=mtcars,
       layout=c(3,1),
       aspect=1.5,
       main = "Miles per Gallon vs. Weight by Engine Displacement",
       xlab = "Weight",
       ylab = "Miles per Gallon",
       panel = mypanel)
img

在第二个例子中,我们将画出以汽车变速器类型为条件的油耗和发动机排量之间的关系。除了画出自动和手动变速器发动机独立的面板图外,我们还将添加拟合曲线和水平均值曲线

library(lattice)

mtcars$transmission <- factor(mtcars$am, levels=c(0,1), labels=c("Automatic", "Manual"))

panel.smoother <- function(x, y){
    panel.grid(h=-1, v=-1)
    panel.xyplot(x, y)
    panel.loess(x, y)
    panel.abline(h = mean(y), lwd = 2, lty = 2, col = "darkgreen")
}

xyplot(mpg~disp|transmission,data=mtcars,
       scales=list(cex=.8, col="red"),
       panel = panel.smoother,
       xlab = "Displacement", ylab = "Miles per Gallon",
       main = "MPG vs Displacement by Transmission Type",
       sub = "Dotted lines are Groups Means", aspect = 1)
img

分组变量

当你在lattice绘图公式中增加调节变量时,该变量的每个水平的独立面板就会产生。如果想添加的结果和每个水平正好相反,可以指定改变了为分组变量。

比如说,我们想利用核密度图展示使用手动和自动变速器时汽车的油耗的分布:

densityplot(~mpg, data=mtcars,
            group=transmission,
            main="MPG Distribution by Transmission Type",
            xlab = "Miles per Gallon",
            auto.key = TRUE)
img

默认情况下,group=选项添加分组变量每个水平的图。值得注意的是,图例和关键字不会在默认情况下生成,这里auto.key=TRUE创建一个基本图例并把它放在图的上方。

下面实现更加自定义的图形:

colors <- c("red", "blue")
lines <- c(1,2)
points <- c(16, 17)

key.trans <- list(title="Transmission",
                  space="bottom", columns=2,
                  text=list(levels(mtcars$transmission)),
                  points=list(pch=points, col=colors),
                  lines=list(col=colors, lty=lines),
                  cex.title=1, cex=.9)

densityplot(~mpg, data=mtcars,
            group=transmission,
            main="MPG Distribution by Transmission Type",
            xlab = "Miles per Gallon",
            pch=points, lty=lines, col=colors,
            lwd=2, jitter=.005,
            key=key.trans)
img

让我们使用R自带的CO2数据集讨论一下在单个图中包含分组和调节变量的例子。

这个数据集描述了12种植物在7种二氧化碳浓度下的二氧化碳吸收率。6种植物来自魁北克,6种来自密西西比。每个产地有3种植物在冷藏条件下研究,3种在非冷藏条件下研究。在该例子中,plant是分组变量,TypeTreatment是调节变量。

library(lattice)
colors <- "darkgreen"
symbols <- c(1:12)
linetypes <- c(1:3)

key.species <- list(title="Plant",
                    space = "right",
                    text = list(levels(CO2$Plant)),
                    points = list(pch=symbols, col=colors))

xyplot(uptake~conc|Type*Treatment, data=CO2,
       group=Plant,
       type="o",
       pch=symbols, col=colors, lty=linetypes,
       main = "Carbon Dioxide Uptake\nin Grass Plants",
       ylab = "Uptake",
       xlab = "Concentration",
       sub = "Grass Species: Echinochloa crus-galli",
       key = key.species)
img

图形参数

lattice图形不受par()函数的影响,它使用的默认设置在一个大的列表对象中,可以通过trellis.par.get()函数获得并通过trellis.par.set()函数更改。我们可以使用show.settings()函数来直观地展示当前的图形设置。

查看默认设置:

show.settings()
img

将它们保存到列表:

mysettings <- trellis.par.get()

可以使用names()查看列表成分:

names(mysettings)
##  [1] "grid.pars"         "fontsize"          "background"       
##  [4] "panel.background"  "clip"              "add.line"         
##  [7] "add.text"          "plot.polygon"      "box.dot"          
## [10] "box.rectangle"     "box.umbrella"      "dot.line"         
## [13] "dot.symbol"        "plot.line"         "plot.symbol"      
## [16] "reference.line"    "strip.background"  "strip.shingle"    
## [19] "strip.border"      "superpose.line"    "superpose.symbol" 
## [22] "superpose.polygon" "regions"           "shade.colors"     
## [25] "axis.line"         "axis.text"         "axis.components"  
## [28] "layout.heights"    "layout.widths"     "box.3d"           
## [31] "par.xlab.text"     "par.ylab.text"     "par.zlab.text"    
## [34] "par.main.text"     "par.sub.text"

修改:

mysettings$superpose.symbol$pch <- c(1:10)
trellis.par.set(mysettings)

查看更改:

show.settings()
img

自定义图形条带

面板条带默认的背景是:第一个调节变量时桃红色,第二个调节变量时浅绿色,第三个调节变量的浅蓝色。我们可以使用上一节描述的方法;或是加强控制,写一个自定义条带各方面的函数。

library(lattice)
histogram(~height | voice.part, data = singer,
          strip = strip.custom(bg="lightgrey",
                               par.strip.text = list(color="black", cex=.8, font=3)),
          main = "Distribution of Heights by Voice Pitch",
          xlab = "Height (inches)")
img

option=选项用来指定设定条带外观的函数,尽管我们可以从头写一个函数(参见?strip.default),但是改变一些设置并使用其他项的默认值更简单。strip.custom()函数可以帮助我们实现。bg选项控制了背景颜色,par.strip.text允许我们控制条带文本的外观。font选项可以分别取数值1、2、3、4,代表正常字体、粗体、斜体和粗斜体。

页面布局

split选项将一个页面分成指定数量的行和列,并把图放到结果矩阵的特定单元格。split选项的格式是:

split = c(x, y, nx, ny)

也就是在包括nx乘以ny个图形的数组中,把当前图形放在x,y的位置,图形的起始位置是在左上角。

library(lattice)

graph1 <- histogram(~height | voice.part, data = singer,
                    main = "Height of Choral Singers by Voice Part")
graph2 <- bwplot(height~voice.part, data = singer)
plot(graph1, split = c(1, 1, 1, 2))
plot(graph2, split = c(1, 2, 1, 2), newpage = FALSE)
img

我们可以更好的控制尺寸:

plot(graph1, position = c(0, .3, 1, 1))
plot(graph2, position = c(0, 0, 1, .3), newpage = FALSE)
img

这里的position=c(xmin, ymin, xmax, ymax)选项中,页面的坐标系是x轴和y轴都从0到1的矩形,原点在左下角的(0,0)。

我们还可以控制面板的顺序

histogram(~height | voice.part, data = singer,
          index.cond = list(c(2, 4, 6, 8, 1, 3, 5, 7)))
img

深入学习

Deepayan Sarkar 的“Lattice Graphics: An Introduction”(http://mng.bz/jXUG, 2008)和Willian G.Jacoby的“An Introduction to Lattice Graphics in R” (http://mng.bz/v4TO, 2010)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容