読者です 読者をやめる 読者になる 読者になる

ggplot2で軸と色を固定するオプション

R animation ggplot2

(注:ここに載せているサンプルは、手元で動かした感じそこそこ重いです。実際に動かす際はrnorm()nをもっと減らすことをお勧めします...)

animationパッケージ楽しいですね。今さらknitrと同じ人が作者だと知りました。

楽しいんですが、たまにつまづきます。ggplotとかplotとかは、軸とか色とかをデータに合わせていい感じに調整してくれるわけですが、それに頼ってanimationすると、、

library(ggplot2)
library(animation)
library(dplyr)

set.seed(100)
data <- data.frame(value = rnorm(1000, mean = 5, sd = sqrt(5)))

saveGIF({
  for (i in 1:nrow(data)) {
    p <- ggplot(slice(data, 1:i), aes(x = value)) + geom_bar()
    print(p)
  }
}, movie.name = "animation_bad.gif", interval=c(rep(0.02, nrow(data) - 1), 5) )

f:id:yutannihilation:20141108145425g:plain

うおお。。

こんなにめまぐるしく変わる時代画像にオジサンはついていけません。上司に見せられません。やばいです。
これをなんとかしたい、ということで役に立ちそうなggplot2のオプションについて軽くメモ。

連続値の軸

scale_*_continuous()/scale_*_log10()/scale_*_reverse()/scale_*_sqrt()には、limitsオプションが指定できます。 あと、bin="stat"のときは集計する幅をbinwidthで指定できます。(ここでで指定しているbinwidth = rangewidth/30というのはデフォルトと同じなので、別に指定しなくても結果は一緒)

limits
A numeric vector of length two describing the scale limits.
(http://docs.ggplot2.org/current/continuous_scale.html)

xlimits <- range(data)
xrangewidth <- diff(limits)
ylimits <- c(0,100)

saveGIF({
  for (i in 1:nrow(data)) {
    p <- ggplot(slice(data, 1:i), aes(x = value)) + geom_bar(binwidth = xrangewidth/30) +
      scale_x_continuous(limits = xlimits) +
      scale_y_continuous(limits = ylimits)
    
    print(p)
  }
}, movie.name = "animation1.gif", interval=c(rep(0.02, nrow(data) - 1), 5) )

f:id:yutannihilation:20141108145732g:plain

または、ショートカットとしてylim()/xlim()も使えます。

saveGIF({
  for (i in 1:nrow(data)) {
    p <- ggplot(slice(data, 1:i), aes(x = value)) + geom_bar(binwidth = xrangewidth/30) +
      xlim(xlimits) +
      ylim(ylimits)

    print(p)
  }
}, interval=c(rep(0.02, nrow(data) - 1), 5) )

離散値の軸

scale_*_discrete() にはdropオプションがあります。これをFALSEにしておくと、factorのレベルをすべて表示してくれます。(TRUEだと値がひとつもないレベルは省略されます)

drop
drop unused factor levels from the scale (TRUE or FALSE)
(http://docs.ggplot2.org/0.9.3.1/discrete_scale.html)

breaks <- floor(limits[1]):ceiling(limits[2])
labels <- sapply(1:(length(breaks)-1), function(i) paste(breaks[i], "~", breaks[i+1]))

data %>% mutate(valuerange = cut(value, breaks = breaks, labels = labels)) -> data2

ylimits <- c(0, max(table(data2$valuerange)))

saveGIF({
  for (i in 1:nrow(data)) {
    p <- ggplot(slice(data2, 1:i), aes(x = valuerange)) + geom_bar() +
      scale_x_discrete(drop = FALSE) +
      scale_y_continuous(limits = ylimits)
    
    print(p)
  }
}, movie.name = "animation2.gif", interval=c(rep(0.02, nrow(data) - 1), 5) )

f:id:yutannihilation:20141108145556g:plain

limitsを指定して、順番を入れ替えたり、任意のレベルだけ表示することもできます。

saveGIF({
  for (i in 1:nrow(data)) {
    p <- ggplot(slice(data2, 1:i), aes(x = valuerange)) + geom_bar() +
      scale_x_discrete(drop = FALSE, limits = rev(labels)) +
      scale_y_continuous(limits = ylimits)
    
    print(p)
  }
}, interval=c(rep(0.02, nrow(data) - 1), 5) )

色も固定できます。fillcolourに与える変数が連続値か離散値かによって違いますが、やることは上と同じです。
連続値であれば、scale_colour_continuous()scale_fill_continuous()limitsを設定します。 離散値であれば、scale_colour_discrete()とかscale_fill_discrete()drop = FALSEを付けます。

scale_*_brewer()とかで指定する方が正しい気もしますが、よく分かりませんでした)

saveGIF({
  for (i in 1:nrow(data)) {
    p <- ggplot(slice(data2, 1:i), aes(x = valuerange, fill = valuerange)) + geom_bar() +
      scale_x_discrete(drop = FALSE) +
      scale_y_continuous(limits = ylimits) +
      scale_fill_discrete(drop = FALSE)
    
    print(p)
  }
}, movie.name = "animation3.gif", interval=c(rep(0.02, nrow(data) - 1), 5) )

f:id:yutannihilation:20141108191743g:plain