はじめに
gganimate
パッケージについて理解したいシリーズです。
この記事では、2次元ランダムウォークのアニメーションをR言語で作成します。
【前の内容】
【他の内容】
【目次】
2次元ランダムウォーク:全方向移動
gganimate
パッケージを利用して、平面上を移動するランダムウォークのアニメーション(gif画像)を作成します。transition_reveal()
については、transition_reveal.Rmdを参照してください。
利用するパッケージを読み込みます。
# 利用パッケージ library(gganimate) library(tidyverse)
全方向移動の確認
まずは、平面上をランダムに移動する処理を確認します。
単位円上の点を作成します。
# フレーム数を指定 N <- 101 # 単位円上の点を作成 df <- tibble::tibble( n = 1:N, # 時計回りならN:1 d = seq(from = 0, to = 1, length.out = N), r = 2 * pi * d, x = cos(r), y = sin(r) ) head(df)
## # A tibble: 6 x 5 ## n d r x y ## <int> <dbl> <dbl> <dbl> <dbl> ## 1 1 0 0 1 0 ## 2 2 0.01 0.0628 0.998 0.0628 ## 3 3 0.02 0.126 0.992 0.125 ## 4 4 0.03 0.188 0.982 0.187 ## 5 5 0.04 0.251 0.969 0.249 ## 6 6 0.05 0.314 0.951 0.309
0から1の値を作成します。
さらに、円周率を使って
とします。
は、0から
の値になります。
x軸の値を、y軸の値を
で計算します。
作成した点を描画します。
# 単位円上の点を作図 anim <- ggplot(df, aes(x = x, y = y)) + geom_point(color = "red", size = 5) + # 散布図 geom_path(color = "hotpink") + # 折れ線 gganimate::transition_reveal(d) + # フレーム coord_fixed(ratio = 1) + # アスペクト比 labs(subtitle = "d = {frame_along}") # gif画像を作成 gganimate::animate(plot = anim, nframes = N, fps = 10)
の(
の)の点が、原点から半径1の円上の点になるのが分かります。
作成した点と原点を繋ぐ線を描画します。
# 原点から単位円上の点の方向を作図 anim <- df %>% rbind(tibble::tibble(n = 1:N, d = NA, r = NA, x = 0, y = 0)) %>% # 原点を追加 ggplot(aes(x = x, y = y)) + geom_line(color = "hotpink") + # 折れ線グラフ geom_point(color = "red", fill = "red", size = 5) + # 散布図 gganimate::transition_manual(n) + # フレーム coord_fixed(ratio = 1) + # アスペクト比 labs(subtitle = "n = {current_frame}") # gif画像を作成 gganimate::animate(plot = anim, nframes = N, fps = 10)
作成した点は、原点
からx軸方向に
・y軸方向に
移動(加算)した点であり、また直線距離で1移動した点と言えます。
つまり、を0から1の(
を0から
の)乱数として生成すると、現在の点(位置)からランダムな方向に距離1だけ移動できます。
1サンプル
1個のサンプルを描画します。
0からの一様乱数を生成します。
# 試行回数を指定 max_iter <- 100 # ランダムに値を生成 random_vec <- 2 * pi * runif(n = max_iter, min = 0, max = 1) random_vec[1:5]
## [1] 0.9700719 2.3439519 0.6918857 2.7661949 4.0917664
一様乱数は、runif()
で生成できます。最小値の引数min
に0
、最大値の引数max
に1
を指定します。(本当は0以上1未満の乱数を生成しないといけないのですが、やり方が分かりませんでした。)
生成した0
から1
の乱数に、2 * pi
を掛けます。max
引数に2 * pi
を指定してもできますが、ここでは原理的な部分が分かりやすいように処理しています。
生成した値からx軸とy軸の値を計算してデータフレームに格納して、過去の値の合計を求めます。
# 試行ごとに集計 random_df <- tibble::tibble( iteration = 0:max_iter, x = c(0, cos(random_vec)), y = c(0, sin(random_vec)) ) %>% dplyr::mutate( x = cumsum(x), y = cumsum(y) ) # 各試行までの合計 head(random_df)
## # A tibble: 6 x 3 ## iteration x y ## <int> <dbl> <dbl> ## 1 0 0 0 ## 2 1 0.565 0.825 ## 3 2 -0.133 1.54 ## 4 3 0.637 2.18 ## 5 4 -0.293 2.55 ## 6 5 -0.875 1.73
0回目の結果(スタート地点・原点)に相当する行を加えておきます。
x
列とy
列それぞれの累積和をcumsum()
で計算します。
アニメーションを作成します。
# 2次元ランダムウォークを作図 anim <- ggplot(random_df, aes(x = x, y = y)) + geom_point(color = "hotpink", size = 5) + # 散布図 geom_path(color = "hotpink", size = 1, alpha = 0.5) + # 折れ線 geom_hline(yintercept = 0, color = "red", linetype = "dashed") + # 水平線 geom_vline(xintercept = 0, color = "red", linetype = "dashed") + # 垂直線 gganimate::transition_reveal(along = iteration) + # フレーム coord_fixed(ratio = 1) + # アスペクト比 labs(title = "Random Walk", subtitle = "iter : {frame_along}") # gif画像を作成 gganimate::animate(plot = anim, nframes = max_iter+1, fps = 10)
目安として、と
の線を引いています。
複数サンプル
続いて、複数個のサンプルを同時に描画します。
0からの一様乱数を生成します。
# 試行回数を指定 max_iter <- 100 # サンプルサイズを指定 sample_size <- 10 # ランダムに値を生成 random_vec <- 2 * pi * runif(n = max_iter*sample_size, min = 0, max = 1) random_vec[1:5]
## [1] 0.5864319 5.5377602 3.5582797 2.4157349 0.1761415
試行回数max_iter
とサンプルサイズsample_size
を指定して、max_iter * sample_size
個の値を生成します。
生成した値をデータフレームに格納します。
# 元になるデータフレームを確認 tibble::tibble( iteration = rep(0:max_iter, each = sample_size), id = rep(1:sample_size, times = max_iter+1), x = c(rep(0, times = sample_size), cos(random_vec)), y = c(rep(0, times = sample_size), sin(random_vec)) ) %>% dplyr::filter(iteration == 1) %>% # 1回目の結果を表示 head()
## # A tibble: 6 x 4 ## iteration id x y ## <int> <int> <dbl> <dbl> ## 1 1 1 0.833 0.553 ## 2 1 2 0.735 -0.678 ## 3 1 3 -0.914 -0.405 ## 4 1 4 -0.748 0.664 ## 5 1 5 0.985 0.175 ## 6 1 6 -0.821 0.571
全てのサンプルに対して0回目の結果に相当する行を加えます。
また、random_vec
の1番目の要素をサンプル1の1回目の結果、2番目の要素をサンプル2の1回目の結果と続けて、さらにsample_size + 1
番目の要素をサンプル1の2回目の結果と続けて割り当てることにします。
0
からmax_iter
までの整数をそれぞれsample_size
個に複製して、試行回数(iteration
)列とします。
1
からsample_size
までの整数をmax_iter + 1
回繰り返して、サンプル番号(id
)列とします。
sample_size
個の0
の後にcos(random_vec), sin(random_vec)
を続けて、x軸y軸(x, y
)列とします。
サンプルごとに過去の値を合計を求めます。
# 試行ごとに集計 random_df <- tibble::tibble( iteration = rep(0:max_iter, each = sample_size), id = rep(1:sample_size, times = max_iter+1) %>% as.factor(), x = c(rep(0, times = sample_size), cos(random_vec)), y = c(rep(0, times = sample_size), sin(random_vec)) ) %>% dplyr::group_by(id) %>% # サンプルごとにグループ化 dplyr::mutate( x = cumsum(x), y = cumsum(y) ) %>% # 各試行までの合計 dplyr::ungroup() # グループ化の解除 # 3サンプルの3回目までの結果 random_df %>% dplyr::filter(iteration <= 3, id %in% 1:3)
## # A tibble: 12 x 4 ## iteration id x y ## <int> <fct> <dbl> <dbl> ## 1 0 1 0 0 ## 2 0 2 0 0 ## 3 0 3 0 0 ## 4 1 1 0.833 0.553 ## 5 1 2 0.735 -0.678 ## 6 1 3 -0.914 -0.405 ## 7 2 1 0.559 1.52 ## 8 2 2 0.299 -1.58 ## 9 2 3 -1.20 0.555 ## 10 3 1 0.811 0.547 ## 11 3 2 -0.325 -2.36 ## 12 3 3 -1.29 1.55
作図用に、サンプル番号(id
列の値)を因子型にしておきます。
サンプル番号(id
列)でグループ化して、x軸の値(x
列)とy軸の値(y
列)をそれぞれcumsum()
で累積和を計算します。
アニメーションを作成します。
# 2次元ランダムウォークを作図 anim <- ggplot(random_df, aes(x = x, y = y, color = id)) + geom_point(size = 5, show.legend = FALSE) + # 散布図 geom_path(size = 1, alpha = 0.3, show.legend = FALSE) + # 折れ線 geom_hline(yintercept = 0, color = "red", linetype = "dashed") + # 水平線 geom_vline(xintercept = 0, color = "red", linetype = "dashed") + # 垂直線 gganimate::transition_reveal(along = iteration) + # フレーム coord_fixed(ratio = 1) + # アスペクト比 labs(title = "Random Walk", subtitle = "iter : {frame_along}") # gif画像を作成 gganimate::animate(plot = anim, nframes = max_iter+1, fps = 10)
sample_size
個の折れ線グラフが描画されます。
最終結果は次のようになります。
# 最終結果 random_df %>% dplyr::filter(iteration == max_iter) %>% ggplot() + geom_point(mapping = aes(x = x, y = y, color = id), size = 5, show.legend = FALSE) + # 散布図 geom_path(data = random_df, mapping = aes(x = x, y = y, color = id), size = 1, alpha = 0.3, show.legend = FALSE) + # 折れ線 geom_hline(yintercept = 0, color = "red", linetype = "dashed") + # 水平線 geom_vline(xintercept = 0, color = "red", linetype = "dashed") + # 垂直線 coord_fixed(ratio = 1) + # アスペクト比 labs(title = "Random Walk", subtitle = paste0("iter : ", max_iter))
以上で、2次元ランダムウォークを作図できました。
おわりに
試作段階では酷いコードだったんですが、清書してると思いの外きれいに処理できて良かったです。