LoginSignup
16
16

More than 3 years have passed since last update.

nested dataでggplot

Last updated at Posted at 2020-05-09

これまでいくつかの記事でnest()あるいはgroup_nest()を使ってデータを畳み込みながら見通しよく解析を進める方法について紹介して来ました。

この記事では、ggplot2を使った作図もnested dataの中で完結してしまう方法を紹介します。

スクリーンショット 2020-05-20 17.18.03.png

右下の指先で示している箇所の話です。

みんな大好きiris

今回もirisのお世話になりましょう。

> iris %>% as.tibble()
# A tibble: 150 x 5
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          <dbl>       <dbl>        <dbl>       <dbl> <fct>  
 1          5.1         3.5          1.4         0.2 setosa 
 2          4.9         3            1.4         0.2 setosa 
 3          4.7         3.2          1.3         0.2 setosa 
 4          4.6         3.1          1.5         0.2 setosa 
 5          5           3.6          1.4         0.2 setosa 
 6          5.4         3.9          1.7         0.4 setosa 

pivot_*()は履修済みですか?

以前の記事ではデータの縦横変形をする際に、Long→Wideはspread()、Wide→Longはgather()を使いましょうと書いてありますが、最近は新しい関数が追加され、それぞれpivot_wider()pivot_longer()が便利です。

この乗り換えは最初は抵抗があったのですが、ようやくpivot関数を自然に使えるようになってきました。例えば、こういう変形がサクサク書けます。


library(tidyverse)

iris_long <-
  iris %>% 
  pivot_longer(cols = c(starts_with("Sepal"), starts_with("Petal")),
               names_to = c("key", ".value"),
               names_sep = "\\.") 

iris_long
> iris_long
# A tibble: 300 x 4
   Species key   Length Width
   <fct>   <chr>  <dbl> <dbl>
 1 setosa  Sepal    5.1   3.5
 2 setosa  Petal    1.4   0.2
 3 setosa  Sepal    4.9   3  
 4 setosa  Petal    1.4   0.2
 5 setosa  Sepal    4.7   3.2
 6 setosa  Petal    1.3   0.2
 7 setosa  Sepal    4.6   3.1
 8 setosa  Petal    1.5   0.2
 9 setosa  Sepal    5     3.6
10 setosa  Petal    1.4   0.2
# … with 290 more rows

これをgather()spread()でやろうとして出来ないことはありませんが、

iris %>% 
  rowid_to_column() %>% 
  gather("key", "value", -c(Species, rowid))  %>% 
  separate(key, into = c("key", "LW")) %>% 
  spread(LW, value) %>% 
  select(- rowid)

なかなか辛いですね。

この記事ではこれ以上紹介しません。また改めてまとめる日が来るでしょう。

ggplot2の基本

ggplot2パッケージを使った作図では、データから規則的に層別化されたパネルを並べて1枚の図として出力することが簡単にできます。ggplot()への入力には基本的にLong型のデータを使い、各軸に対応するカラムを事前に整形することを心がけます。

ggplot(data = iris_long)+
  aes(x = Length, y = Width, color = Species)+
  geom_point(alpha = 0.75)+
  facet_wrap(key ~ Species)+
  theme(legend.position = "none")

iris_long.png

patchworkは履修済みですか?

第73回Tokyo.Rでatusyさんがggplot2で図を並べる〜facetごり押した私とpatchworkとの出会い〜というタイトルで紹介されて会場がドヨメイたことも記憶に新しいですね。

これを使って、例えば、こんな風にできます。


library(patchwork)

gg_iris <- function(dat){
    dat %>% 
      ggplot()+
      aes(Length, Width, color = Species)+
      geom_point(alpha = 0.75)+
      facet_wrap(~ Species)+
      theme(legend.position = "none")
  }

g_Sepal <-
  iris_long %>% 
  filter(key == "Sepal") %>% 
  gg_iris()+
  labs(title = "Sepal")

g_Petal <-
  iris_long %>% 
  filter(key == "Petal") %>% 
  gg_iris()+
  labs(title = "Petal")
g_Sepla

g_Sepal.png

g_Sepal / g_Petal

g_Sepal_Petal.png

あまり変わっていないように見えますが、Sepal, Petalごとに軸範囲がいい感じになっていて、層別化の水準もスッキリした図になりました。

ちなみに図の出力はwrap_plots()も使えます。

wrap_plots(g_Sepal, g_Petal, nrow = 2)

# もしくは
wrap_plots(list(g_Sepal, g_Petal), nrow = 2)

これは図の保存の時に便利ですね。

g <-
  wrap_plots(g_Sepal, g_Petal, nrow = 2)

ggsave("fig/fig_Sepal_Petal.png", g, width = 6, height = 4.5)

とりあえずnest

2つの全く違う図を作ってpatchworkでくっつけるなら上のような方法で自然なのですが、今回の場合は水準が違うだけで同じ関数で作図していますね。こういう時はfilter()関数でデータを分解するのではなく、nested dataを作ってmap()関数でアクセスしましょうというのが以前の記事で紹介した考え方でした。

やってみましょう。

iris_nest <-
  iris_long %>% 
  group_nest(key)
> iris_nest
# A tibble: 2 x 2
  key   data              
  <chr> <list>            
1 Petal <tibble [150 × 3]>
2 Sepal <tibble [150 × 3]>
> iris_nest$data[[1]]
# A tibble: 150 x 3
   Species Length Width
   <fct>    <dbl> <dbl>
 1 setosa     1.4   0.2
 2 setosa     1.4   0.2
 3 setosa     1.3   0.2
 4 setosa     1.5   0.2
 5 setosa     1.4   0.2
 6 setosa     1.7   0.4
 7 setosa     1.4   0.3
 8 setosa     1.5   0.2
 9 setosa     1.4   0.2
10 setosa     1.5   0.1
# … with 140 more rows

準備完了です。

nested dataの中で作図

通常のnested data加工と同様に、mutate()関数で新しいカラムを作り、そこにmap()関数で作図していきます。

iris_g <-
  iris_nest %>% 
  mutate(g = map(data, gg_iris))
> iris_g
# A tibble: 2 x 3
  key   data               g     
  <chr> <list>             <list>
1 Petal <tibble [150 × 3]> <gg>  
2 Sepal <tibble [150 × 3]> <gg>  

めっちゃ簡単ですね。

このように事前にgg_iris()関数を準備していなくても、チルダ~を使って無名関数を定義すれば同じ結果が得られます。

iris_g <-
  iris_nest %>% 
  mutate(g = map(data, 
                 ~ggplot(.)+
                   aes(Length, Width, color = Species)+
                   geom_point(alpha = 0.75)+
                   facet_wrap(~Species)+
                   theme(legend.position = "none")))

中身はどうなっているかというと、iris_g$g[[1]]iris_g$g[[2]]に、patchworokで作った時と同じ個別のグラフが入っています。

これらをwrap_plots()関数を使って1つの図にまとめます。

iris_g$g %>% 
  wrap_plots(nrow = 2)

g_iris_wp.png

おっとtitleを忘れていましたね。

iris_g_title <-
  iris_nest %>%
  mutate(g = map(data, gg_iris)) %>% 
  mutate(g = map2(g, key, 
                  ~ .x + labs(title = .y)))
iris_g_title$g %>% 
  wrap_plots(nrow = 2)

g_iris_wp_title.png

まとめ

データを層別化して畳み込んでおくことで、今、自分がどの水準で何をしようとしているのかを上手く把握できるようになります。データ加工から作図まで、風通しよく進めていきましょう。

16
16
0

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
16
16