StatsBeginner: 初学者の統計学習ノート

統計学およびR、Pythonでのプログラミングの勉強の過程をメモっていくノート。たまにMacの話題。

ggplot2で2軸グラフを描く時の軸スケーリングの作業

ggplot2で2軸のグラフを描くときは、先日のエントリでも書いたように、ggplot2自身は左軸(第1軸)と右軸(第2軸)を別々の情報として持つことはできないので、左軸と右軸の尺度の違いを自分で設定して変換しなければならない。
あとで使いまわすので、このスケーリングの作業を行うスケーラを以下のように定義しておいた。
最初は1行で書いてggplotの描画の中に埋め込んでたけどあとで混乱しないように分けて書いておいた。

library(ggplot2)
data(airquality)  # 練習用データのよみこみ

### 変数を追加
# x軸用に月と日の列を日付の変数にしておく(2行に分けてかいてる)
airquality <- airquality %>%
  mutate(Date = paste(as.character(Month), '/', as.character(Day), sep='')) %>%
  mutate(Date = as.Date(Date, format='%m/%d'))

### y1, y2の目盛り範囲を決めておく
# ここでは恣意的に決めてるが、最大と最小を取るとかでもいいと思う
y1.lim <- c(0, 25)
y2.lim <- c(50, 100)

### スケーラの関数を書いておく
# 上でつくった、y1とy2の目盛りの範囲を定めた要素数2のベクトルを使って、
# いい感じにスケールを合わせる。

# 変数のスケーラ。
# pにy2の値ベクトルを与えると、y1の尺に合わせた数字に変換。
# y2をまずゼロ基準に戻し、y2とy1のlimの幅の比でスケーリング
# した後で、y1のゼロ基準からの乖離分を足す。

variable_scaler <- function(p, lim1, lim2){
  to_zero <- p-lim2[1]
  y1_range <- lim1[2]-lim1[1]
  y2_range <- lim2[2]-lim2[1]
  scaled <- to_zero*y1_range/y2_range
  from_zero <- scaled + lim1[1]
  return(from_zero)
}

# 第2軸の目盛りのスケーラ。
# pは、sec_axis()の'.'になる。y1の目盛りをy2の目盛りに読み替えるもの。
# y1の目盛りをまずゼロ基準に戻し、y1とy2のlimの幅の比スケーリング
# した後で、y2の目盛りのゼロ基準からの乖離分を足す。

axis_scaler <- function(p, lim1, lim2){
  to_zero <- p-lim1[1]
  y1_range <- lim1[2]-lim1[1]
  y2_range <- lim2[2]-lim2[1]
  scaled <- to_zero*y2_range/y1_range
  from_zero <- scaled + lim2[1]
  return(from_zero)
}

### 描画してみる
airquality %>%
  ggplot(aes(x=Date)) +
  geom_line(aes(y=Wind, colour='Wind')) + 
  geom_line(aes(y=variable_scaler(Temp, y1.lim, y2.lim), colour='Temp')) + 
  scale_y_continuous(limit=y1.lim,  # 第1軸の範囲
                     breaks=c(0, 5, 10, 15, 20, 25),  # 第1軸の目盛り
                     sec.axis=sec_axis(
                       ~(axis_scaler(., y1.lim, y2.lim)), # 軸スケーリング
                       breaks=c(50, 60, 70, 80, 90,100), # 第2軸の目盛り
                       name='Temp')  # y2のラベルはここで設定する
                     ) + 
  labs(title = 'DUAL AXIS CHART', 
       x='Date',
       y='Wind',
       colour = 'Variable')


もちろんy2のラベルもscale_y_continuousの中でnameをつかって指定してもよい。
2つ目の折れ線のyを指定するときに変数スケーラを使い、sec.axisを指定するときにformulaの中で軸スケーラを使う。


f:id:midnightseminar:20200908181313p:plain