からっぽのしょこ

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

ハロプロ関連チャンネルのMV再生回数の推移のバーチャートレースを作ってみた

はじめに

 ハロー!プロジェクトの歴史を可視化しようシリーズ(仮)です。
 この記事では、MVの再生回数をバーチャートレースにします。

【他の記事】

www.anarchive-beta.com

【目次】

再生回数の可視化

 YouTubeのアップフロント関連チャンネルのMV再生回数の推移をバーチャートレースで可視化します。

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

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

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

データの設定

 次のデータを利用します。

github.com

 GitHub上のexcelデータ(save.xlsx)をRから読み込む方法が分からなかったので、ダウンロードしてローカルフォルダに保存しておきます。

 保存したデータのファイルパスを指定します。

# ファイルパスを指定
file_path <- "data/save.xlsx"


アーティストごとに集計

 アーティストごとに再生回数の上位楽曲の推移を見ます。

データの読込

 シート名を指定して、再生回数の日時データを読み込みます。

# アーティスト名(シート名)を指定
artist_name <- "Berryz工房"

# データを読み込み
views_df <- readxl::read_excel(
  path = file_path, 
  sheet = artist_name, 
  col_names = TRUE, .name_repair = "unique"
) |>
  dplyr::select(!...1, title = タイトル) |> 
  dplyr::mutate(
    title = stringr::str_remove(title, pattern = artist_name)
  )
views_df
## # A tibble: 165 × 584
##    title        `2021-07-08` `2021-07-09` `2021-07-10` `2021-07-11` `2021-07-12`
##    <chr>               <dbl>        <dbl>        <dbl>        <dbl>        <dbl>
##  1 "「ジンギス…            0            0      3299138      3299150      3301848
##  2 "「あなたな…            0            0      1796110      1796129      1797244
##  3 "「スッペシ…            0            0      2826772      2826781      2828557
##  4 "「抱きしめ…            0            0       891741       891741       892023
##  5 "「付き合っ…            0            0      1705021      1705025      1705612
##  6 "「ハピネス…            0            0        57163        57163        57199
##  7 "「雄叫びボ…            0            0      1885305      1885311      1886344
##  8 "「恋の呪縛…            0            0      1265620      1265625      1266067
##  9  <NA>                   0            0       268024       268025       268109
## 10 " 『あなた…             0            0       195643       195645       195930
## # … with 155 more rows, and 578 more variables: `2021-07-13` <dbl>,
## #   `2021-07-14` <dbl>, `2021-07-15` <dbl>, `2021-07-16` <dbl>,
## #   `2021-07-17` <dbl>, `2021-07-18` <dbl>, `2021-07-19` <dbl>,
## #   `2021-07-20` <dbl>, `2021-07-21` <dbl>, `2021-07-22` <dbl>,
## #   `2021-07-23` <dbl>, `2021-07-24` <dbl>, `2021-07-25` <dbl>,
## #   `2021-07-26` <dbl>, `2021-07-27` <dbl>, `2021-07-28` <dbl>,
## #   `2021-07-29` <dbl>, `2021-07-30` <dbl>, `2021-07-31` <dbl>, …

 再生回数データをreadxlパッケージのread_excel()で読み込みます。

再生回数の集計

 アニメーションとしてグラフ化する(再生回数を集計する)期間を指定します。

# 期間を指定
date_from <- "2023-01-01"
date_to   <- "2023-01-31"

# 表示する日付を作成
date_vec <- seq(
  from = date_from |> 
    lubridate::as_date(), 
  to = date_to |> 
    lubridate::as_date(), 
  by = "day" # 間隔を指定
)
head(date_vec)
## [1] "2023-01-01" "2023-01-02" "2023-01-03" "2023-01-04" "2023-01-05"
## [6] "2023-01-06"

 開始日をdate_from、終了日をdate_toとして期間を指定します。文字列でyyyy-mm-ddyyyy/mm/ddyyyymmddなどと指定できます。
 date_fromからdate_toの日付データをseq()で作成します。by引数に日付の間隔を指定します。

総再生回数

 MV公開時からの総再生回数の推移を可視化します。

 グラフに表示する最大順位(曲数)を指定して、日付ごとに総再生回数が多い順に順位を付けます。

# グラフに表示する楽曲数(順位)を指定
max_rank <- 30

# 総再生回数を集計
rank_df <- views_df |> 
  dplyr::mutate(
    music_id = dplyr::row_number() |> 
      factor() # 楽曲番号
  ) |> 
  tidyr::pivot_longer(
    cols = !c(title, music_id), 
    names_to = "date", # 記録日
    names_transform = list(date = lubridate::as_date), 
    values_to = "count" # 再生回数
  ) |> 
  dplyr::filter(date %in% date_vec) |> # 期間内のデータを抽出
  dplyr::filter(count > 0) |> # 公開前・未集計のデータを除去
  dplyr::arrange(date, -count, music_id) |> # 確認用
  dplyr::group_by(date) |> # ランク付け用
  dplyr::mutate(
    rank = dplyr::row_number(-count) # 再生回数ランキング
  ) |> 
  dplyr::ungroup() |> 
  dplyr::filter(rank <= max_rank) # 上位楽曲を抽出
rank_df
## # A tibble: 870 × 5
##    title                              music_id date         count  rank
##    <chr>                              <fct>    <date>       <dbl> <int>
##  1 " 『cha cha SING』"                14       2023-01-01 5928151     1
##  2 "「本気ボンバー!!」"               96       2023-01-01 4008560     2
##  3 "「流星ボーイ」"                   95       2023-01-01 3948225     3
##  4 "「ジンギスカン」"                 1        2023-01-01 3704406     4
##  5 "「スッペシャル ジェネレ~ション」" 3        2023-01-01 3218755     5
##  6 " 『WANT!』"                       21       2023-01-01 3127991     6
##  7 " 『Loving you Too much』"         28       2023-01-01 3023105     7
##  8 " 『アジアン セレブレイション』"   17       2023-01-01 2978037     8
##  9 " 『Be 元気"                       19       2023-01-01 2604359     9
## 10 "「ヒロインになろうか!」"          18       2023-01-01 2233557    10
## # … with 860 more rows

 row_number()で、楽曲番号を割り当てます。
 pivot_longer()で、日ごとの再生回数列をまとめて、列名を記録日列に変換します。
 指定した期間内のデータ(date列がdate_from以上date_to以下の行)を抽出して、未記録日のデータ(count列が0の行)を除去します。
 row_number()で、再生回数が多い順にランキングを付けて、指定した順位以上のデータ(rank列がmax_rank以上の行)を抽出します。

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

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

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

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

# 終了時の停止フレーム数を指定
tmp_fps <- (t + s) * 3 # 1秒当たりのフレーム数
e <- tmp_fps * 3

# 描画日数を取得
n <- rank_df[["date"]] |> 
  unique() |> 
  length()
n
## [1] 29

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

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

# バーチャートレースを作成
graph <- ggplot(data = rank_df, mapping = aes(x = rank, y = count, color = music_id, fill = music_id)) + 
  geom_bar(stat = "identity", width = 0.8) + # 再生回数バー
  geom_text(aes(y = 0, label = paste(title, " ")), hjust = 1) + # 曲名ラベル
  geom_text(aes(label = paste(" ", format(count, big.mark = ",", scientific = FALSE), "回")), hjust = 0) + # 再生回数ラベル
  gganimate::transition_states(states = date, transition_length = t, state_length = s, wrap = FALSE) + # フレーム
  gganimate::ease_aes("cubic-in-out") + # アニメーションの緩急
  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_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 = 100, b = 10, l = 200, unit = "pt"), # 全体の余白
    legend.position = "none" # 凡例の表示位置
  ) + # 図の体裁
  coord_flip(clip = "off", expand = FALSE) + # 軸の入れ変え
  labs(
    title = paste0(artist_name, "のMV再生回数"), 
    subtitle = "{lubridate::year(closest_state)}年{lubridate::month(closest_state)}月{lubridate::day(closest_state)}日", "時点", 
    caption = "データ:「https://github.com/yayoimizuha/youtube-viewcount-logger-python」"
  )

 transition_states()に日付列を指定して、横向きの棒グラフを作成します。

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

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

総再生回数の推移

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

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

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

 filename引数に保存先のファイルパス、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/Veiws.mp4")
)

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

期間中の再生回数

 期間開始日からの再生回数の推移を可視化します。

 日付ごとに再生回数が多い順に順位を付けます。

# グラフに表示する楽曲数(順位)を指定
max_rank <- 30

# 縦型に変換
long_df <- views_df |> 
  dplyr::mutate(
    music_id = dplyr::row_number() |> 
      factor() # 楽曲番号
  ) |> 
  tidyr::pivot_longer(
    cols = !c(title, music_id), 
    names_to = "date", # 記録日
    names_transform = list(date = lubridate::as_date), 
    values_to = "count" # 再生回数
  )

# 期間中の再生回数を集計
rank_df <- long_df |> 
  dplyr::group_by(title, music_id) |> # 日付の補完用
  dplyr::summarise(
    date = seq(from = min(date), to = max(date), by = "day"), .groups = "drop"
  ) |> # 欠損記録日を補完
  dplyr::left_join(
    long_df |> 
      dplyr::select(music_id, date, count), 
    by = c("music_id", "date"), 
  ) |> # 再生回数列が落ちるので戻す
  dplyr::arrange(music_id, date) |> # 1日当たりの再生回数の計算用
  dplyr::group_by(music_id) |> # 1日当たりの再生回数の計算用
  dplyr::mutate(
    count = tidyr::replace_na(count, replace = 0), # 補完した日付の再生回数を0に置換
    count = cummax(count), # 再生回数が0なら前日の回数に変更
    count_before = dplyr::lag(count, n = 1, default = NA), # 前日の再生回数列を追加
    count_before = dplyr::if_else(
      is.na(count_before)|count_before == 0, true = count, false = count_before
    ), # 前日の記録がなければ同じ再生回数に変更
    diff = count - count_before # 1日当たりの再生回数
  ) |> 
  dplyr::filter(date >= min(date_vec), date <= max(date_vec)) |> # 期間中の再生回数の計算用
  dplyr::mutate(
    sum_diff = cumsum(diff) # 期間中の累積再生回数
  ) |> 
  dplyr::filter(date %in% date_vec) |> # 描画する日のデータを抽出
  dplyr::filter(count > 0) |> # 公開前のデータを除去
  dplyr::arrange(date, -sum_diff, music_id) |> # 念のため
  dplyr::group_by(date) |> # ランク付け用
  dplyr::mutate(
    rank = dplyr::row_number(-sum_diff) # 再生回数ランキング
  ) |> 
  dplyr::ungroup() |> 
  dplyr::filter(rank <= max_rank) # 上位楽曲を抽出
rank_df
## # A tibble: 930 × 8
##    title            music_id date        count count_before  diff sum_diff  rank
##    <chr>            <fct>    <date>      <dbl>        <dbl> <dbl>    <dbl> <int>
##  1 "「流星ボーイ」" 95       2023-01-01 3.95e6      3946820  1405     1405     1
##  2 "「本気ボンバー… 96       2023-01-01 4.01e6      4007446  1114     1114     2
##  3 "「雄叫びボーイ… 7        2023-01-01 2.22e6      2219479   598      598     3
##  4 "「スッペシャル… 3        2023-01-01 3.22e6      3218200   555      555     4
##  5 "「ジンギスカン… 1        2023-01-01 3.70e6      3703855   551      551     5
##  6 " 『cha cha SIN… 14       2023-01-01 5.93e6      5927600   551      551     6
##  7 "「あなたなしで… 2        2023-01-01 2.13e6      2128400   460      460     7
##  8 "「青春バスガイ… 16       2023-01-01 1.57e6      1566176   408      408     8
##  9 "『青春バスガイ… 131      2023-01-01 9.05e5       904542   397      397     9
## 10 "「シャイニング… 34       2023-01-01 1.24e6      1241506   320      320    10
## # … with 920 more rows

 曲ごとに(title, music_id列でグループ化して)、summarise()で最初の日付から最後の日付までの全ての日付情報を作成します。再生回数の情報が欠損するのでleft_join()で追加します。

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

# 描画日数を取得
n <- rank_df[["date"]] |> 
  unique() |> 
  length()

# バーチャートレースを作成
graph <- ggplot(data = rank_df, mapping = aes(x = rank, y = sum_diff, color = music_id, fill = music_id)) + 
  geom_bar(stat = "identity", width = 0.8) + # 再生回数バー
  geom_text(aes(y = 0, label = paste(title, " ")), hjust = 1) + # 曲名ラベル
  geom_text(aes(label = paste(" ", format(count, big.mark = ",", scientific = FALSE), "回")), hjust = 0) + # 再生回数ラベル
  gganimate::transition_states(states = date, transition_length = t, state_length = s, wrap = FALSE) + # フレーム
  gganimate::ease_aes("cubic-in-out") + # アニメーションの緩急
  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_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 = 100, b = 10, l = 200, unit = "pt"), # 全体の余白
    legend.position = "none" # 凡例の表示位置
  ) + # 図の体裁
  coord_flip(clip = "off", expand = FALSE) + # 軸の入れ変え
  labs(
    title = paste0(artist_name, "のMV再生回数"), 
    subtitle = paste0(
      format(lubridate::as_date(date_from), format = "%Y年%m月%d日"), "~", 
      "{lubridate::year(closest_state)}年{lubridate::month(closest_state)}月{lubridate::day(closest_state)}日", "時点"
    ), 
    caption = "データ:「https://github.com/yayoimizuha/youtube-viewcount-logger-python」"
  )

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

累積再生回数の推移

 「総再生回数」と同様に処理します。

複数アーティストを集計

 執筆中です…

おわりに

 久しぶりになってしました。
 これまでのハロプロデータベースとはまた別に、アップフロント関連のYouTubeチャンネルの再生回数を毎日収集してくれている方がいらっしゃったので、使わせていただきました。ありがとうございます。

 前日の再生回数が毎朝自動投稿されるアカウントもあります!


 3月6日は、ももちこと嗣永桃子さんのお誕生日です!

 毎回言ってる気がしますが、ハロプロの諸々にハマる2か月前に引退されていたことが未だに悔やまれる。