LoginSignup
2
0

More than 1 year has passed since last update.

ggplot2で多軸プロットを書きたくない

Last updated at Posted at 2022-12-25

初めに

Excelではy軸が左右にある2軸プロットを簡単に作れるが、ggplot2では簡単には作れない。
現在の業務では2軸グラフだけではなく、3~4軸の多軸なグラフが要求されることがある。
これまでは黒魔術と化したExcelテンプレートでなんとかしてきているが、黒魔術な上、100以上のデータセットに適応しなければならない未来が見えているため、どうにか冬休み中には解法を考えたい。

Baseのプロットを使ったり力技で書く方法もあるが、ggplot2の簡便できれいな図に魂を売った身としてはggplot2で簡単になんとかしたい。

ここに心拍数[bpm]と歩数[steps/min]と速度[m/min]がそろっている1分毎の経時データがある。これをモデルとしてつかっていく。速度(Speed)を折れ線グラフ、心拍数(HeartRate)を、歩数(Steps)を棒グラフで示す。

DateTime Speed HeartRate Steps
2022-12-01 12:00:00 93.41503 85.43951 107
2022-12-01 12:01:00 87.36342 91.94734 110
2022-12-01 12:02:00 94.70084 88.93372 97
2022-12-01 12:03:00 84.55080 94.74760 97
2022-12-01 12:04:00 84.42936 83.16780 99
## データセット
df <- tibble(
  DateTime = seq(lubridate::ymd_hms("2022-12-01 12:00:00", tz="Asia/Tokyo"), by= 60, length.out=120),
  Speed = rnorm(120, mean = 90, sd = 5),
  HeartRate = c(rnorm(40, mean = 90, sd = 5),rnorm(40, mean = 110, sd = 10),rnorm(40, mean = 130, sd = 15)),
  Steps = floor(c(rnorm(40, mean = 100, sd = 5),rnorm(40, mean = 110, sd = 10),rnorm(40, mean = 120, sd = 15))),
)

グラフを並べる方法について

ggplot2のグラフを並べる方法は、こちらの記事にあるようにいろいろ方法はあるが、x軸を共通にしたいという要望が強いため(できないことはないが)少し目的と外れる。

今回の解法:Facet_wrap

多軸プロットにしたいということは、共通のx軸を持ったグラフを縦方向で比較したいということである。
Facet_wrapを使って、縦一列に並べてあげればいい。

df %>%
  pivot_longer(cols = c(Speed, HeartRate, Steps)) %>%
  ggplot(aes(x = DateTime, y = value)) +
  geom_point(data = ~ filter(.x, name == "HeartRate")) +
  geom_line(data = ~ filter(.x, name == "Speed")) +
  geom_bar(data = ~ filter(.x, name == "Steps"), stat = "identity") +
  facet_wrap(~ name, ncol = 1, scale = "free_y")

image.png

facet_wrap(~ name, ncol = 1, scale = "free_y")ではncol=1で縦一列を指定し、scale = "free_y"でy軸の範囲を適切にしている。ここでは、geom_***(data = ~ filter(.x, name == "***")) としてあげることで、縦持ちのデータから必要なものだけをを個別にプロットすることができる。

微調整

並び順

グラフの並び順は2通りあるが、1つめの前処理がおすすめである。

  1. 前処理で対応する方法
    縦持ちに変換した後の属性列(name)をFactor化する。この時のlevelsを縦に並べたい順にしておく。
    df %>%
      pivot_longer(cols = c(Speed, HeartRate, Steps)) %>%
      mutate(across(name, factor, levels=c("Speed", "HeartRate", "Steps"))) %>% 
      ggplot(aes(x = DateTime, y = value)) +
      geom_point(data = ~ filter(.x, name == "HeartRate")) +
      geom_line(data = ~ filter(.x, name == "Speed")) +
      geom_bar(data = ~ filter(.x, name == "Steps"), stat = "identity") +
      facet_wrap(~name, ncol = 1, scale = "free_y")
    
  2. facet_wrapの段階でコントロールする方法(非推奨)
    原理は一緒であるが、Factor化する場所の違いである。
    非推奨.R
    df %>%
      pivot_longer(cols = c(Speed, HeartRate, Steps)) %>%
      ggplot(aes(x = DateTime, y = value)) +
      geom_point(data = ~ filter(.x, name == "HeartRate")) +
      geom_line(data = ~ filter(.x, name == "Speed")) +
      geom_bar(data = ~ filter(.x, name == "Steps"), stat = "identity") +
      facet_wrap(~factor(name, levels=c("Speed", "HeartRate", "Steps"))
    , ncol = 1, scale = "free_y")
    

どちらも結果は一緒である。
image.png

ラベルのタイトルと位置

ここまでやれば、最低限はできていますがラベルのタイトルに単位を入れたり、位置を変えた方が見やすいかもしれません。この時facet_wrapでコントロールすると、labeller内での表記が難しくなります。

df %>%
  pivot_longer(cols = c(Speed, HeartRate, Steps)) %>%
  mutate(across(name, factor, levels = c("Speed", "HeartRate", "Steps"))) %>%
  ggplot(aes(x = DateTime, y = value)) +
  geom_point(data = ~ filter(.x, name == "HeartRate")) +
  geom_line(data = ~ filter(.x, name == "Speed")) +
  geom_bar(data = ~ filter(.x, name == "Steps"), stat = "identity") +
  facet_wrap(~ name,
             ncol = 1,
             scale = "free_y",
             strip.position = "right",
             labeller = labeller(
               name =
                 c(
                   "HeartRate" = "HeartRate [BPM]",
                   "Speed" = "Speed [m/min]",
                   "Steps" = "Steps [steps/min]"
                 )
             ))

image.png

後は、Y軸の調整がありますが、12/25までに投稿にはここまでが限界でした。 ggh4x::facetted_pos_scalesがうまく動かない。

2
0
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
2
0