はじめに
gganimate
パッケージについて理解したいシリーズです。
この記事では、transition_reveal関数の機能を確認します。
【他の内容】
【目次】
transition_reveal
gganimate
パッケージの関数transition_reveal()
について解説します。transition_reveal()
は、アニメーション(gif画像)のフレーム切り替えを制御します。
利用するパッケージを読み込みます。
# 利用パッケージ library(ggplot2) library(gganimate)
基本的に、パッケージ名::関数名()
の記法で書きます。ただし作図コードに関してはごちゃごちゃしてしまうので、ggplot2
パッケージの関数は関数名のみで、gganimate
パッケージの関数はパッケージ名を明示して書きます。
データフレームの作成
まずは、作図に利用するデータフレームを作成します。
# データフレームを作成 df <- tibble::tibble( x = 0:5, y = 0:5, along = 0:5 ) df
## # A tibble: 6 x 3 ## x y along ## <int> <int> <int> ## 1 0 0 0 ## 2 1 1 1 ## 3 2 2 2 ## 4 3 3 3 ## 5 4 4 4 ## 6 5 5 5
分かりやすい値を作成しておきます。tibble::tibble()
を使ってデータフレームを作成していますが、data.frame()
を使っても問題ありません。
transition_reveal()
のalong
引数(第1引数)に、フレームの順序を示す列を指定します。この例では、along
列が対応します。
このデータを用いた散布図と折れ線グラフのアニメーションを通じて、transition_reveal()
の機能を確認していきます。
グラフとの関係
グラフの種類(geom_***()
)によって、transition_reveal()
の挙動が変わります。
・散布図の場合
# 散布図を作成 anim <- ggplot(df, aes(x = x, y = y)) + geom_point(size = 10, color = "hotpink") + # 棒グラフ gganimate::transition_reveal(along = along) + # フレーム scale_x_continuous(breaks = df[["x"]], minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal() + geom_point()"), subtitle = paste0("frame : {frame_along}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 50, fps = 10)
フレーム列(along
列)の値に従い、フレームごとに描画されるデータ(行)が切り替わります。よって、点が移動するように見えます。
また、データ間が補完されます。詳しくは「along引数」節で確認します。
・棒グラフの場合
# 棒グラフを作成 anim <- ggplot(df, aes(x = 1, y = y)) + geom_bar(stat = "identity", fill = "hotpink", color = "hotpink") + # 棒グラフ gganimate::transition_reveal(along = along) + # フレーム scale_x_continuous(breaks = df[["x"]], minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal() + geom_bar()"), subtitle = paste0("frame : {frame_along}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 50, fps = 10)
棒グラフの場合も同様です。y
列の値に応じてバーが伸び縮みするように描画されます。
・折れ線グラフの場合
# 折れ線グラフを作成 anim <- ggplot(df, aes(x = x, y = y)) + geom_line(size = 1, color = "hotpink") + # 棒グラフ gganimate::transition_reveal(along = along) + # フレーム scale_x_continuous(breaks = df[["x"]], minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal() + geom_line()"), subtitle = paste0("frame : {frame_along}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 50, fps = 10)
折れ線グラフgeom_line()
やgeom_path()
の場合は、過去のフレームのデータ(行)も描画されます。よって、データの履歴や軌跡のように見えます。
・散布図と折れ線グラフの場合
# 折れ線グラフを作成 anim <- ggplot(df, aes(x = x, y = y)) + geom_point(size = 10, color = "hotpink") + # 棒グラフ geom_path(size = 1, color = "hotpink") + # 棒グラフ gganimate::transition_reveal(along = along) + # フレーム scale_x_continuous(breaks = df[["x"]], minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal() + geom_point() + geom_path()"), subtitle = paste0("frame : {frame_along}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 50, fps = 10)
散布図に関しては現フレームのデータのみが描画され、折れ線グラフに関しては過去フレームのデータも描画されます。
ラベル変数
transition_reveal()
で使えるラベル変数frame_along
を確認します。
ラベル変数名を{}
で挟んだ文字列をlabs()
などに指定します。
# ラベル変数を指定 lv <- "frame_along" # 現フレームの値 # ラベル変数の設定 anim <- ggplot(df, aes(x = x, y = y)) + geom_point(size = 10, color = "hotpink") + # 散布図 geom_path(color = "hotpink") + # 折れ線 gganimate::transition_reveal(along = along) + # フレーム scale_x_continuous(breaks = df[["x"]], minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal() + labs(", lv, ")"), subtitle = paste0("frame : {", lv, "}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 50, fps = 10)
"{frame_along}"
は、現在のフレームに対応する値に置き換わります。表示される値については「along引数」節で掘り下げます。
解説用に複雑な作図コードになっていますが、title = "{frame_along}"
のように指定してください。
また、"{round(frame_along, 1)}"
のように書くことで、ラベル変数の値に関数を使えます。
引数
続いて、transition_reveal()
で利用できる3つの引数(along
・range
・keep_last
)を確認します。
range
range
引数は、描画するフレームを2つの値を持つベクトルで指定します。
# 値を指定:(デフォルト:NULL) r <- NULL r <- c(2L, 3L) # range(描画範囲)の設定 anim <- ggplot(df, aes(x = x, y = y)) + geom_point(size = 10, color = "hotpink") + # 散布図 geom_path(color = "hotpink") + # 折れ線 gganimate::transition_reveal(along = along, range = r) + # フレーム scale_x_continuous(breaks = df[["x"]], minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal(range = c(", paste0(r, collapse = ", "), "))"), subtitle = paste0("frame : {frame_along}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 10, fps = 10)
デフォルトはNULL
で、全てのデータを描画します。(NULL
の図は、サブタイトルに表示する文字列とフレーム数の設定を変更しています。)
c(始まりの値, 終わりの値)
の形で指定することで、描画するデータ(行)を指定できます。この例では、整数型(integer
)なので2L, 3L
のように指定しています。数値型(numeric
)であれば2, 3
のように指定します。
keep_last
keep_last
引数は、最後のフレームにおいて折れ線グラフ以外のグラフを描画するかを論理値で指定します。
# 値を指定:(デフォルト:TRUE) b <- TRUE #b <- FALSE # keep_last(最後の描画)の設定 anim <- ggplot(head(df, 3), aes(x = x, y = y)) + geom_point(size = 10, color = "hotpink") + # 散布図 geom_path(color = "hotpink") + # 折れ線 gganimate::transition_reveal(along = along, keep_last = b) + # フレーム scale_x_continuous(breaks = df[["x"]], minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal(keep_last = ", b, ")"), subtitle = paste0("frame : {frame_along}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 3, fps = 1)
デフォルトはTRUE
で、最後のフレームでもそれまでのフレームと同様に描画します。
FALSE
を指定すると、最後のフレームでは描画しません。
along
along
引数(第1引数)に指定する値の影響を確認します。
等間隔でない値をフレーム列とします。
# 等間隔でないフレーム列を作成 df <- tibble::tibble( x = c(1, 2, 10), y = c(1, 2, 10), along = c(1, 2, 10) ) # 折れ線グラフを作成 anim <- ggplot(df, aes(x = x, y = y)) + geom_point(size = 10, color = "hotpink") + # 散布図 geom_path(color = "hotpink") + # 折れ線 gganimate::transition_reveal(along = along) + # フレーム scale_x_continuous(breaks = df[["x"]], minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal()"), subtitle = paste0("frame : {frame_along}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 10, fps = 1)
データの大小関係に応じて補完されます。そのため(この例だと?)、データ間のフレーム数が変わり、データ全体で等間隔になります。(transition_states()
の場合は、データ間のフレーム数は一定で、データ間ごとに等間隔に補完されます。)
フレーム数(nframes
引数の値)を増やしてみます。
# gif画像を作成 gganimate::animate(plot = anim, nframes = 100, fps = 10)
ラベル変数の値が、各フレームに対応した値になっているのが分かります。(transition_states()
の場合は、フレーム列の値がそのまま表示されます。)
表示されている値は次のように再現できます。
# 確認 seq(from = 1, to = 10, length.out = 100)[1:15]
## [1] 1.000000 1.090909 1.181818 1.272727 1.363636 1.454545 1.545455 1.636364 ## [9] 1.727273 1.818182 1.909091 2.000000 2.090909 2.181818 2.272727
これまでの例で整数が表示されていた理由は謎。
棒グラフの場合も同様です。
# 棒グラフを作成 anim <- ggplot(df, aes(x = 1, y = y)) + geom_bar(stat = "identity", fill = "hotpink", color = "hotpink") + # 棒グラフ gganimate::transition_reveal(along = along) + # フレーム scale_x_continuous(breaks = 1, minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal()"), subtitle = paste0("frame : {frame_along}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 101, fps = 10)
割り切れる値のフレーム数を決め打ちで指定できます。または、{round(frame_along, 2)}
で丸め込めます。
重複しない不規則な値をフレーム列とします。
# 不規則なフレーム列を作成 df <- tibble::tibble( x = 1:5, y = 1:5, along = c(1, 3, 2, 5, 4) ) # 折れ線グラフを作成 anim <- ggplot(df, aes(x = x, y = y)) + geom_point(size = 10, color = "hotpink") + # 散布図 geom_path(color = "hotpink") + # 折れ線 gganimate::transition_reveal(along = along) + # フレーム scale_x_continuous(breaks = df[["x"]], minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal()"), subtitle = paste0("frame : {frame_along}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 40, fps = 10)
謎。上の行から読み込んで、データ(点)が合流すると消える?
along
列を昇順に並び替えてみます。
# 不規則なフレーム列を作成 df <- dplyr::arrange(df, along) df
## # A tibble: 5 x 3 ## x y along ## <int> <int> <dbl> ## 1 1 1 1 ## 2 3 3 2 ## 3 2 2 3 ## 4 5 5 4 ## 5 4 4 5
意図通りの変化になりました。along
列が昇順になるようにしておくのが無難かもしれません。
重複する値をフレーム列とします。
# 重複するフレーム列を作成 df <- tibble::tibble( x = 1:6, y = 1:6, along = c(1, 1, 2, 3, 4, 3) ) # 折れ線グラフを作成 anim <- ggplot(df, aes(x = x, y = y)) + geom_point(size = 10, color = "hotpink") + # 散布図 #geom_path(color = "hotpink") + # 折れ線 gganimate::transition_reveal(along = along) + # フレーム scale_x_continuous(breaks = df[["x"]], minor_breaks = FALSE) + # x軸目盛 scale_y_continuous(breaks = df[["y"]], minor_breaks = FALSE) + # y軸目盛 coord_fixed(ratio = 1) + # アスペクト比 labs(title = paste0("transition_reveal()"), subtitle = paste0("frame : {frame_along}")) # gif画像を作成 gganimate::animate(plot = anim, nframes = 50, fps = 10)
謎。フレーム列が同じ値のデータは同時(同じフレーム)に描画されるが、隣り合ってると即合流したものとみなされ?消える?
(よく分からないこともありましたが)以上で、ラベル変数と引数の機能を確認しました。次は、実用を想定してグラフを作成します。
利用例:確率分布
gganimate
パッケージの使用例として、正規分布(ガウス分布)の標準偏差(パラメータ)とグラフの形状の関係をアニメーションで可視化します。
利用するパッケージを読み込みます。
# 利用パッケージ library(ggplot2) library(gganimate)
データの作成
まずは、描画する正規分布のデータを作成します。
# x軸の値を作成 x_vals <- seq(from = -5, to = 5, by = 0.1) # 標準偏差として利用する値を作成 sd_vec <- seq(from = 0.5, to = 3, by = 0.2) # 標準偏差ごとにガウス分布の計算 dens_df <- tibble::tibble() for(sd in sd_vec) { # ガウス分布の確率密度を計算 tmp_df <- tibble::tibble( x = x_vals, sigma = sd, density = dnorm(x = x_vals, mean = 0, sd = sd) ) # 計算結果を結合 dens_df <- rbind(dens_df, tmp_df) } head(dens_df)
## # A tibble: 6 x 3 ## x sigma density ## <dbl> <dbl> <dbl> ## 1 -5 0.5 1.54e-22 ## 2 -4.9 0.5 1.11e-21 ## 3 -4.8 0.5 7.76e-21 ## 4 -4.7 0.5 5.19e-20 ## 5 -4.6 0.5 3.33e-19 ## 6 -4.5 0.5 2.06e-18
グラフとして描画するx軸の値(確率変数がとり得る値)を作成してx_vals
とします。
標準偏差として利用する値を作成してsd_vec
とします。
for()
を使って、sd_vec
の要素ごとに繰り返し正規分布に従う確率密度を計算します。計算結果をdens_df
に追加していきます。
正規分布の確率密度は、dnorm()
で計算できます。確率変数の引数x
にx_vals
、平均の引数mean
にこの例では0
、標準偏差の引数にsd
を指定します。
作図
アニメーションを作成する前に、全てのグラフを重ねて描画して確認します。
# ガウス分布を作図 ggplot(dens_df, aes(x = x, y = density, color = as.factor(sigma))) + geom_line() + # 折れ線グラフ labs(title = "Gaussian Distribution", color = "sigma")
sigma
列をas.factor()
で因子型に変換すると、標準偏差ごとに色付けされます。
パラメータごとにフレームを切り替えて描画します。
# ガウス分布のアニメーションを作成:折れ線グラフ anim <- ggplot(dens_df, aes(x = x, y = density, color = as.factor(sigma))) + geom_line(show.legend = FALSE) + # 折れ線グラフ gganimate::transition_reveal(along = sigma) + # フレーム labs(title = "transition_reveal() + geom_line()", subtitle = "sigma = {frame_along}") # gif画像を作成 gganimate::animate(plot = anim, nframes = length(sd_vec), fps = 10)
折れ線グラフの場合は、過去フレームのデータが残るので、描画される分布が増えていきます。
散布図でも同様に描画します。
# ガウス分布のアニメーションを作成:散布図 anim <- ggplot(dens_df, aes(x = x, y = density, color = as.factor(sigma), group = x)) + geom_point(show.legend = FALSE) + # 散布図 gganimate::transition_reveal(along = sigma) + # フレーム labs(title = "transition_reveal() + geom_point()", subtitle = "sigma = {frame_along}") # gif画像を作成 gganimate::animate(plot = anim, nframes = length(sd_vec), fps = 10)
group
引数にx
列を指定すると、標準偏差の値によって各点が変化する様子が分かります。
ただし、パラメータ数よりもフレーム数を多く指定すると、データ間を自動で補完されます。
・コード(クリックで展開)
フレーム数を10倍にしたグラフを作成します。
# ガウス分布のアニメーションを作成:散布図 anim <- ggplot(dens_df, aes(x = x, y = density, color = as.factor(sigma), group = x)) + geom_point(show.legend = FALSE) + # 散布図 gganimate::transition_reveal(along = sigma) + # フレーム labs(title = "transition_reveal() + geom_point()", subtitle = "sigma = {frame_along}") # gif画像を作成 gganimate::animate(plot = anim, nframes = length(sd_vec)*10, fps = 10)
フレーム数の引数nframes
にsd_vec
の10
倍の値を指定します。
パラメータ数を10倍にしたグラフを作成します。
# 標準偏差として利用する値を作成 tmp_sd_vec <- seq(from = 0.5, to = 3, length.out = length(sd_vec)*10) # 標準偏差ごとにガウス分布の計算 tmp_dens_df <- tibble::tibble() for(sd in sd_vec) { # ガウス分布の確率密度を計算 tmp_df <- tibble::tibble( x = x_vals, sigma = sd, density = dnorm(x = x_vals, mean = 0, sd = sd) ) # 計算結果を結合 tmp_dens_df <- rbind(tmp_dens_df, tmp_df) } # ガウス分布のアニメーションを作成:散布図 anim <- ggplot(tmp_dens_df, aes(x = x, y = density, color = as.factor(sigma), group = x)) + geom_point(show.legend = FALSE) + # 散布図 gganimate::transition_reveal(along = sigma) + # フレーム labs(title = "transition_reveal() + geom_point()", subtitle = "sigma = {frame_along}") # gif画像を作成 gganimate::animate(plot = anim, nframes = length(tmp_sd_vec), fps = 10)
sd_vec
と同じ範囲で値の数が10倍(値の間隔が10分の1)になるように作成します。
補完された分布と、実際に計算した分布の違いが分かり・・・
データ間の補完が不要な場合は、transition_manual()
を使うのがいいと思います。
最後に、x軸の値ごとにフレームを切り替えます。
# ガウス分布のアニメーションを作成:散布図 anim <- ggplot(dens_df, aes(x = x, y = density, color = as.factor(sigma), group = sigma)) + geom_point(show.legend = FALSE) + # 散布図 geom_path(show.legend = FALSE) + # 折れ線 gganimate::transition_reveal(along = x) + # フレーム labs(title = "transition_reveal() + geom_point() + geom_path()", subtitle = "x = {round(frame_along, 1)}") # gif画像を作成 gganimate::animate(plot = anim, nframes = length(x_vals), fps = 10)
group
引数にsigma
列を指定すると、x軸の値の変化によってy軸の値が変化する様子を確認できます。
利用例:ランダムウォーク
ランダムウォークのアニメーションについては次の記事を参照してください。
おわりに
微妙に分からないところがありました。それと、分布の計算のところのループ処理をtidyverse的に処理したいのですが、長らくできないでいます。