LoginSignup
27

More than 5 years have passed since last update.

散布図と周辺分布をあわせて描きたい (scatterplot with marginal distributions)

Last updated at Posted at 2016-03-05

Q. 散布図と周辺分布をあわせて描きたいです。

  • {ggExtra}パッケージのggMarginal関数は見た目がイマイチです。
  • {gridExtra}パッケージのgrid.arrange関数を使うと以下のようになってしまい、軸の目盛が揃わなくて困っています。
NG_example.R
library(ggplot2)

p_xy <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
  theme(text=element_text(size=20)) +
  geom_point(alpha=0.8, size=2, shape=1) +
  scale_x_continuous(limits=c(4, 8)) +
  scale_y_continuous(limits=c(2, 4.5))

p_x <- ggplot(iris, aes(Sepal.Length)) +
  theme(axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank()) +
  geom_histogram(aes(y=..density..), colour='black', fill='white') +
  geom_density(alpha=0.3, fill='gray20') +
  scale_x_continuous(limits=c(4, 8)) +
  labs(x='', y='')

p_y <- ggplot(iris, aes(Sepal.Width)) +
  theme(axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank()) +
  coord_flip() +
  geom_histogram(aes(y=..density..), colour='black', fill='white') +
  geom_density(alpha=0.3, fill='gray20') +
  scale_x_continuous(limits=c(2, 4.5)) +
  labs(x='', y='')


library(gridExtra)
png(file='NG_example.png', res=300, w=1500, h=1500)
grid.arrange(
  arrangeGrob(p_x,       ncol=2, widths=c(3,1)),
  arrangeGrob(p_xy, p_y, ncol=2, widths=c(3,1)),
  heights=c(1,3)
)
dev.off()

NG_exapmle.png

A.

{ggplot2}パッケージのtheme関数のplot.marginオプションを手調整で頑張る方法があります。
http://stackoverflow.com/questions/8545035/scatterplot-with-marginal-histograms-in-ggplot2

しかし、この方法は手調整が大変でやってられません。そこで{gtable}パッケージを使います。基本的な使い方はbaptisteさんが以下で説明しています。
https://github.com/baptiste/gridextra/wiki/arranging-ggplot

OK_example.R
library(ggplot2)

p_xy <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
  theme(text=element_text(size=20)) +
  geom_point(alpha=0.8, size=2, shape=1) +
  scale_x_continuous(limits=c(4, 8)) +
  scale_y_continuous(limits=c(2, 4.5))

p_x <- ggplot(iris, aes(Sepal.Length)) +
  theme(axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank()) +
  geom_histogram(aes(y=..density..), colour='black', fill='white') +
  geom_density(alpha=0.3, fill='gray20') +
  scale_x_continuous(limits=c(4, 8)) +
  labs(x='', y='')

p_y <- ggplot(iris, aes(Sepal.Width)) +
  theme(axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank()) +
  coord_flip() +
  geom_histogram(aes(y=..density..), colour='black', fill='white') +
  geom_density(alpha=0.3, fill='gray20') +
  scale_x_continuous(limits=c(2, 4.5)) +
  labs(x='', y='')

p_emp <- ggplot(data.frame(0,0)) + theme_bw() + theme(rect=element_rect(fill='white'), panel.border=element_blank())

g_xy <- ggplotGrob(p_xy)
g_x <- ggplotGrob(p_x)
g_y <- ggplotGrob(p_y)
g_emp <- ggplotGrob(p_emp)

g1 <- cbind(g_x, g_emp, size='first')
g2 <- cbind(g_xy, g_y, size='first')
g <- rbind(g1, g2, size='first')

g$widths[1:3] <- grid::unit.pmax(g1$widths[1:3], g2$widths[1:3])
g$heights[7:12] <- g$widths[5:10] <- rep(unit(0.3,'mm'), 6)
g$heights[6] <- g$widths[11] <- unit(3,'cm')

png(file='OK_example.png', res=300, w=1500, h=1500)
grid::grid.draw(g)
dev.off()

OK_example.png

コードの説明

基本的な方針はggplotGrob関数で個々のplotからgrob(graphical objectとのことです)を作り、それをrbindcbindで組み合わせていきます。上記ではcbind関数のsizeオプションは'first'にしていますが、デフォルトの'max''min'ではエラーが出てしまいます(既知のバグのようです)。

上記のgtableクラスのオブジェクトであるgprintしますと以下になっています。

> g
TableGrob (20 x 14) "layout": 68 grobs
    z         cells       name                                   grob
1   0 ( 1-10, 1- 7) background        rect[plot.background..rect.339]
2   5 ( 5- 5, 3- 3)     spacer                         zeroGrob[NULL]
3   7 ( 6- 6, 3- 3)     axis-l    absoluteGrob[GRID.absoluteGrob.334]
4   3 ( 7- 7, 3- 3)     spacer                         zeroGrob[NULL]
5   6 ( 5- 5, 4- 4)     axis-t                         zeroGrob[NULL]
6   1 ( 6- 6, 4- 4)      panel               gTree[panel-1.gTree.320]
7   9 ( 7- 7, 4- 4)     axis-b    absoluteGrob[GRID.absoluteGrob.330]
8   4 ( 5- 5, 5- 5)     spacer                         zeroGrob[NULL]
9   8 ( 6- 6, 5- 5)     axis-r                         zeroGrob[NULL]
10  2 ( 7- 7, 5- 5)     spacer                         zeroGrob[NULL]
11 10 ( 4- 4, 4- 4)     xlab-t                         zeroGrob[NULL]
12 11 ( 8- 8, 4- 4)     xlab-b titleGrob[axis.title.x..titleGrob.323]
13 12 ( 6- 6, 2- 2)     ylab-l titleGrob[axis.title.y..titleGrob.326]
14 13 ( 6- 6, 6- 6)     ylab-r                         zeroGrob[NULL]
15 14 ( 3- 3, 4- 4)   subtitle  zeroGrob[plot.subtitle..zeroGrob.336]
16 15 ( 2- 2, 4- 4)      title     zeroGrob[plot.title..zeroGrob.335]
17 16 ( 9- 9, 4- 4)    caption   zeroGrob[plot.caption..zeroGrob.337]
18  0 ( 1-10, 8-14) background        rect[plot.background..rect.392]
19  5 ( 5- 5,10-10)     spacer                         zeroGrob[NULL]
20  7 ( 6- 6,10-10)     axis-l                         zeroGrob[NULL]
21  3 ( 7- 7,10-10)     spacer                         zeroGrob[NULL]
22  6 ( 5- 5,11-11)     axis-t                         zeroGrob[NULL]
23  1 ( 6- 6,11-11)      panel               gTree[panel-1.gTree.387]
24  9 ( 7- 7,11-11)     axis-b                         zeroGrob[NULL]
25  4 ( 5- 5,12-12)     spacer                         zeroGrob[NULL]
26  8 ( 6- 6,12-12)     axis-r                         zeroGrob[NULL]
27  2 ( 7- 7,12-12)     spacer                         zeroGrob[NULL]
28 10 ( 4- 4,11-11)     xlab-t                         zeroGrob[NULL]
29 11 ( 8- 8,11-11)     xlab-b                         zeroGrob[NULL]
30 12 ( 6- 6, 9- 9)     ylab-l                         zeroGrob[NULL]
31 13 ( 6- 6,13-13)     ylab-r                         zeroGrob[NULL]
32 14 ( 3- 3,11-11)   subtitle  zeroGrob[plot.subtitle..zeroGrob.389]
33 15 ( 2- 2,11-11)      title     zeroGrob[plot.title..zeroGrob.388]
34 16 ( 9- 9,11-11)    caption   zeroGrob[plot.caption..zeroGrob.390]
35  0 (11-20, 1- 7) background        rect[plot.background..rect.299]
36  5 (15-15, 3- 3)     spacer                         zeroGrob[NULL]
37  7 (16-16, 3- 3)     axis-l    absoluteGrob[GRID.absoluteGrob.294]
38  3 (17-17, 3- 3)     spacer                         zeroGrob[NULL]
39  6 (15-15, 4- 4)     axis-t                         zeroGrob[NULL]
40  1 (16-16, 4- 4)      panel               gTree[panel-1.gTree.274]
41  9 (17-17, 4- 4)     axis-b    absoluteGrob[GRID.absoluteGrob.287]
42  4 (15-15, 5- 5)     spacer                         zeroGrob[NULL]
43  8 (16-16, 5- 5)     axis-r                         zeroGrob[NULL]
44  2 (17-17, 5- 5)     spacer                         zeroGrob[NULL]
45 10 (14-14, 4- 4)     xlab-t                         zeroGrob[NULL]
46 11 (18-18, 4- 4)     xlab-b titleGrob[axis.title.x..titleGrob.277]
47 12 (16-16, 2- 2)     ylab-l titleGrob[axis.title.y..titleGrob.280]
48 13 (16-16, 6- 6)     ylab-r                         zeroGrob[NULL]
49 14 (13-13, 4- 4)   subtitle  zeroGrob[plot.subtitle..zeroGrob.296]
50 15 (12-12, 4- 4)      title     zeroGrob[plot.title..zeroGrob.295]
51 16 (19-19, 4- 4)    caption   zeroGrob[plot.caption..zeroGrob.297]
52  0 (11-20, 8-14) background        rect[plot.background..rect.379]
53  5 (15-15,10-10)     spacer                         zeroGrob[NULL]
54  7 (16-16,10-10)     axis-l    absoluteGrob[GRID.absoluteGrob.374]
55  3 (17-17,10-10)     spacer                         zeroGrob[NULL]
56  6 (15-15,11-11)     axis-t                         zeroGrob[NULL]
57  1 (16-16,11-11)      panel               gTree[panel-1.gTree.360]
58  9 (17-17,11-11)     axis-b    absoluteGrob[GRID.absoluteGrob.370]
59  4 (15-15,12-12)     spacer                         zeroGrob[NULL]
60  8 (16-16,12-12)     axis-r                         zeroGrob[NULL]
61  2 (17-17,12-12)     spacer                         zeroGrob[NULL]
62 10 (14-14,11-11)     xlab-t                         zeroGrob[NULL]
63 11 (18-18,11-11)     xlab-b titleGrob[axis.title.x..titleGrob.366]
64 12 (16-16, 9- 9)     ylab-l titleGrob[axis.title.y..titleGrob.363]
65 13 (16-16,13-13)     ylab-r                         zeroGrob[NULL]
66 14 (13-13,11-11)   subtitle  zeroGrob[plot.subtitle..zeroGrob.376]
67 15 (12-12,11-11)      title     zeroGrob[plot.title..zeroGrob.375]
68 16 (19-19,11-11)    caption   zeroGrob[plot.caption..zeroGrob.377]

さて、カスタマイズする上ではさきほどのコードの中の以下の3行が重要です。

g$widths[1:3] <- grid::unit.pmax(g1$widths[1:3], g2$widths[1:3])
g$heights[7:12] <- g$widths[5:10] <- rep(unit(0.3,'mm'), 6)
g$heights[6] <- g$widths[11] <- unit(3,'cm')
  • 1行目:上段(g1)の左3列と下段(g2)の左3列の単位の大きい方を取ることで、ylabaxis-lの文字がかぶるのを防いでいます。
  • 2行目:「左上panelと左下titleの間のスペース(g$heights[7:12]に相当)」と「左下panelと右下panelの間のスペース(g$widths[5:10]に相当)」を0.3mm*6の1.8mmで揃えています。左下titleの大きさは今回は0となるのでこれで揃います。
  • 3行目:「左上panelの縦幅」と「右下panelの横幅」を3cmで揃えています。

数値のところはそれぞれ好きな大きさにできます。

もう一つの方法

散布図行列のようにサブの図の数が多い場合には、cbind関数とrbind関数を組み合わせて作るのが大変です。また、空のg_empを作るのも面倒です。そこで、gtable関数とgtable_add_grob関数を使います。

OK_example2.R
library(ggplot2)

p_xy <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
  theme(text=element_text(size=20)) +
  geom_point(alpha=0.8, size=2, shape=1) +
  scale_x_continuous(limits=c(4, 8)) +
  scale_y_continuous(limits=c(2, 4.5))

p_x <- ggplot(iris, aes(Sepal.Length)) +
  theme(axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank()) +
  geom_histogram(aes(y=..density..), colour='black', fill='white') +
  geom_density(alpha=0.3, fill='gray20') +
  scale_x_continuous(limits=c(4, 8)) +
  labs(x='', y='')

p_y <- ggplot(iris, aes(Sepal.Width)) +
  theme(axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank()) +
  coord_flip() +
  geom_histogram(aes(y=..density..), colour='black', fill='white') +
  geom_density(alpha=0.3, fill='gray20') +
  scale_x_continuous(limits=c(2, 4.5)) +
  labs(x='', y='')

g_xy <- ggplotGrob(p_xy)
g_x <- ggplotGrob(p_x)
g_y <- ggplotGrob(p_y)

max_width <- grid::unit.pmax(g_xy$widths[1:3], g_x$widths[1:3])
max_height <- grid::unit.pmax(g_xy$heights[7:10], g_y$heights[7:10])
g_xy$widths[1:3] <- g_x$widths[1:3] <- max_width
g_xy$heights[7:10] <- g_y$heights[7:10] <- max_height
g_x$heights[7:10] <- rep(unit(0,'mm'), 4)
g_y$widths[1:3] <- rep(unit(0,'mm'), 3)

g <- gtable::gtable(widths=unit(c(3, 1), 'null'), heights=unit(c(1, 3), 'null'))
g <- gtable::gtable_add_grob(g, g_xy, 2, 1)
g <- gtable::gtable_add_grob(g, g_x, 1, 1)
g <- gtable::gtable_add_grob(g, g_y, 2, 2)

png(file='OK_example2.png', res=300, w=1500, h=1500)
grid::grid.draw(g)
dev.off()

コードの説明

g_xy$widths[1:3] <- g_x$widths[1:3] <- max_width
g_xy$heights[7:10] <- g_y$heights[7:10] <- max_height

この1行目:左上の図と左下の図の左端を揃えておきます。
この2行目:左下の図と右下の図の下端を揃えておきます。

g <- gtable::gtable(widths=unit(c(3, 1), 'null'), heights=unit(c(1, 3), 'null'))
g <- gtable::gtable_add_grob(g, g_xy, 2, 1)

この1行目:2×2のgrobの格納場所を持つ、大きさの比率だけ決めた空のgtableクラスのオブジェクトを作ります。
この2行目:gの2行1列目にg_xyを追加します。以下同様です。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27