themeを作ってggplotの出力コードをコピペで何回も繰り返さない
はじめに
ggplotを使っていて、結構何回も同じこと書いていることが多いなーと感じたので改めて省略できる部分を省略することを考える。
そもそもプログラマーのための原則として、DRY原則というものもある。
また、Rではじめるデータサイエンスでは以下のように書いている。
- 作者:Hadley Wickham,Garrett Grolemund
- 発売日: 2017/10/25
- メディア: 単行本(ソフトカバー)
データサイエンティストとして上達する最良の方法は関数を書くことです。よくある作業の自動化 という点で、関数は、コピー&ペーストよりも強力で汎用的です。
関数を書くことには、コピー&ペー ストよりも大きな利点が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はここに色々と載っている。
枠線がいる場合は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])) }