Rで解析:ggplot2の図をAnime.jsで動くSVGにする「gganime」パッケージの紹介
時間とともに変化するデータは、静止した図を並べるよりも、動きのある表現の方が推移を追いやすい場面があります。しかし、gganimateで作成したアニメーションをGIFや動画として書き出すと、ファイルの容量が大きくなり、拡大したときに輪郭がぼやける、再生位置を指定しにくいといった手間がかかります。
「gganime」パッケージは、gganimateで定義した図をAnime.jsというJavaScriptライブラリを用いて、ブラウザ上で動作するSVGアニメーションのウィジェットへ変換します。フレーム数や再生速度、ウィジェットの大きさの指定、ループ回数の制限、再生と一時停止の操作バーの表示が可能です。
また、gganimeOutputとrenderGganimeによるShinyアプリへの埋め込みも可能です。本パッケージの利用で、解像度に依存しない動的なグラフをHTMLの一部として扱えるのではないかと考えます。
パッケージバージョンは0.1.0。Windows 11 x64 (build 26200)のR version 4.6.1で確認しています。
<おすすめのRに関する書籍です>
パッケージのインストール
下記コマンドを実行してください。
# パッケージのインストール
install.packages("gganime")
# パッケージの読み込み
library("gganime")
# コマンド例実行で必要なパッケージを読み込み
library("ggplot2")
library("shiny")コマンド例
詳細はコメント、パッケージのヘルプを確認してください。
gganimeは、gganimateの記法をそのまま使います。ggplot2で作成した図にtransition_states()やtransition_time()などの時間遷移を加えると、gganim(アニメーションの設計図)というオブジェクトになります。gganimateのtransition_states、transition_time、transition_reveal、ease_aes、enter_fade、exit_fade、shadow_mark などの関数はgganimeからも読み込めるため、library(“gganimate”) を別途行う必要はありません。
anime()はこのgganimオブジェクトを受け取り、指定した数のフレームで図を描き分け、フレーム間の座標の変化をAnime.jsで補間するSVGアニメーションのウィジェットに変換します。出力はhtmlwidgetのため、R Markdownやシャイニー、単体のHTMLファイルとして保存できます。
ここでは、京都の主要な観光地(嵐山、伏見稲荷、宇治、天橋立)について、1年を隔月で区切った推計入込客数、平均気温、月間降水量をまとめた擬似データを共通データとして使用します。桜と紅葉の時期に客数が増える動きを、月の経過に沿ったアニメーションで確認します。
# 京都の観光地の隔月データを模したデータフレームの作成(4地点×6時点=24行)
観光 <- data.frame(
# 観光地の名称
地点 = factor(rep(c("嵐山", "伏見稲荷", "宇治", "天橋立"), each = 6)),
# 対象の月(隔月)
月 = rep(c(1, 3, 5, 7, 9, 11), times = 4),
# 月の平均気温(℃)
気温 = c(
4, 9, 19, 27, 23, 12,
4, 9, 19, 27, 23, 12,
4, 9, 18, 27, 23, 12,
3, 8, 17, 26, 22, 11
),
# 月間の推計入込客数(万人)
客数 = c(
18, 42, 30, 22, 28, 55,
22, 35, 28, 25, 26, 40,
8, 15, 14, 12, 13, 22,
4, 9, 12, 20, 11, 10
),
# 月間降水量(mm)
降水量 = c(
50, 110, 150, 220, 180, 70,
50, 110, 150, 220, 180, 70,
48, 108, 150, 210, 175, 68,
60, 120, 140, 200, 190, 80
)
)
<おすすめのRに関する書籍です>
[アニメーション付きSVGウィジェットの生成]:animeコマンド
transition_*()を加えたgganimオブジェクトを、Anime.jsで動作するSVGアニメーションのウィジェットに変換します。フレーム数や再生速度、ウィジェットの大きさ、ループ回数、操作バーの有無を指定できます。
| オプション | 意味 | 初期値 |
|---|---|---|
| plot | transition_*()を加えたgganimオブジェクト(ggplot2の図に時間遷移を付けたもの) | なし |
| nframes | アニメーションを標本化するフレーム数 | 100 |
| fps | 毎秒のフレーム数。再生速度を決める | 10 |
| duration | 総再生時間(秒)。指定するとfpsより優先される | NULL |
| width | ウィジェットの横幅(ピクセル)。heightとの比が図の縦横比になる | 640 |
| height | ウィジェットの高さ(ピクセル) | 480 |
| precision | アニメーションさせる座標を丸める小数点以下の桁数 | 2 |
| loop | ループの指定。TRUE、FALSE、または繰り返し回数の数値 | TRUE |
| controls | 再生・一時停止と再生位置の操作バーを表示するか | TRUE |
| elementId | htmlwidgetの要素IDの指定 | NULL |
| … | 未使用。将来の引数のための予約 | なし |
# 気温と入込客数の関係を月ごとに描き分けるgganimオブジェクトの作成
p <- ggplot(観光, aes(気温, 客数, size = 降水量, colour = 地点)) +
geom_point(alpha = 0.8) +
# 図の主題に現在の月を表示する
labs(title = "{closest_state}月の京都観光", size = "降水量(mm)") +
# 月の値ごとに状態を切り替えるアニメーション
transition_states(月)
# 既定の設定でSVGアニメーションのウィジェットを生成
anime(p)
# フレーム数と再生速度、ウィジェットの大きさを指定して生成
anime(p, nframes = 60, fps = 15, width = 720, height = 540)
# ループを2回に制限し、操作バーを非表示にして生成
anime(p, loop = 2, controls = FALSE)実際のプロットはアニメーションします。

<おすすめのRに関する書籍です>
[Shinyアプリへのアニメーション表示枠の配置]:gganimeOutputコマンド
ShinyアプリのUI側で、gganimeのアニメーションを表示する領域を宣言します。出力IDと表示枠の大きさを指定します。
| オプション | 意味 | 初期値 |
|---|---|---|
| outputId | 読み取り先となる出力変数の名前 | なし |
| width | 妥当なCSS単位(”100%”、”400px”、”auto” など)または数値。数値は文字列に変換され “px” が付く | “100%” |
| height | 妥当なCSS単位(”100%”、”400px”、”auto” など)または数値。数値は文字列に変換され “px” が付く | “400px” |
# ShinyアプリのUIでアニメーションの表示枠を宣言する
ui <- fluidPage(
titlePanel("京都観光の月別推移"),
# 出力IDと表示枠の大きさを指定する
gganimeOutput("観光アニメ", width = "600px", height = "450px")
)[Shinyサーバーでのアニメーションの描画]:renderGganimeコマンド
Shinyアプリのサーバー側で、anime()が返すウィジェットを生成する式を評価し、UIのgganimeOutputへ描画します。exprにはanime()の呼び出しを含む式を渡します。
| オプション | 意味 | 初期値 |
|---|---|---|
| expr | gganimeのウィジェットを生成する式。通常はanime()の呼び出し | なし |
| env | exprを評価する環境 | parent.frame() |
| quoted | exprがquote()で囲んだ式かどうか。式を変数に保存して渡す場合に使う | FALSE |
# UIのgganimeOutputに対応する描画処理をサーバーで定義する
server <- function(input, output, session) {
output$観光アニメ <- renderGganime({
p <- ggplot(観光, aes(気温, 客数, size = 降水量, colour = 地点)) +
geom_point(alpha = 0.8) +
transition_states(月)
# anime()が返すウィジェットをそのまま描画する
anime(p, width = 600, height = 450)
})
}
# 定義したUIとサーバーを渡してアプリを起動する
if (interactive()) {
shinyApp(ui, server)
}
<おすすめのRに関する書籍です>
この記事が誰かの役に立ちますように。