まずは蝋の翼から。

学んだことを書きながら確認・整理するためのメモブログ。こういうことなのかな?といったことをふわっと書いたりしていますが、理解が浅いゆえに的はずれなことも多々あると思うのでツッコミ歓迎

themeを作ってggplotの出力コードをコピペで何回も繰り返さない

はじめに

ggplotを使っていて、結構何回も同じこと書いていることが多いなーと感じたので改めて省略できる部分を省略することを考える。

そもそもプログラマーのための原則として、DRY原則というものもある。

DRY原則 | プログラマが知るべき97のこと

また、Rではじめるデータサイエンスでは以下のように書いている。

Rではじめるデータサイエンス

Rではじめるデータサイエンス

データサイエンティストとして上達する最良の方法は関数を書くことです。よくある作業の自動化 という点で、関数は、コピー&ペーストよりも強力で汎用的です。
関数を書くことには、コピー&ペー ストよりも大きな利点が3つあります。

● コードが理解しやすい直感的な名前を関数に付けることができる。
● 要求が変わっても、複数箇所を変更せずに、1か所のコードを変更するだけで済む。
● コピー&ペーストするときの間違いをなくすことができる(すなわち、他のところではなく、1か 所の変数名だけ変更すればよい)。

Rではじめるデータサイエンス P237 「15章 関数 15.1 はじめに」より

ggplotで何を繰り返さないか

よく考えると、ある程度グラフタイプによって自分なりのスタイルがある。つまり、theme部分の設定は基本的に同じ内容をコピペで繰り返している。
また、x軸やfacetだけ変えた同じグラフを生成することもよくある。

つまり、例えばhistgramならば、histgram共通のthemeを作成→共通テーマを使ってhistgram_x1, histgram_x2,...それぞれのplotをする関数を作成。といったことをおこなえば良さそう。

サンプルコード

共通theme

histgram

theme_hist = function() {
    theme_minimal(base_size = 12, base_family = 'HiraKakuProN-W3') %+replace%
        theme(strip.text = element_text(size = 8, family = 'HiraKakuProN-W3'),
              plot.title = element_text(size = 15, 
                                        family = 'HiraKakuProN-W6'),
              axis.title = element_text(family = 'HiraKakuProN-W3'),
              
              panel.grid.minor = element_blank())
}

bar

theme_bar = function() {
    theme_minimal(base_size = 12, base_family = 'HiraKakuProN-W3') %+replace%
        theme(strip.text = element_text(size = 10, family = 'HiraKakuProN-W3'),
              plot.title = element_text(size = 15, 
                                        family = 'HiraKakuProN-W6'),
              axis.title = element_text(family = 'HiraKakuProN-W3'),
              axis.text.y = element_text(size = 15),
              axis.text.x = element_blank(),
              
              panel.grid.minor = element_blank(),
              panel.grid = element_blank())
}

heatmap

theme_heatmap = function() {
    theme_minimal(base_size = 12, base_family = 'HiraKakuProN-W3') %+replace%
        theme(
            strip.text = element_text(size = 10, family = 'HiraKakuProN-W3'),
            plot.title = element_text(size = 15, 
                                      family = 'HiraKakuProN-W6'),
            axis.title = element_text(family = 'HiraKakuProN-W3'),
            axis.text.x = element_text(),
            panel.grid.minor = element_blank(),
            panel.grid = element_blank())
}

scatter

theme_scatter = function() {
    theme_minimal(base_size = 12, base_family = 'HiraKakuProN-W3') %+replace%
        theme(
            strip.text = element_text(size = 10, family = 'HiraKakuProN-W3'),
            plot.title = element_text(size = 15, family = 'HiraKakuProN-W6'),
            axis.title = element_text(family = 'HiraKakuProN-W3'),
            panel.grid.minor = element_blank()
        )
}

themeを使ったplot

上記themeをそれぞれ使用して、各グラフを書く。
このとき、変数やラベルなどできる限り変数化することで、以降で使い回すことができる。

histgram

plot_hist_facet = function(df, var, label, fill_var) {
    var = enquo(var)
    fill_var = enquo(fill_var)
    
    g = df %>% 
        ggplot(aes(!!var, fill = !!fill_var)) +
        geom_density(alpha = 0.5) +
        facet_wrap( ~ piyo) +
        labs(x = label, y = 'Density', 
             title = glue('{label}の分布 \n  ')) +
        theme_hist()  
    
    ggsave(filename = glue('figure/histogram/{label}_facet.png'),
           plot = g,
           width = 15,
           height = 10,
           dpi = 'retina')
}

# 出力
plot_hist_facet(df, var1, 'hoge', fill_var)
plot_hist_facet(df, var2, 'fuga', fill_var)

bar

plot_bar = function(df, var, label, cols) {
    var = enquo(var)
    
    g = df %>% 
        group_by(hoge, !!var) %>%
        summarise(
            N = n(),
            prop = mean(fuga),
            se = sd(fuga) / sqrt(N)
        ) %>% 
        drop_na() %>% 
        ggplot(aes(!!var, prop, fill = fct_reorder(!!var, desc(!!var)), label = N)) +
        geom_col() +
        geom_text(aes(y = 0.01), color='white') + 
        #geom_errorbar(aes(ymax = prop + se, ymin = prop - se), width = 0.4, color = 'gray40') +
        facet_wrap(~fuga, ncol=5, labeller = 'label_both') + 
        labs(x = glue('{label}'), y = '割合', 
             title = glue('{label}と割合 \n ')) + 
        scale_fill_manual(name = '', values = cols) + 
        scale_y_continuous(limits = c(0, 0.5)) + 
        theme_bar() +
        theme(legend.position = 'none')
    
    ggsave(file = glue('figure/bar/{label}piyo.png'),
           plot = g,
           width = 10,
           height = 7,
           dpi = 'retina')
}

# 出力
plot_high_low(df, var1, 'hoge',  c('gray30', 'gray70'))
plot_high_low(df, var2, 'fuga',  c('gray30', 'gray70'))

参考

ベースとなるthemeはここに色々と載っている。

ggplot2のテーマはどれを使うべきか - Qiita

枠線がいる場合はtheme_minimal、いらない場合はtheme_voidかなぁ。

theme置き場(追記:順次更新)

画像保存

save_plot = function(fname, width = 8, height = 4) {
  ggsave(fname, width = width, height = height, dpi = "retina")
}

theme共通

# theme -------------------------------------------------------------------

# ggplot2のテーマ

# 案件に合わせて色は変える
cols = c(rgb(0, 113, 188, maxColorValue = 255),
         rgb(255, 80, 80, maxColorValue = 255),
         rgb(55, 55, 55, maxColorValue = 255))

# cols = c("#5D9CEC", "#4A89DC", 
#          "#48CFAD", "#37BC9B",
#          "#FC6E51", "#E9573F",
#          "#AAB2BD", "#656D78", "#434A54")

base_family =  "Noto Sans JP Regular"
bold_family =  "Noto Sans JP Bold"

折れ線

theme_line = function() {
  theme_minimal(base_size = 12, base_family = base_family) %+replace% 
    theme(panel.grid.minor = element_blank(),
          panel.grid.major.x = element_blank(),
          axis.title = element_text(size = 15, color = cols[3]),
          axis.title.x = element_text(margin = margin(10, 0, 0, 0), hjust = 1),
          axis.title.y = element_text(margin = margin(0, 10, 0, 0), angle = 90, hjust = 1),
          axis.text = element_text(size = 12, color = cols[3]),
          axis.line.x.bottom = element_line(color = cols[3], size = 0.5),
          axis.ticks.x = element_line(color = cols[3], size = 0.5),
          axis.ticks.length.x = unit(5, units = "pt"),
          strip.text = element_text(size = 15, color = cols[3], margin = margin(5, 5, 5, 5)),
          plot.title = element_text(family = bold_family, size = 15, color = cols[3], margin = margin(0, 0, 18, 0)),
          plot.subtitle = element_text(size = 15, hjust = -0.05, color = cols[3],
                                       margin = margin(5, 5, 5, 5)),
          legend.position = "none"
    )
}

棒グラフ

theme_bar = function() {
  theme_minimal(base_size = 12, base_family = base_family) %+replace%
    theme(panel.grid.minor = element_blank(),
          panel.grid.major = element_blank(),
          panel.grid.major.y = element_line(color = "gray", size = 0.1),
          axis.title = element_text(size = 15, color = cols[8]),
          axis.title.x = element_text(margin = margin(10, 0, 0, 0), hjust = 1),
          axis.title.y = element_text(margin = margin(0, 10, 0, 0), angle = 90, hjust = 1),
          axis.text = element_text(size = 12, color = cols[8]),
          strip.text = element_text(size = 15, color = cols[8], margin = margin(5, 5, 5, 5)),
          plot.title = element_text(size = 15, color = cols[8], margin = margin(5, 0, 5, 0)),
          plot.subtitle = element_text(size = 15, hjust = -0.05, color = cols[8],
                                       margin = margin(5, 5, 5, 5)),
          legend.position = "none",
          legend.text = element_text(color = cols[8])
    )
}

散布図

theme_scatter = function() {
  theme_minimal(base_size = 12, base_family = base_family) %+replace%
    theme(panel.grid.minor = element_blank(),
          panel.grid.major = element_line(color = "gray", size = 0.1),
          axis.title = element_text(size = 15, color = cols[8]),
          axis.title.x = element_text(margin = margin(10, 0, 0, 0), hjust = 1),
          axis.title.y = element_text(margin = margin(0, 10, 0, 0), angle = 90, hjust = 1),
          axis.text = element_text(size = 12, color = cols[8]),
          # axis.line.x.bottom = element_line(color = cols[8], size = 0.5),
          # axis.ticks.x = element_line(color = cols[8], size = 0.5),
          # axis.ticks.length.x = unit(5, units = "pt"),
          strip.text = element_text(size = 15, color = cols[8], margin = margin(5, 5, 5, 5)),
          plot.title = element_text(size = 15, color = cols[8], margin = margin(5, 0, 5, 0)),
          plot.subtitle = element_text(size = 15, hjust = -0.05, color = cols[8],
                                       margin = margin(5, 5, 5, 5)),
          legend.position = "none",
          legend.text = element_text(color = cols[8]))
}