R 數(shù)據(jù)可視化 —— grid 系統(tǒng)(二)

前言

在前面一節(jié)中,我們主要介紹了如何使用 grid 來生成圖形輸出萄焦,以及圖形窗口的布局扇商。利用這些知識凤瘦,可以很容易地為圖形添加注釋,編寫一些簡單的繪圖函數(shù)

這一節(jié)案铺,我們將重點介紹如何使用 grid 函數(shù)來創(chuàng)建和操作圖形對象蔬芥。利用這些信息,可以交互式地編輯和修改圖形輸出

圖形對象

1. 控制圖像輸出

我們可以使用圖形原語來繪制圖形輸出,并返回一個圖形對象(grobs)笔诵,例如

library(RColorBrewer)

grid.circle(
  name = "circles",
  x = seq(0.1, 0.9, length = 40),
  y = 0.5 + 0.4 * sin(seq(0, 2 * pi, length = 40)),
  r = abs(0.1 * cos(seq(0, 2 * pi, length = 40))),
  gp = gpar(col = brewer.pal(40, "Set2"))
)

這段代碼將會繪制一串圓形


同時也會生成一個 circle grob返吻,該對象保存了當前繪制的這些圓形的信息

grid 保留了一個顯示列表,用于記錄當前畫布中的所有的 viewportgrobs乎婿。因此测僵,grid.circle() 函數(shù)構造的對象也會保存在顯示列表中,意味著我們可以根據(jù)對象的名稱 circles 來獲取谢翎、修改該對象

使用 grid.get() 函數(shù)捍靠,可以獲取該 circle 對象的拷貝

> grid.get("circles")
circle[circles]

使用 grid.edit() 可以用來修改該 circle 對象的圖像屬性

grid.edit(
  "circles",
  gp = gpar(
    col = brewer.pal(10, "RdBu")
    )
  )

修改顏色屬性之后,會直接顯示在圖形輸出中

還可以使用 grid.remove() 函數(shù)森逮,從顯示列表中刪除圖形對象的輸出

grid.remove("circles")

一片空白榨婆,什么也沒有

1.1 標準的函數(shù)及參數(shù)

控制 grobs 的函數(shù)包括:

所有圖像輸出函數(shù)的第一個參數(shù)都是圖像對象的名稱,如果參數(shù) grep = TRUE褒侧,可以接受正則表達式對象名稱

如果 global = TRUE良风,則會返回顯示列表中所有匹配的對象,例如

suffix <- c("even", "odd")

for (i in 1:8)
  grid.circle(
    name = paste0("circle.", suffix[i %% 2 + 1]),
    r = (9 - i) / 20,
    gp = gpar(
      col = NA, 
      fill = grey(i / 10)
      )
  )

我們繪制了 8 個同心圓闷供,并根據(jù)奇偶順序將 circle grob 命名為 circle.oddcircle.even

然后拖吼,我們可以使用 grid.edit() 函數(shù),修改所有名為 circle.oddgrobs 的顏色

grid.edit(
  "circle.odd", 
  gp = gpar(
    fill = brewer.pal(4, "Set3")[4]),
  global = TRUE
  )

或者这吻,用正則表達式來匹配以 circle 開頭的 grob

grid.edit(
  "circle", 
  gp = gpar(
    col = "#80b1d3", 
    fill = "#fdb462"
    ),
  grep=TRUE, 
  global=TRUE
  )

只要我們知道了 grobs 的名稱吊档,就可以對其獲取、修改或刪除

getNames() 函數(shù)唾糯,可以幫助我們獲取當前圖形中所有 grobs 的名稱

2. grob 排布結構

grob 的排布結果包括:

  • gList:包含多個 grobslist
  • gTreegrobs 的樹形結構怠硼,即一個 grob 中包含其他的 grob

例如,對于 xaxis grob

pushViewport(viewport(y = 0.5, height = 0.5, width = 0.5))
grid.rect()
pushViewport(viewport(y = 0.5, height = 0.5, width = 0.5))

grid.xaxis(name="axis1", at=1:4/5)

會包含有多個子 grob移怯,如線條香璃,文本等

> childNames(grid.get("axis1"))
[1] "major"  "ticks"  "labels"

如果把 xaxis grob 看作為一棵樹的根,那么它包含三個子 grob舟误。

其中 majortickslines grob葡秒,labelstext grob

其中 at 參數(shù)設置了軸刻度嵌溢,我們可以使用 grid.edit 來修改

grid.edit("axis1", at=1:3/4)

那想要修改 labels 的格式眯牧,怎么辦?即如何訪問一個對象的子對象呢赖草?

可以使用 gPath(grob path) 函數(shù)学少,類似于 vpPath,可以使用父節(jié)點名稱加子節(jié)點名稱來訪問

grid.edit(gPath("axis1", "labels"), rot=45)

或者秧骑,也可以使用 axis1::labels 方式來訪問

注意grobs 的搜索是深度優(yōu)先版确,也就是說扣囊,如果在顯示列表中遍歷到了一個 gTree grob,且未找到匹配項绒疗,則會對該 grob 執(zhí)行深度優(yōu)先遍歷

這種 gTree 結構對象侵歇,也包含 gpvp 參數(shù)。

在父節(jié)點上設置對應的 gp 參數(shù)值吓蘑,會作為默認值傳遞給子對象盒至。例如

grid.xaxis(gp=gpar(col="grey"))

也可以將一個 viewport 直接作為參數(shù)值傳遞

grid.xaxis(vp=viewport(y=0.75, height=0.5))

3. 圖形對象

在前面的章節(jié)中,我們介紹的都是如何使用函數(shù)直接生成圖形輸出并返回圖形對象(grob

在這一節(jié)士修,我們將介紹如果創(chuàng)建 grob枷遂,但不繪制圖形,通過對 grob 創(chuàng)建及修改棋嘲,并在最后使用 grid.draw() 函數(shù)來繪制出圖形酒唉。

每個能產生圖形輸出和圖形對象的 grid 函數(shù)都有一個對應的只創(chuàng)建圖形對象,沒有圖形輸出的函數(shù)

例如沸移,grid.circle() 對應于 circleGrob()痪伦,grid.edit() 對應于 editGrob(),在前面的函數(shù)表中都有列出

例如

grid.newpage()
pushViewport(viewport(width = 0.5, height = 0.5))
# 創(chuàng)建 x 軸對象
ag <- xaxisGrob(at=1:4/5)
# 修改對象雹锣,將標簽的字體變?yōu)樾斌w
ag <- editGrob(ag, "labels", gp=gpar(fontface="italic"))
# 繪圖
grid.draw(ag)

我們可以將不同的 grob 組合在一起网沾,生成一個復雜的圖形。比如

grid.newpage()

tg <- textGrob("sample text")

rg <- rectGrob(
  width = 1.2*grobWidth(tg),
  height = 1.5*grobHeight(tg)
  )

boxedText <- gTree(
  children = gList(rg, tg)
  )

我們構建一個名為 boxedTextgTree 對象蕊爵,包含其子對象包括一個文本和一個矩形

我們直接可以繪制組合對象

grid.draw(boxedText)

而對該對象的圖形屬性的修改辉哥,會反映到具體的子對象中

grid.draw(
  editGrob(
    boxedText, 
    gp=gpar(col="skyblue")
    )
  )

指定 viewport

grid.draw(
  editGrob(
    boxedText, 
    vp = viewport(angle=45), 
    gp = gpar(fontsize=18)
    )
  )

3.1 捕獲輸出

在上面的例子中,我們先構建了一個組合對象攒射,然后繪制該對象

還可以反著來醋旦,先繪制圖形對象,然后對它們進行組合会放。

使用 grid.grab() 函數(shù)饲齐,可以獲取當前畫布中所有輸出的圖形對象,并以 gTree 的形式返回

例如咧最,我們使用 ggplot2 繪制一個直方圖捂人,并獲取所有圖形對象

ggplot(mpg) + geom_histogram(aes(displ, fill = class), bins = 10, position = "dodge")

histTree <- grid.grab()

然后,你可以嘗試運行下面的代碼

grid.newpage()
grid.draw(histTree)

你會發(fā)現(xiàn)矢沿,可以繪制出一張一模一樣的圖


也可以使用 grid.grabExpr 來獲取表達式的輸出圖形對象

grid.grabExpr(
  print(
    ggplot(mpg) + 
      geom_histogram(
        aes(displ, fill = class), 
        bins = 10, 
        position = "dodge")
    )
  )

4. 圖形對象的放置

假設我們有一個復雜圖形

# 文本對象
label <- textGrob(
  "A\nPlot\nLabel ",
  x = 0, 
  just = "left"
  )

x <- seq(0.1, 0.9, length=50)
y <- runif(50, 0.1, 0.9)

# gTree 結構圖形對象滥搭,包括矩形、線圖咨察、點圖
gplot <- gTree(
  children = gList(
    rectGrob(
      gp = gpar(
        col = "grey60",
        fill = "#cbd5e8",
        alpha = 0.3)
    ),
    linesGrob(
      x, 
      y,
      gp = gpar(
        col = "#33a02c"
      )),
    pointsGrob(
      x, y, 
      pch = 16, 
      size = unit(5, "mm"),
      gp = gpar(
        col = "#fb8072"
      ))
  ),
  vp = viewport(
    width = unit(1, "npc") - unit(5, "mm"),
    height = unit(1, "npc") - unit(5, "mm")
  )
)

我們可以使用上一章節(jié)提到的布局方法论熙,將該圖像設計為 12 列的布局

layout <- grid.layout(
  nrow = 1, 
  ncol = 2, 
  widths = unit(
    c(1, 1), 
    c("null", "grobwidth"), 
    list(NULL, label)
    )
  )

然后將圖形繪制到指定位置中

pushViewport(viewport(layout=layout))
pushViewport(viewport(layout.pos.col=2))
grid.draw(label)
popViewport()

pushViewport(viewport(layout.pos.col=1))
grid.draw(gplot)
popViewport(2)

但其實,grid 提供了更簡便的函數(shù)用于放置 grobs

grid.frame() 函數(shù)創(chuàng)建一個沒有子對象的 gTree摄狱,可以使用 grid.pack() 向其中添加子對象脓诡,同時確保為每個子對象保留足夠的繪圖空間

上面的代碼可以改寫成

grid.newpage()

# 新建一個空 frame
grid.frame(name="frame1")
# 放置 gplot 對象,在這一階段媒役,gplot 會占據(jù)整個 frame
grid.pack("frame1", gplot)
# 在 frame 的右邊放置 label 對象
grid.pack("frame1", label, side="right")

這種動態(tài)的方式很簡便祝谚,但是也帶來了時間上的花費,隨著需要放置的對象越來越多酣衷,速度會越來越慢交惯。

另一種替代的方式是,先定義一個布局穿仪,然后再放置對象

grid.frame(name="frame1", layout=layout)

grid.place("frame1", gplot, col=1)
grid.place("frame1", label, col=2)

4.1 安靜模式

在上面兩個例子中席爽,每次放置一個 grob 都會更新一遍圖形輸出。所以啊片,一個更好的方式是只锻,在安靜模式下創(chuàng)建一個 frame,然后放置 grobs紫谷。

安靜模式齐饮,即使用對象函數(shù) frameGrob()placeGrob()/packGrob 創(chuàng)建 frame、放置 grobs笤昨,但是不會輸出圖形祖驱,只有在所有設置完成之后,使用 grid.draw 一次性繪制

# 創(chuàng)建 frame
fg <- frameGrob(layout=layout)
# 添加 grob
fg <- placeGrob(fg, gplot, col=1)
fg <- placeGrob(fg, label, col=2)
# 一次性繪制
grid.draw(fg)
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末瞒窒,一起剝皮案震驚了整個濱河市捺僻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌崇裁,老刑警劉巖陵像,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異寇壳,居然都是意外死亡醒颖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門壳炎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泞歉,“玉大人,你說我怎么就攤上這事匿辩⊙遥” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵铲球,是天一觀的道長挺庞。 經常有香客問我,道長稼病,這世上最難降的妖魔是什么选侨? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任掖鱼,我火速辦了婚禮,結果婚禮上援制,老公的妹妹穿的比我還像新娘戏挡。我一直安慰自己,他們只是感情好晨仑,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布褐墅。 她就那樣靜靜地躺著,像睡著了一般洪己。 火紅的嫁衣襯著肌膚如雪妥凳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天答捕,我揣著相機與錄音逝钥,去河邊找鬼。 笑死噪珊,一個胖子當著我的面吹牛晌缘,可吹牛的內容都是我干的。 我是一名探鬼主播痢站,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼磷箕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了阵难?” 一聲冷哼從身側響起岳枷,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呜叫,沒想到半個月后空繁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡朱庆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年盛泡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娱颊。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡傲诵,死狀恐怖,靈堂內的尸體忽然破棺而出箱硕,到底是詐尸還是另有隱情拴竹,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布剧罩,位于F島的核電站栓拜,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜幕与,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一挑势、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纽门,春花似錦薛耻、人聲如沸营罢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饲漾。三九已至蝙搔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間考传,已是汗流浹背吃型。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留僚楞,地道東北人勤晚。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像泉褐,于是被迫代替她去往敵國和親赐写。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容