からっぽのしょこ

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

ハロプログループの最小・最大活動月数の推移のバーチャートレースを作ってみた

はじめに

 ハロー!プロジェクトの歴史を可視化しようシリーズ(仮)です。
 この記事では、ハロプロキッズ・エッグ・研修生期間を含めた各グループの最小活動月数または最大活動月数の推移をバーチャートレースにします。

【他の記事】

www.anarchive-beta.com

【目次】

最小・最大活動月数の推移の可視化

 ハロー!プロジェクトのグループ・ユニットの(キッズ・エッグ・研修生期間を含む)最小活動月数または最大活動月数の推移をバーチャートレースで可視化します。

 次のパッケージを利用します。

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

 この記事では、基本的にパッケージ名::関数名()の記法を使うので、パッケージを読み込む必要はありません。ただし、作図コードがごちゃごちゃしないようにパッケージ名を省略しているため、ggplot2は読み込む必要があります。また、基本的にベースパイプ(ネイティブパイプ)演算子|>を使いますが、パイプ演算子%>%ででないと処理できない部分があるため、magrittrも読み込む必要があります。

「データの読込」と「期間の指定」

 共通する処理なので「ハロプログループの平均年齢のバーチャートレースを作ってみた - からっぽのしょこ」を参照してください。

演出用の処理

 集計を行う前に、アニメーションの演出用のデータフレームを作成します。

改名グループの対応データ

 各フレーム(各グラフ・各月)に応じてグループ名のラベルを変更するために、月とグループ名の対応データを作成します。この処理も先ほどの記事を参照してください。

結成前月と解散月のデータ

 続いて、バーの変化を強調するために、結成1か月前と解散月のデータ(メンバー数が0のデータ)を作成します。

 グループごとの「結成1か月前」と「解散月」のデータフレームを作成します。

# 結成前月・解散月のデータを作成
member_0_df <- group_df |> 
  dplyr::group_by(groupID) |> # 日付の再設定用にグループ化
  dplyr::mutate(dissolveDate = dplyr::lead(dissolveDate, n = max(dplyr::n())-1)) |> # 最後の行を1行目にズラす
  dplyr::slice_head(n = 1) |> # 1行目を抽出
  dplyr::ungroup() |> # グループ化を解除
  dplyr::mutate(
    formDate = formDate |> 
      lubridate::rollback() |> # 結成1か月前に変更
      lubridate::floor_date(unit = "mon"), 
    dissolveDate = dissolveDate |> 
      lubridate::floor_date(unit = "mon")
  ) |> # 月単位に切り捨て
  tidyr::pivot_longer(
    cols = c(formDate, dissolveDate), 
    names_to = "date_type", 
    values_to = "date"
  ) |> # 結成前月・解散月を同じ列に変形
  dplyr::select(date, groupID) |> # 利用する列を選択
  dplyr::filter(!is.na(date)) |> # 現在活動中のグループの解散月を除去
  tibble::add_column(
    groupName = " ", 
    exp = 0
  ) |> # メンバー数(0人)を追加
  dplyr::filter(date > min(date_vec), date < max(date_vec)) |> # 指定した期間内のデータを抽出
  dplyr::arrange(date, groupID) # 昇順に並び替え
member_0_df
## # A tibble: 76 × 4
##    date       groupID groupName   exp
##    <date>       <int> <chr>     <dbl>
##  1 1998-09-01       2 " "           0
##  2 1999-01-01       3 " "           0
##  3 1999-02-01       4 " "           0
##  4 1999-03-01       5 " "           0
##  5 1999-07-01       7 " "           0
##  6 1999-09-01       6 " "           0
##  7 2000-05-01       8 " "           0
##  8 2000-06-01       9 " "           0
##  9 2000-10-01       3 " "           0
## 10 2001-12-01       9 " "           0
## # … with 66 more rows

 group_dfformDate, dissolveDate列は、改名したグループだと、最初の行は「結成日・改名日」、途中の行は「改名日・改名日」、最後の行は「改名日・解散日」になります。そこで、グループごとに、formDate列の最初の値とdissolveDate列の最後の値を取り出して、「結成日・解散日」にします。

 dissolveDate列の最後の行の値が最初の行に来るように、lead()で要素をズラします。ズラす行数の引数nに、(各グループの)データ(行)数 - 1の値を指定します。行数はn()で得られますが行数分の値が返ってくるので、max()で1つの値にして使います(一発で行数をスカラで返す関数を使いたい)。
 必要な値を最初の行にまとめられたので、slice_head()で最初の行のみ取り出します。
 ここまでで、グループごとに、結成日と解散日をまとめた行が得られました。続いて、次で作成する集計データと対応するようにデータフレームを整形します。

 結成日と解散日をfloor_date()で月初の値にします。その際に、rollback()で結成月の1か月前にします。
 pivot_longer()で結成前月と解散月の列をまとめて、date列とします。その際に、結成か解散かを表す列をdate_type列としますが、この列は使いません。
 現在活動中のグループの解散月はNAなので、is.na()を使って取り除きます。
 結成前と解散後はメンバー数が0なので、結合時(集計データ)の列と対応するように値(データ)を設定します。グループ名については、作図時に"NA"と表示されないように(空白にするために)半角スペースにしておきます。

 演出用の2つのデータフレームを用意できました。次の集計処理に利用します。

最小・最大活動月数の集計と順位付け

 活動月数の最小値または最大値を集計してランキングを付けます。

 受け皿となるデータフレームの作成用にサイズを取得します。

# サイズを取得
group_size  <- max(group_df[["groupID"]])
member_size <- max(member_df[["memberID"]])

 グループ数・メンバー数を取得します。

 各月における「最小活動月数または最大活動月数」と「順位」のデータフレームを作成します。

# 最小活動月数or最大活動月数を集計
rank_df <- tidyr::expand_grid(
  date = date_vec, 
  groupID = 1:group_size, 
  memberID = 1:member_size
) |> # 全ての組み合わせを作成
  dplyr::left_join(group_name_df, by = c("date", "groupID")) |> # グループ情報を結合
  dplyr::filter(date >= formDate, date <= dissolveDate) |> # 活動中のグループを抽出
  dplyr::select(!c(formDate, dissolveDate)) |> # 不要な列を削除
  dplyr::left_join(
    join_df |> 
      dplyr::mutate(
        joinDate = lubridate::floor_date(joinDate, unit = "mon"), 
        gradDate = lubridate::floor_date(gradDate, unit = "mon")
      ), # 月単位に切り捨て
    by = c("groupID", "memberID")
  ) |> # 加入メンバー情報を結合
  dplyr::filter(date >= joinDate, date < gradDate | is.na(gradDate)) |> # 活動中のメンバーを抽出
  dplyr::select(!c(joinDate, gradDate)) |> # 不要な列を削除
  dplyr::left_join(
    member_df |> 
      dplyr::distinct(memberID, .keep_all = TRUE) |> # 重複を除去
      dplyr::mutate(HPjoinDate = lubridate::floor_date(HPjoinDate, unit  = "mon")), # 月単位に切り捨て
    by = "memberID"
  ) |> # メンバー情報を結合
  dplyr::select(date, groupID, groupName, memberID, memberName, HPjoinDate) |> # 利用する列を選択
  dplyr::mutate(
    exp = lubridate::interval(start = HPjoinDate, end = date) |> 
      lubridate::time_length(unit = "mon")
  ) |> # メンバーの活動月数を計算
  dplyr::group_by(date, groupID) |> # 最小・最大活動月数の抽出用にグループ化
  #dplyr::slice_min(exp, n = 1, with_ties = FALSE) |> # グループの最小活動月数を抽出
  dplyr::slice_max(exp, n = 1, with_ties = FALSE) |> # グループの最大活動月数を抽出
  dplyr::select(!c(memberID, memberName, HPjoinDate)) |> # 不要な列を削除
  dplyr::bind_rows(member_0_df) |> # 結成前月・解散月を追加
  dplyr::arrange(date, exp, groupID) |> # 順位付け用に並べ替え
  dplyr::group_by(date) |> # 順位付け用にグループ化
  dplyr::mutate(
    groupID = factor(groupID), 
    year = exp %/% 12, 
    month = round(exp %% 12, digits = 1), 
    ranking = dplyr::row_number(-exp), 
  ) |> # ラベル用の値と順位を追加
  dplyr::ungroup() |> # グループ化の解除
  dplyr::select(date, groupID, groupName, exp, year, month, ranking) |> # 利用する列を選択
  dplyr::arrange(date, ranking) # 昇順に並べ替え
rank_df
## # A tibble: 3,219 × 7
##    date       groupID groupName        exp  year month ranking
##    <date>     <fct>   <chr>          <dbl> <dbl> <dbl>   <int>
##  1 1997-09-01 1       モーニング娘。     0     0     0       1
##  2 1997-10-01 1       モーニング娘。     1     0     1       1
##  3 1997-11-01 1       モーニング娘。     2     0     2       1
##  4 1997-12-01 1       モーニング娘。     3     0     3       1
##  5 1998-01-01 1       モーニング娘。     4     0     4       1
##  6 1998-02-01 1       モーニング娘。     5     0     5       1
##  7 1998-03-01 1       モーニング娘。     6     0     6       1
##  8 1998-04-01 1       モーニング娘。     7     0     7       1
##  9 1998-05-01 1       モーニング娘。     0     0     0       1
## 10 1998-06-01 1       モーニング娘。     1     0     1       1
## # … with 3,209 more rows

 まずは、データの受け皿となる、月・グループID・メンバーIDの全ての組み合わせを持つデータフレームを作成します。
 次に、各グループの(改名に対応した)名前と、各メンバーの加入・卒業日の情報を結合して、不要な行(組み合わせ)を削除します。
 続いて、各メンバーのハロプロ加入日の情報を結合して、各メンバーの活動月数を計算し、各グループの活動月数の最小値をslice_min()または最大値をslice_max()で取り出します。最小・最大活動月数はコメントアウトで切り替えます。
 最後に、各グループの最小・最大活動月数に応じて順位付けして、作図用にデータを編集します。

 各処理を細かく見ます。

・コード(クリックで展開)

 月・グループID・メンバーIDの全ての組み合わせを持つデータフレームを作成します。

# 受け皿を作成
df1 <- tidyr::expand_grid(
  date = date_vec, 
  groupID = 1:group_size, 
  memberID = 1:member_size
) # 全ての組み合わせを作成
df1
## # A tibble: 3,540,240 × 3
##    date       groupID memberID
##    <date>       <int>    <int>
##  1 1997-09-01       1        1
##  2 1997-09-01       1        2
##  3 1997-09-01       1        3
##  4 1997-09-01       1        4
##  5 1997-09-01       1        5
##  6 1997-09-01       1        6
##  7 1997-09-01       1        7
##  8 1997-09-01       1        8
##  9 1997-09-01       1        9
## 10 1997-09-01       1       10
## # … with 3,540,230 more rows

 月date_vec・グループID1:group_size・メンバーID1:member_sizeのそれぞれの値について、全ての組み合わせをexpand_grid()で作成します。
 これは、次のようなデータフレームが得られます。

tidyr::expand_grid(
  x = 1:3, 
  y = 1:3, 
  z = 1:3
)
## # A tibble: 27 × 3
##        x     y     z
##    <int> <int> <int>
##  1     1     1     1
##  2     1     1     2
##  3     1     1     3
##  4     1     2     1
##  5     1     2     2
##  6     1     2     3
##  7     1     3     1
##  8     1     3     2
##  9     1     3     3
## 10     2     1     1
## # … with 17 more rows

 一時的に、行数が「月数×グループ数×メンバー数」のデータフレームが作成されます(かなり大きくなるのでご注意ください。purrrを使えるともっと上手くやれるんだと思う)。

 各グループの各月に対応した名前・結成日・解散日の情報を結合します。

# グループの情報を結合
df2 <- df1 |> 
  dplyr::left_join(group_name_df, by = c("date", "groupID")) |> # グループ情報を結合
  dplyr::filter(date >= formDate, date <= dissolveDate) # 活動中のグループを抽出
df2
## # A tibble: 877,770 × 6
##    date       groupID memberID groupName      formDate   dissolveDate
##    <date>       <int>    <int> <chr>          <date>     <date>      
##  1 1997-09-01       1        1 モーニング娘。 1997-09-01 2013-12-01  
##  2 1997-09-01       1        2 モーニング娘。 1997-09-01 2013-12-01  
##  3 1997-09-01       1        3 モーニング娘。 1997-09-01 2013-12-01  
##  4 1997-09-01       1        4 モーニング娘。 1997-09-01 2013-12-01  
##  5 1997-09-01       1        5 モーニング娘。 1997-09-01 2013-12-01  
##  6 1997-09-01       1        6 モーニング娘。 1997-09-01 2013-12-01  
##  7 1997-09-01       1        7 モーニング娘。 1997-09-01 2013-12-01  
##  8 1997-09-01       1        8 モーニング娘。 1997-09-01 2013-12-01  
##  9 1997-09-01       1        9 モーニング娘。 1997-09-01 2013-12-01  
## 10 1997-09-01       1       10 モーニング娘。 1997-09-01 2013-12-01  
## # … with 877,760 more rows

 left_join()で、group_name_dfからグループ名(groupName列)・結成月(formDate列)・解散月(dissolveDate列)の情報を、月(date列)とグループ(groupID列)で対応付けて結合します。
 dateformDate以上でdissoveDate以下の行を抽出します。各月において活動中のグループが得られ(groupIDについて不要なデータが削除され)ます。

 各メンバーのグループ加入・卒業日の情報を結合します。

# 加入・卒業の情報を結合
df3 <- df2 |> 
  dplyr::select(!c(formDate, dissolveDate)) |> # 不要な列を削除
  dplyr::left_join(
    join_df |> 
      dplyr::mutate(
        joinDate = lubridate::floor_date(joinDate, unit = "mon"), 
        gradDate = lubridate::floor_date(gradDate, unit = "mon")
      ), # 月単位に切り捨て
    by = c("groupID", "memberID")
  ) |> # 所属メンバー情報を結合
  dplyr::filter(date >= joinDate, date < gradDate | is.na(gradDate)) # 活動中のメンバーを抽出
df3
## # A tibble: 21,273 × 6
##    date       groupID memberID groupName      joinDate   gradDate  
##    <date>       <int>    <int> <chr>          <date>     <date>    
##  1 1997-09-01       1        1 モーニング娘。 1997-09-01 2001-04-01
##  2 1997-09-01       1        2 モーニング娘。 1997-09-01 2000-01-01
##  3 1997-09-01       1        3 モーニング娘。 1997-09-01 2005-01-01
##  4 1997-09-01       1        4 モーニング娘。 1997-09-01 2004-01-01
##  5 1997-09-01       1        5 モーニング娘。 1997-09-01 1999-04-01
##  6 1997-10-01       1        1 モーニング娘。 1997-09-01 2001-04-01
##  7 1997-10-01       1        2 モーニング娘。 1997-09-01 2000-01-01
##  8 1997-10-01       1        3 モーニング娘。 1997-09-01 2005-01-01
##  9 1997-10-01       1        4 モーニング娘。 1997-09-01 2004-01-01
## 10 1997-10-01       1        5 モーニング娘。 1997-09-01 1999-04-01
## # … with 21,263 more rows

 left_join()で、join_dfから加入月(joinDate列)・卒業月(gradDate列)の情報を、グループ(groupID列)とメンバー(memberID列)で対応付けて結合します。結合時に、floor_date()で日付から月に変換します。
 datejoinDate以上でgradDate以下または欠損値の行を抽出します。各月において活動中のメンバーが得られ(memberIDについて不要なデータが削除され)ます。

 各メンバーのハロプロ加入日の情報を結合して、活動月数を計算します。

# 各メンバーの活動月数を計算
df4 <- df3 |> 
  dplyr::select(!c(joinDate, gradDate)) |> # 不要な列を削除
  dplyr::left_join(
    member_df |> 
      dplyr::distinct(memberID, .keep_all = TRUE) |> # 重複を除去
      dplyr::mutate(HPjoinDate = lubridate::floor_date(HPjoinDate, unit  = "mon")), # 月単位に切り捨て
    by = "memberID"
  ) |> # メンバー情報を結合
  dplyr::select(date, groupID, groupName, memberID, memberName, HPjoinDate) |> # 利用する列を選択
  dplyr::mutate(
    exp = lubridate::interval(start = HPjoinDate, end = date) |> 
      lubridate::time_length(unit = "mon")
  ) # メンバーの活動月数を計算
df4
## # A tibble: 21,273 × 7
##    date       groupID groupName      memberID memberName HPjoinDate   exp
##    <date>       <int> <chr>             <int> <chr>      <date>     <dbl>
##  1 1997-09-01       1 モーニング娘。        1 中澤裕子   1997-09-01     0
##  2 1997-09-01       1 モーニング娘。        2 石黒彩     1997-09-01     0
##  3 1997-09-01       1 モーニング娘。        3 飯田圭織   1997-09-01     0
##  4 1997-09-01       1 モーニング娘。        4 安倍なつみ 1997-09-01     0
##  5 1997-09-01       1 モーニング娘。        5 福田明日香 1997-09-01     0
##  6 1997-10-01       1 モーニング娘。        1 中澤裕子   1997-09-01     1
##  7 1997-10-01       1 モーニング娘。        2 石黒彩     1997-09-01     1
##  8 1997-10-01       1 モーニング娘。        3 飯田圭織   1997-09-01     1
##  9 1997-10-01       1 モーニング娘。        4 安倍なつみ 1997-09-01     1
## 10 1997-10-01       1 モーニング娘。        5 福田明日香 1997-09-01     1
## # … with 21,263 more rows

 left_join()で、member_dfからハロプロ加入日(HPjoinDate列)の情報を、メンバー(memberID列)で対応付けて結合します。メンバー名(memberName列)は確認用です。結合時に、distinct()で重複データを削除し、floor_date()で月単位に切り捨てます。
 interval()time_length()で、HPjoinDateからdateまでの月数を求めます。各月におけるメンバーの活動月数が得られます。

 各グループの最小活動月数または最大活動月数を抽出します。

# 各グループの最小活動月数または最大活動月数を抽出
df5 <- df4 |> 
  dplyr::group_by(date, groupID) |> # 最小・最大活動月数の抽出用にグループ化
  dplyr::slice_min(exp, n = 1, with_ties = FALSE) # グループの最小活動月数を抽出
  #dplyr::slice_max(exp, n = 1, with_ties = FALSE) # グループの最大活動月数を抽出
df5
## # A tibble: 3,143 × 7
## # Groups:   date, groupID [3,143]
##    date       groupID groupName      memberID memberName HPjoinDate   exp
##    <date>       <int> <chr>             <int> <chr>      <date>     <dbl>
##  1 1997-09-01       1 モーニング娘。        1 中澤裕子   1997-09-01     0
##  2 1997-10-01       1 モーニング娘。        1 中澤裕子   1997-09-01     1
##  3 1997-11-01       1 モーニング娘。        1 中澤裕子   1997-09-01     2
##  4 1997-12-01       1 モーニング娘。        1 中澤裕子   1997-09-01     3
##  5 1998-01-01       1 モーニング娘。        1 中澤裕子   1997-09-01     4
##  6 1998-02-01       1 モーニング娘。        1 中澤裕子   1997-09-01     5
##  7 1998-03-01       1 モーニング娘。        1 中澤裕子   1997-09-01     6
##  8 1998-04-01       1 モーニング娘。        1 中澤裕子   1997-09-01     7
##  9 1998-05-01       1 モーニング娘。        7 保田圭     1998-05-01     0
## 10 1998-06-01       1 モーニング娘。        7 保田圭     1998-05-01     1
## # … with 3,133 more rows

 活動月数(exp列)の最小値をslice_min()または最大値をslice_max()で取り出します。必要に応じてコメントアウトで処理を切り替える必要があります。

 作図用に編集します。

# 作図用に編集
df6 <- df5 |> 
  dplyr::select(!c(memberID, memberName, HPjoinDate)) |> # 不要な列を削除
  dplyr::bind_rows(member_0_df) |> # 結成前月・解散月を追加
  dplyr::arrange(date, exp, groupID) |> # 順位付け用に並べ替え
  dplyr::group_by(date) |> # 順位付け用にグループ化
  dplyr::mutate(
    groupID = factor(groupID), 
    year = exp %/% 12, 
    month = round(exp %% 12, digits = 1), 
    ranking = dplyr::row_number(-exp), 
  ) |> # ラベル用の値と順位を追加
  dplyr::ungroup() |> # グループ化の解除
  #dplyr::select(date, groupID, groupName, exp, year, month, ranking) |> # 利用する列を選択
  dplyr::arrange(date, ranking) # 昇順に並べ替え
df6
## # A tibble: 3,219 × 7
##    date       groupID groupName        exp  year month ranking
##    <date>     <fct>   <chr>          <dbl> <dbl> <dbl>   <int>
##  1 1997-09-01 1       モーニング娘。     0     0     0       1
##  2 1997-10-01 1       モーニング娘。     1     0     1       1
##  3 1997-11-01 1       モーニング娘。     2     0     2       1
##  4 1997-12-01 1       モーニング娘。     3     0     3       1
##  5 1998-01-01 1       モーニング娘。     4     0     4       1
##  6 1998-02-01 1       モーニング娘。     5     0     5       1
##  7 1998-03-01 1       モーニング娘。     6     0     6       1
##  8 1998-04-01 1       モーニング娘。     7     0     7       1
##  9 1998-05-01 1       モーニング娘。     0     0     0       1
## 10 1998-06-01 1       モーニング娘。     1     0     1       1
## # … with 3,209 more rows

 bind_rows()で「メンバーが0のデータmember_0_df」を結合します。
 色分け用にグループIDを因子型に変換します。
 活動月数をyy歳mmか月と表示するために、活動月数を12で割った整数(yyの値)をyear列、12で割った余り(mmの値)をmonth列とします。整数部分は%/%、余り部分は%%で計算できます。また、余り部分についてはround()で値を小数点以下第1位で丸めておきます。
 row_number()で活動月数に応じて順位を付けて、ranking列とします。昇順に通し番号が割り当てられるので、-を付けて大小関係を反転させます。


 以上で、必要なデータを得られました。次は、作図を行います。

推移の可視化

 最小または最大活動月数と順位を棒グラフで可視化します。

バーチャートレースの作成

 最小活動月数または最大活動月数の推移をバーチャートレースで可視化します。バーチャートレースの作図については別資料を参照してください。

 フレームに関する値を指定します。

# 遷移フレーム数を指定
t <- 9

# 一時停止フレーム数を指定
s <- 1

# 1秒間に表示する月数を指定:(値が大きいと意図した通りにならない)
mps <- 3

# フレーム数を取得
n <- length(unique(rank_df[["date"]]))
n
## [1] 298

 現月と次月のグラフを繋ぐアニメーションのフレーム数をtとして、整数を指定します。
 各月のグラフで一時停止するフレーム数をsとして、整数を指定します。
 基本となるフレーム数(月数)をnとします。

 バーチャートレースを作成します。

# バーチャートレースを作成:(y軸可変)
anim <- ggplot(rank_df, aes(x = ranking, y = exp, fill = groupID, color = groupID)) + 
  geom_bar(stat = "identity", width = 0.9, alpha = 0.8) + # 活動月数バー
  geom_text(aes(label = paste(" ", year, "年", month, "か月")), hjust = 0) + # 活動年数ラベル
  geom_text(aes(y = 0, label = paste(groupName, " ")), hjust = 1) + # グループ名ラベル
  gganimate::transition_states(states = date, transition_length = t, state_length = s, wrap = FALSE) + # フレーム
  gganimate::ease_aes("cubic-in-out") + # アニメーションの緩急
  gganimate::view_follow(fixed_x = TRUE) + # 表示範囲のフィット
  coord_flip(clip = "off", expand = FALSE) + # 軸の入れ変え
  scale_x_reverse() + # x軸を反転
  theme(
    axis.title.x = element_blank(), # x軸のラベル
    axis.title.y = element_blank(), # y軸のラベル
    axis.text.x = element_blank(), # x軸の目盛ラベル
    axis.text.y = element_blank(), # y軸の目盛ラベル
    axis.ticks.x = element_blank(), # x軸の目盛指示線
    axis.ticks.y = element_blank(), # y軸の目盛指示線
    #panel.grid.major.x = element_line(color = "grey", size = 0.1), # x軸の主目盛線
    panel.grid.major.y = element_blank(), # y軸の主目盛線
    #panel.grid.minor.x = element_line(color = "grey", size = 0.1), # x軸の補助目盛線
    panel.grid.minor.y = element_blank(), # y軸の補助目盛線
    panel.border = element_blank(), # グラフ領域の枠線
    #panel.background = element_blank(), # グラフ領域の背景
    plot.title = element_text(color = "black", face = "bold", size = 20, hjust = 0.5), # 全体のタイトル
    plot.subtitle = element_text(color = "black", size = 15, hjust = 0.5), # 全体のサブタイトル
    plot.margin = margin(t = 10, r = 100, b = 10, l = 150, unit = "pt"), # 全体の余白
    legend.position = "none" # 凡例の表示位置
  ) + # 図の体裁
  labs(
    title = "ハロプログループの最小活動年数の推移", 
    #title = "ハロプログループの最大活動年数の推移", 
    subtitle = paste0(
      "{lubridate::year(closest_state)}年", 
      "{stringr::str_pad(lubridate::month(closest_state), width = 2, pad = 0)}月", 
      "01日時点"
    ), 
    caption = "データ:「https://github.com/xxgentaroxx/HP_DB」"
  ) # ラベル

 最小活動月数か最大活動月数かによって、タイトル(labs()title引数の文字列)をコメントアウトで切り替えます。

 animate()でgif画像を作成します。

# gif画像を作成
g <- gganimate::animate(
  plot = anim, 
  nframes = n*(t+s), fps = (t+s)*mps, 
  width = 900, height = 800
)
g

 plot引数にグラフ、nframes引数にフレーム数、fps引数に1秒当たりのフレーム数を指定します。

 anim_save()でgif画像を保存します。

# gif画像を保存
gganimate::anim_save(filename = "output/Exp_MinMax.gif", animation = g)

 filename引数にファイルパス("(保存する)フォルダ名/(作成する)ファイル名.gif")、animation引数に作成したgif画像を指定します。
 (ファイルサイズの上限は越えてないんだけどアップロードできなかったので完成例は省略します。)

 動画を作成する場合は、renderer引数を指定します。

# 動画を作成と保存
m <- gganimate::animate(
  plot = anim, 
  nframes = n*(t+s), fps = (t+s)*mps, 
  width = 900, height = 800, 
  renderer = gganimate::av_renderer(file = "output/Exp_MinMax.mp4")
)

 renderer引数に、レンダリング方法に応じた関数を指定します。この例では、av_renderer()を使います。
 av_renderer()file引数に保存先のファイルパス("(保存する)フォルダ名/(作成する)ファイル名.mp4")を指定します。

 (記事に動画を貼れないのでこれで代用します。)

 エッグに加入する前にユニットに参加されている方がいるので一瞬マイナスになります。

月を指定して作図

 最後に、指定した月における最小年齢または最大年齢のグラフを作成します。

 月を指定して、作図用のデータを作成します。

# 月(月初の日付)を指定
date_val <- "2022-06-01"

# 作図用のデータを抽出
mon_rank_df <- rank_df |> 
  dplyr::filter(date == lubridate::as_date(date_val)) |> # 指定した月のデータを抽出
  dplyr::filter(groupName != " ") # 演出用のデータを除去

# 年齢の最大値を取得
y_max <- max(mon_rank_df[["exp"]]) %/% 12
y_max
## [1] 5

 y軸目盛の編集用に、活動年数の最大値y_maxを作成しておきます。

 棒グラフを作成します。

# 棒グラフを作成
graph <- ggplot(mon_rank_df, aes(x = ranking, y = exp, fill = groupID, color = groupID)) + 
  geom_bar(stat = "identity", width = 0.9, alpha = 0.8) + # 活動月数バー
  geom_text(aes(y = 0, label = paste(" ", year, "年", month, "か月")), hjust = 0, color = "white") + # 活動年数ラベル
  geom_text(aes(y = 0, label = paste(groupName, " ")), hjust = 1) + # グループ名ラベル
  coord_flip(clip = "off", expand = FALSE) + # 軸の入れ変え
  scale_x_reverse(breaks = 1:nrow(mon_rank_df)) + # x軸(縦軸)を反転
  scale_y_continuous(breaks = seq(0, y_max, 5)*12, labels = seq(0, y_max, 5)) + # y軸(横軸)目盛
  theme(
    axis.title.y = element_blank(), # y軸のラベル
    axis.text.y = element_blank(), # y軸の目盛ラベル
    axis.ticks.x = element_blank(), # x軸の目盛指示線
    #panel.grid.major.x = element_line(color = "grey", size = 0.1), # x軸の主目盛線
    panel.grid.major.y = element_blank(), # y軸の主目盛線
    #panel.grid.minor.x = element_blank(), # x軸の補助目盛線
    panel.grid.minor.y = element_blank(), # y軸の補助目盛線
    panel.border = element_blank(), # グラフ領域の枠線
    #panel.background = element_blank(), # グラフ領域の背景
    plot.title = element_text(color = "black", face = "bold", size = 20, hjust = 0.5), # 全体のタイトル
    plot.subtitle = element_text(color = "black", size = 15, hjust = 0.5), # 全体のサブタイトル
    plot.margin = margin(t = 10, r = 20, b = 10, l = 120, unit = "pt"), # 全体の余白
    legend.position = "none" # 凡例の表示位置
  ) + # 図の体裁
  labs(
    title = "ハロプログループの最小活動月数", 
    #title = "ハロプログループの最大活動月数", 
    subtitle = paste0(lubridate::year(date_val), "年", lubridate::month(date_val), "月1日時点"), 
    y = "年数", 
    caption = "データ:「https://github.com/xxgentaroxx/HP_DB」"
  ) # ラベル
graph

 y軸(横軸)の値は、データ上では月数です。これを年数に対応させて表示します。scale_y_continuous()breaks引数に目盛位置、labels引数に目盛ラベルを指定します。年数を使って目盛位置を指定するには、値を12倍して月数に変換する必要があります。
 最小活動月数か最大活動月数かによって、タイトル(labs()title引数の文字列)をコメントアウトで切り替えます。

 ggsave()で画像を保存できます。

# 画像を保存
ggplot2::ggsave(
  filename = paste0("output/Exp_MinMax_", date_val, ".png"), plot = graph, 
  width = 24, height = 18, units = "cm", dpi = 100
)

最短メンバーのグラフ

最短メンバーのグラフ

最長メンバーのグラフ

最長メンバーの推移


 以上で、最小・最大活動月数の推移を可視化できました。

おわりに

 末っ子メンバーの方は、一般からの加入があると0か月になるので、オーディションからの経過月数の推移でもあります。

 そして、投稿日の2022年6月29日は、モーニング娘。'22とJuice=Juiceの新メンバーの発表日です!どきどきわくわく。