からっぽのしょこ

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

【R】2次元ランダムウォークの作図:2方向移動【gganimate】

はじめに

 R言語のgganimateパッケージを使ってグラフを動かそうシリーズです。
 この記事では、上下左右に移動する2次元のランダムウォークのアニメーションを作成します。

【前の内容】

www.anarchive-beta.com

【他の内容】

www.anarchive-beta.com

【目次】

2次元ランダムウォークの作図:2方向移動

 2方向(上下左右)に移動するランダムウォーク(random walk)のアニメーションを作成します。
 gganimate パッケージの関数 transition_reveal() については、「transition_reveal関数【gganimate】 - からっぽのしょこ」を参照してください。

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

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

 この記事では、基本的に パッケージ名::関数名() の記法を使うので、パッケージの読み込みは不要です。ただし、作図コードについてはパッケージ名を省略するので、ggplot2 を読み込む必要があります。
 また、ネイティブパイプ演算子 |> を使います。magrittr パッケージのパイプ演算子 %>% に置き換えられますが、その場合は magrittr を読み込む必要があります。

1サンプル

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

 試行回数を指定して、移動する量と軸をランダムに生成します。

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

# 乱数を生成
random_val_vec  <- sample(c(-1, 1), size = max_iter, replace = TRUE) # ±1
random_axis_vec <- sample(c("x", "y"), size = max_iter, replace = TRUE) # x軸・y軸方向
head(random_val_vec); head(random_axis_vec)
[1]  1  1 -1  1  1 -1
[1] "y" "y" "y" "x" "x" "x"

 試行ごとに「 -1 または 1 」と「 "x" または "y" 」をランダムに(等確率で)割り当てます。"x" はx軸方向、"y" はy軸方向の変化に対応し、1 は正の方向、-1 は負の方向の移動に対応します。

 移動量に応じた座標を計算します。

# 試行ごとに集計
trace_df <- tibble::tibble(
  iter = c(0, 0, 1:max_iter), # 試行番号
  r    = c(0, 0, random_val_vec), # 移動量
  axis = c("x", "y", random_axis_vec) # 移動方向
) |> 
  tidyr::pivot_wider(
    names_from  = axis, 
    values_from = r, 
    values_fill = 0
  ) |> # 軸ごとの列に分割
  dplyr::mutate(
    x = cumsum(x), 
    y = cumsum(y)
  ) # 総移動量を計算
trace_df
# A tibble: 101 × 3
    iter     x     y
   <dbl> <dbl> <dbl>
 1     0     0     0
 2     1     0     1
 3     2     0     2
 4     3     0     1
 5     4     1     1
 6     5     2     1
 7     6     1     1
 8     7     2     1
 9     8     1     1
10     9     1     2
# ℹ 91 more rows

 初期値(スタート地点・0回目の結果)を 0 として追加して、pivot_wider()で軸ごとの列に展開して、それぞれ各試行までの和(累積和)を cumsum() で計算します。

・簡単な例で確認(クリックで展開)

 簡単な例で試してみます。

# 簡単な例で確認
wide_df <- tibble::tibble(
  iter = c(1, 2, 3), 
  r    = c(-1, 1, -1), 
  axis = c("x", "y", "y")
)
long_df <- wide_df |> 
  tidyr::pivot_wider(
    names_from  = axis, 
    values_from = r
  )
wide_df; long_df
# A tibble: 3 × 3
   iter     r axis 
  <dbl> <dbl> <chr>
1     1    -1 x    
2     2     1 y    
3     3    -1 y    
# A tibble: 3 × 3
   iter     x     y
  <dbl> <dbl> <dbl>
1     1    -1    NA
2     2    NA     1
3     3    NA    -1

 移動量 -1, 1r 列、移動方向 "x", "y"axis 列としてデータフレームに格納します。
 axis 列の "x", "y" を列名として r 列の対応する行の値 -1, 1 を格納します。values_fill 引数を指定すると欠損値 NA を置き換えられます。

 点の推移のアニメーションを作成します。

# グラフサイズを設定
axis_size <- c(trace_df[["x"]], trace_df[["y"]]) |> 
  abs() |> 
  max()

# 2次元ランダムウォークのアニメーションを作図
anim <- ggplot() + 
  geom_hline(yintercept = 0, color = "red", linetype = "dashed") + # 初期値
  geom_vline(xintercept = 0, color = "red", linetype = "dashed") + # 初期値
  geom_path(data = trace_df, 
            mapping = aes(x = x, y = y), 
            size = 1) + # 軌跡
  geom_point(data = trace_df, 
             mapping = aes(x = x, y = y), 
             size = 4) + # 現在地点
  gganimate::transition_reveal(along = iter) + # フレーム切替
  coord_fixed(ratio = 1, 
              xlim = c(-axis_size, axis_size), 
              ylim = c(-axis_size, axis_size)) + # アスペクト比
  labs(title = "Random Walk", 
       subtitle = "iteration: {frame_along}", 
       x = "x", y = "y")

# gif動画を作成
gganimate::animate(
  plot = anim, nframes = max_iter+1, fps = 10, 
  width = 8, height = 8, units = "in", res = 250
)

2次元ランダムウォーク:1サンプル

 初期値(期待値)を破線で示します。

複数サンプル

 続いて、複数のサンプルを並べて描画します。
 作図コードについては「GitHub - anemptyarchive/gganimate/.../RandomWalk.R」を参照してください。

・最終結果

2次元ランダムウォーク:複数サンプル

・推移のアニメーション

 等確率で移動する場合、初期値が期待値になりますが分散を持ち(散らばり)ます。

 この記事では、格子状に移動する2次元ランダムウォークを扱いました。次の記事では、全ての方向に移動する場合を扱います。

おわりに

 このタイプのランダムウォークは知ってましたが、360°方向に変化するパターンもあるんですね。というわけで次はそっちのタイプをやります。

  • 2024.02.14:加筆修正しました。

 元の記事を書いてから2年近くが経ちましたが、未だに90°・360°タイプの呼び方もどちらが一般的なのかも分かっていません。記事中では雰囲気で呼んでいます。このブログではこういうことが稀によくあります。

【次の内容】

www.anarchive-beta.com