からっぽのしょこ

読んだら書く!書いたら読む!同じ事は二度調べ(たく)ない

【R】1次元ランダムウォークのアニメーションの作図【gganimate】

はじめに

 gganimateパッケージについて理解したいシリーズです。
 この記事では、1次元ランダムウォークのアニメーションをR言語で作成します。

【他の内容】

www.anarchive-beta.com

【目次】

1次元ランダムウォーク

 gganimateパッケージを利用して、1次元のランダムウォークのアニメーション(gif画像)を作成します。transition_reveal()については、transition_reveal.Rmdを参照してください。

 利用するパッケージを読み込みます。

# 利用パッケージ
library(gganimate)
library(tidyverse)


1サンプル

 まずは、1個のサンプルを描画します。

 -11をランダムに生成します。

# 試行回数を指定
max_iter <- 100

# ランダムに値を生成
random_vec <- sample(x = c(-1, 1), size = max_iter, replace = TRUE)
random_vec[1:10]
##  [1] -1  1  1 -1  1  1 -1 -1 -1 -1

 試行回数を指定して、正方向の移動(1)または負方向の移動(-1)をランダムに生成します。

 生成した値をデータフレームに格納して、過去の値の合計を求めます。

# 試行ごとに集計
random_df <- tibble::tibble(
  iteration = 0:max_iter, 
  random_val = c(0, random_vec)
) %>% 
  dplyr::mutate(value = cumsum(random_val)) # 各試行までの合計
head(random_df)
## # A tibble: 6 x 3
##   iteration random_val value
##       <int>      <dbl> <dbl>
## 1         0          0     0
## 2         1         -1    -1
## 3         2          1     0
## 4         3          1     1
## 5         4         -1     0
## 6         5          1     1

 0回目の結果(スタート地点・原点)に相当する行を加えておきます。
 n回目の位置(点)は、0回目からn回目の和で決まります。このような和を累積和と言い、cumsum()で計算できます。random_val列の1行目から各行までの和をcumsum()で計算してvalue列とします。

 折れ線グラフのアニメーションを作成します。

# 1次元ランダムウォークを作図
anim <- ggplot(random_df, aes(x = iteration, y = value)) + 
  geom_point(color = "hotpink", size = 5) + # 散布図
  geom_path(color = "hotpink", size = 1, alpha = 0.5) + # 折れ線
  geom_hline(yintercept = 0, color = "red", linetype = "dashed") + # 水平線
  gganimate::transition_reveal(along = iteration) + # フレーム
  labs(title = "Random Walk", 
       subtitle = "iter : {frame_along}")

# gif画像を作成
gganimate::animate(plot = anim, nframes = max_iter+1, fps = 10)

1次元ランダムウォーク

 目安として、期待値(y = 0)の線を引いています。

複数サンプル

 続いて、複数個のサンプルを同時に描画します。

 -11をランダムに生成します。

# 試行回数を指定
max_iter <- 100

# サンプルサイズを指定
sample_size <- 10

# ランダムに値を生成
random_vec <-  sample(x = c(-1, 1), size = max_iter*sample_size, replace = TRUE)
random_vec[1:10]
##  [1]  1  1 -1  1 -1  1 -1  1  1  1

 試行回数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), 
  random_val = c(rep(0, sample_size), random_vec)
) %>% 
  dplyr::filter(iteration == 1) %>% # 1回目の結果を表示
  head()
## # A tibble: 6 x 3
##   iteration    id random_val
##       <int> <int>      <dbl>
## 1         1     1          1
## 2         1     2          1
## 3         1     3         -1
## 4         1     4          1
## 5         1     5         -1
## 6         1     6          1

 全てのサンプルに対して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の後にrandom_vecを続けて、乱数(random_val)列とします。

 サンプルごとに過去の値を合計します。

# 試行ごとに集計
random_df <- tibble::tibble(
  iteration = rep(0:max_iter, each = sample_size), 
  id = rep(1:sample_size, times = max_iter+1) %>% 
    as.factor(), 
  random_val = c(rep(0, sample_size), random_vec)
) %>% 
  dplyr::group_by(id) %>% # サンプルごとにグループ化
  dplyr::mutate(value = cumsum(random_val)) %>% # 各試行までの合計
  dplyr::group_by() # グループ化の解除

# 3サンプルの3回目までの結果
random_df %>% 
  dplyr::filter(iteration <= 3, id %in% 1:3)
## # A tibble: 12 x 4
##    iteration id    random_val value
##        <int> <fct>      <dbl> <dbl>
##  1         0 1              0     0
##  2         0 2              0     0
##  3         0 3              0     0
##  4         1 1              1     1
##  5         1 2              1     1
##  6         1 3             -1    -1
##  7         2 1             -1     0
##  8         2 2             -1     0
##  9         2 3             -1    -2
## 10         3 1              1     1
## 11         3 2             -1    -1
## 12         3 3              1    -1

 作図用に、サンプル番号(id列の値)を因子型にしておきます。
 id列が同じ値でグループ化して、サンプルごとにcumsum()で累積和を計算します。

 アニメーションを作成します。

# 2次元ランダムウォークを作図
anim <- ggplot(random_df, aes(x = iteration, y = value, color = id)) + 
  geom_point(size = 5, show.legend = FALSE) + # 散布図
  geom_path(size = 1, alpha = 0.5, show.legend = FALSE) + # 折れ線
  geom_hline(yintercept = 0, color = "red", linetype = "dashed") + # 水平線
  gganimate::transition_reveal(along = iteration) + # フレーム
  labs(title = "Random Walk", 
       subtitle = "iter : {frame_along}")

# gif画像を作成
gganimate::animate(plot = anim, nframes = max_iter+1, fps = 10)

1次元ランダムウォーク

 sample_size個の折れ線グラフが描画されます。

 最終結果は次のようになります。

# 最終結果
random_df %>% 
  dplyr::filter(iteration == max_iter) %>% 
  ggplot() + 
  geom_point(mapping = aes(x = iteration, y = value, color = id), 
             size = 5, show.legend = FALSE) + # 散布図
  geom_path(data = random_df, mapping = aes(x = iteration, y = value, color = id), 
            size = 1, alpha = 0.5, show.legend = FALSE) + # 折れ線
  geom_hline(yintercept = 0, color = "red", linetype = "dashed") + # 水平線
  labs(title = "Random Walk", 
       subtitle = paste0("iter : ", max_iter))

1次元ランダムウォーク

 以上で、1次元ランダムウォークを作図できました。試行回数(時間)方向の変化も合わせて可視化しているので2次元に移動していますが、あくまで縦軸に対してランダムに移動しています。次は、x軸とy軸の2方向へ移動する場合を扱います。

おわりに

 transition_reveal()の記事の一部として書き始めたのですが、充実したので別の記事どころか3記事になりました。というわけで続きます。

【次の内容】

www.anarchive-beta.com