はじめに
ハロー!プロジェクトの歴史を可視化しようシリーズ(仮)です。
この記事では、MVの再生回数をバーチャートレースにします。
【他の記事】
【目次】
再生回数の可視化
YouTubeのアップフロント関連チャンネルのMV再生回数の推移をバーチャートレースで可視化します。
次のパッケージを利用します。
# 利用パッケージ library(tidyverse) library(lubridate) library(readxl) library(gganimate)
この記事では、基本的にパッケージ名::関数名()
の記法を使うので、パッケージを読み込む必要はありません。ただし、作図コードがごちゃごちゃしないようにパッケージ名を省略しているため、ggplot2
は読み込む必要があります。
magrittr
パッケージのパイプ演算子%>%
ではなく、ベースパイプ(ネイティブパイプ)演算子|>
を使っています。%>%
に置き換えても処理できます。
データの設定
次のデータを利用します。
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-dd
やyyyy/mm/dd
、yyyymmdd
などと指定できます。
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チャンネルの再生回数を毎日収集してくれている方がいらっしゃったので、使わせていただきました。ありがとうございます。
前日の再生回数が毎朝自動投稿されるアカウントもあります!
#hpytvc 昨日からの再生回数: #Berryz工房
— Hello!Project YouTube View Counter (@hello_counter) 2023年3月5日
🥇Berryz工房「流星ボーイ」 1117回
🥈Berryz工房「本気ボンバー!!」 1023回
🥉Berryz工房「ジンギスカン」 772回 pic.twitter.com/ufTu076qV3
3月6日は、ももちこと嗣永桃子さんのお誕生日です!
毎回言ってる気がしますが、ハロプロの諸々にハマる2か月前に引退されていたことが未だに悔やまれる。