web-dev-qa-db-ja.com

ggplot2スパニンググループのネストされたファセット

3つのグループ化変数によってファセット化されたプロットを作成したいという状況に遭遇しました。そのためには、単にfacet_grid(f1 ~ f2 + f3)を使用しますが、ここでの問題は、f2のラベルが冗長になることであり、f2内にネストされたf3のファセットにまたがる方がはるかに優れています。

MWE:

library('tibble')
library('ggplot2')
df <- tribble(
  ~x, ~y, ~f1, ~f2, ~f3,
  0.5, 0.5, "a", "a", "a",
  0.5, 0.5, "b", "a", "a",
  0.5, 0.5, "a", "b", "a",
  0.5, 0.5, "b", "b", "a",
  0.5, 0.5, "a", "a", "b",
  0.5, 0.5, "b", "a", "b",
  0.5, 0.5, "a", "b", "b",
  0.5, 0.5, "b", "b", "b"
)


p <- ggplot(df, aes(x = x, y = y)) +
  geom_point() +
  facet_grid(f1 ~ f2 + f3)

MWE of nested facet plot

繰り返しになりますが、f2のラベルを組み合わせて、冗長にならないようにしています。

編集:これは、新しいグループを追加するのではなく、既存のグループを使用してファセットを変更する方法を尋ねるという点で、他の質問とは異なります。

18
ZNK

これに対する答えは、gridおよびgtableパッケージ内にあります。プロット内のすべてが特定の順序で配置されており、少し掘るとすべてがどこにあるかを見つけることができます。

library('gtable')
library('grid')
library('magrittr') # for the %>% that I love so well

# First get the grob
z <- ggplotGrob(p) 

この操作の最終的な目標は、上部のファセットラベルをオーバーレイすることですが、トリックは、これらのファセットの両方がグリッドスペースの同じ行に存在することです。これらはテーブル内のテーブルです(「strip」という名前の行を見てください。zeroGrobにも注意してください。これらは後で役立ちます):

z
## TableGrob (13 x 14) "layout": 34 grobs
##     z         cells       name                                   grob
## 1   0 ( 1-13, 1-14) background        rect[plot.background..rect.522]
## 2   1 ( 7- 7, 4- 4)  panel-1-1               gTree[panel-1.gTree.292]

                                    ...

## 20  3 ( 7- 7,12-12)   axis-r-1                         zeroGrob[NULL]
## 21  3 ( 9- 9,12-12)   axis-r-2                         zeroGrob[NULL]
## 22  2 ( 6- 6, 4- 4)  strip-t-1                          gtable[strip]
## 23  2 ( 6- 6, 6- 6)  strip-t-2                          gtable[strip]
## 24  2 ( 6- 6, 8- 8)  strip-t-3                          gtable[strip]
## 25  2 ( 6- 6,10-10)  strip-t-4                          gtable[strip]
## 26  2 ( 7- 7,11-11)  strip-r-1                          gtable[strip]
## 27  2 ( 9- 9,11-11)  strip-r-2                          gtable[strip]

                                    ...

## 32  8 ( 3- 3, 4-10)   subtitle  zeroGrob[plot.subtitle..zeroGrob.519]
## 33  9 ( 2- 2, 4-10)      title     zeroGrob[plot.title..zeroGrob.518]
## 34 10 (12-12, 4-10)    caption   zeroGrob[plot.caption..zeroGrob.520]

最初のストリップにズームインすると、ネストされた構造が表示されます。

z$grob[[22]]
## TableGrob (2 x 1) "strip": 2 grobs
##   z     cells  name                                 grob
## 1 1 (1-1,1-1) strip absoluteGrob[strip.absoluteGrob.451]
## 2 2 (2-2,1-1) strip absoluteGrob[strip.absoluteGrob.475]

グロブごとに、プロットされた順序(z)、グリッド内の位置(cells)をリストするオブジェクトがあります。 ))、ラベル(name)、およびジオメトリ(grob)。

Gtables内にgtablesを作成できるので、これを使用して元のプロットの上にプロットします。まず、置換が必要なプロット内の位置を見つける必要があります。

# Find the location of the strips in the main plot
locations <- grep("strip-t", z$layout$name)

# Filter out the strips (trim = FALSE is important here for positions relative to the main plot)
strip <- gtable_filter(z, "strip-t", trim = FALSE)

# Gathering our positions for the main plot
top <- strip$layout$t[1]
l   <- strip$layout$l[c(1, 3)]
r   <- strip$layout$r[c(2, 4)]

ポジションが決まったら、交換用のテーブルを作成する必要があります。リストのマトリックスを使ってこれを行うことができます(はい、それは奇妙です。それを使って転がしてください)。このマトリックスには、2つのファセットとそれらの間のギャップがあるため、この場合は3つの列と2つの行が必要です。後でマトリックス内のデータを置き換えるだけなので、zeroGrobsでデータを作成します。

mat   <- matrix(vector("list", length = 6), nrow = 2)
mat[] <- list(zeroGrob())

# The separator for the facets has zero width
res <- gtable_matrix("toprow", mat, unit(c(1, 0, 1), "null"), unit(c(1, 1), "null"))

マスクは2つのステップで作成され、最初のファセットグループと2番目のファセットグループをカバーします。最初の部分では、前に記録した場所を使用して、元のプロットから適切なgrobを取得し、それを置換行列resの上に、全長にわたって追加します。次に、その行列をプロットの上に追加します。

# Adding the first layer
zz <- res %>%
  gtable_add_grob(z$grobs[[locations[1]]]$grobs[[1]], 1, 1, 1, 3) %>%
  gtable_add_grob(z, ., t = top,  l = l[1],  b = top,  r = r[1], name = c("add-strip"))

# Adding the second layer (note the indices)
pp <- gtable_add_grob(res, z$grobs[[locations[3]]]$grobs[[1]], 1, 1, 1, 3) %>%
  gtable_add_grob(zz, ., t = top,  l = l[2],  b = top,  r = r[2], name = c("add-strip"))

# Plotting
grid.newpage()
print(grid.draw(pp))

Nested facet labels

23
ZNK

このスレッドと意図しない自己宣伝を壊して申し訳ありませんが、これをfacet_nested()関数に一般化しようとしましたが、コードは ここ にあります。 コードは完全にスタンドアロンである必要があります。つまり、パッケージ内の他の関数に依存しないでください(これは、現時点でいくつかのランダムなプロット関数を備えたパブリックパーソナルパッケージです)。 編集:facet_nested()関数は、パッケージ内の他の場所の関数を使用するようになりました。

機能はあまりテストされていませんが、人にとっては便利かもしれないと思いました。たぶん、これからいくつかの良いフィードバックが来るでしょう。

ストリップのグループ化の範囲を超えて、この関数で行った他の2つの変更があります。 1つは、欠落している変数を自動的に展開しないことです。これは、2つのdata.frameでプロットする場合、ネストされたファセットは、vars()の2番目以降の引数にエントリがなくてもネストされていないファセットと共存できるはずだと私が考えたためです。 2つ目は、ストリップを外側から内側に並べて、switchが設定されている場合でも、内側が外側よりもパネルに近くなるようにすることです。

dfが上記の質問のdfであると仮定すると、この質問のプロットを再現すると、次のようになります。

p <- ggplot(df, aes(x = x, y = y)) +
  geom_point() +
  facet_nested(f1 ~ f2 + f3)

enter image description here

関連する質問 より現実的なプロットの例もありました。これは、dfがその質問のdfであると仮定すると、次のように機能します。

p <- ggplot(df, aes("", density)) + 
  geom_boxplot(width=0.7, position=position_dodge(0.7)) + 
  theme_bw() +
  facet_nested(. ~ species + location +  position) +
  theme(panel.spacing=unit(0,"lines"),
        strip.background=element_rect(color="grey30", fill="grey90"),
        panel.border=element_rect(color="grey90"),
        axis.ticks.x=element_blank()) +
  labs(x="")

enter image description here

27
teunbrand