$30 off During Our Annual Pro Sale. View Details »

「国と音楽」 ~spotifyrを用いて~ #muana

bob3bob3
October 28, 2023

「国と音楽」 ~spotifyrを用いて~ #muana

SpotifyのWebAPIから取得できるデータを使い、国ごとに流行っている曲の傾向やグループ分けを行ってみた。
#muana #R言語 #rstats

bob3bob3

October 28, 2023
Tweet

More Decks by bob3bob3

Other Decks in Science

Transcript

  1. 音楽と国

    ~spotifyrを用いて~

    Music×Analytics Meetup Vol.11 

    (2023/10/28)

    @bob3bob3


    View Slide

  2. Spotify


    View Slide

  3. Spotifyとは?

    Spotify(スポティファイ)は、スウェーデンの
    企業スポティファイ・テクノロジーによって運
    営されている音楽ストリーミングサービス。

    (Wikipediaより引用)


    View Slide

  4. spotifyr

    ● R言語のspotifyrパッケージを使って、SpotifyのWeb APIから楽曲や
    アルバム、アーティストなどの情報を一括で取得。

    ● spotifyrで取得できる情報の一例

    ○ アルバム単位

    ■ ジャケット画像、曲数、アルバム名、リリース日、人気度など

    ○ アーティスト単位

    ■ ジャンル、関連するアーティスト、人気度など

    ○ 楽曲単位

    ■ アコースティック度、ダンス度、インスト度、音圧、テンポ、キー、人気度な
    ど

    ○ 詳しくはspotifyのwebAPIのページを参照のこと。


    View Slide

  5. プレイリスト
    ● Spotifyにはプレイリストという機能が
    あります。好きな曲をまとめて公開す
    る機能です。
    ● Spotify公式のプレイリストもあって、
    例えば各国のTop50がプレイリストと
    して公開されていたりします。
    ● これもspotifyrで扱えて、プレイリスト
    ごとに含まれる楽曲のデータなどを抽
    出できます。

    View Slide

  6. やってみよう!
    ● 「各国のTop50のプレイリストから、
    含まれる楽曲を抽出し、各楽曲の
    特徴を用いて各国をクラスタリング
    する。」というのをやってみます。
    ● 事前準備としてSpotifyのアカウント
    と開発者アカウントの登録が必要で
    す。
    ● 2023年07月14日時点のデータで
    す。

    View Slide

  7. # パッケージ読み込み
    library(conflicted) #関数の衝突防止
    library(tidyverse) #モダンなデータ処理
    library(spotifyr) #Spotifyの Web API 操作
    # 開発者アカウント認証
    Sys.setenv(SPOTIFY_CLIENT_ID = 'xxxxxxxxxx')
    Sys.setenv(SPOTIFY_CLIENT_SECRET = 'xxxxxxxxxx')
    access_token <- get_spotify_access_token()
    準備

    View Slide

  8. # プレイリスト検索
    res_search <- search_spotify(
    q = 'top50',
    type = 'playlist',
    limit = 50
    ) |>
    dplyr::filter(owner.id == "spotify") |> #公式プレイリストに絞る
    select(name, id) |> #列を絞る
    mutate(name = name |> str_remove("Top 50 - ")) |> #プレイリスト名の整形
    dplyr::filter( #ノイズになる行を削除
    !(name %in% c("Global", "Greatest Hip-Hop Beats of All Time"))
    ) |>
    rowid_to_column() #ID番号を振る
    Top50のプレイリストを検索

    View Slide

  9. 検索結果 国名とプレイリストの id。

    View Slide

  10. playlists_tracks <- res_search |>
    pull(id) |>
    map( #各プレイリストに含まれる楽曲を抽出
    \(id) get_playlist_tracks(id) |> select(track.id, track.name),
    .progress = TRUE
    ) |>
    list_rbind(names_to = "rowid")
    Top50のプレイリストから各楽曲のIDを検索

    View Slide

  11. プレイリストに含まれる楽曲のリスト

    View Slide

  12. 各楽曲の分析情報を取得
    res_track <- playlists_tracks |>
    pull(track.id) |>
    unique() |>
    map(
    \(track.id) get_track_audio_features(track.id),
    .progress = TRUE
    ) |>
    list_rbind() |>
    right_join(
    playlists_tracks |>
    left_join(res_search, by = join_by(rowid)),
    by = join_by(id == track.id)
    ) |>
    mutate(
    duration_s = duration_ms / 1000, # ミリ秒を秒に
    country = as.factor(name)
    ) |>
    select(
    country, duration_s, energy, acousticness, liveness, speechiness, valence, danceability,
    tempo, id, track.name)

    View Slide

  13. 各楽曲の分析情報を取得

    View Slide

  14. 各楽曲の分析情報を取得
    ● duration_ms: 曲の長さ(ミリ秒)
    ● energy: 0~1。騒がしい曲か静かな曲か。
    ● acousticness: アコースティック度合。電気的に増幅されている程度。
    ● liveness: 0~1。ライブ音源かスタジオ音源か。
    ● speechiness: 0~1。歌ではない話し言葉の量。
    ● valence: 0~1。ポジティブさ。
    ● danceability: 0~1。踊りやすさ。
    ● tempo: テンポ(BPM)。

    View Slide

  15. 各楽曲の分析情報

    View Slide

  16. 分析情報に関する発表事例

    View Slide

  17. EDA
    library(summarytools)
    res_track |>
    select(!c(country, id, track.name)) |>
    dfSummary() |>
    summarytools::view()

    View Slide

  18. EDA
    library(GGally)
    res_track |>
    select(!c(country, id, track.name)) |>
    ggpairs(aes(alpha = 0.1))

    View Slide

  19. 国ごとの違い
    res_track2 |>
    ggplot(
    aes(
    x = reorder(
    country,
    duration_s,
    FUN = median),
    y = duration_s)) +
    geom_boxplot() +
    coord_flip() +
    labs(x = "国", y = "演奏時間(秒)") +
    theme(text = element_text(size = 12))

    View Slide

  20. クラスタリングしよう!
    # 国ごとに中央値を算出し、さらに標準化する
    median_by_country <- res_track |>
    select(!c(id, track.name)) |>
    group_by(country, .drop = FALSE) |>
    summarise(across(everything(), median)) |>
    column_to_rownames(var = "country") |>
    scale()
    # 階層型クラスタリング
    library(factoextra)
    library(dendextend)
    cluster_tree <- median_by_country |>
    dist() |>
    hclust(method = "ward.D2")
    cluster_tree |>
    fviz_dend(
    k=6, cex=0.5, horiz = TRUE, label_cols = "black",
    k_colors = c(
    "#ff4b00", "#990099", "#03af7a",
    "#005aff", "#804000", "#ff8082"),
    rect = TRUE, rect_fill = TRUE, rect_border = 8
    )

    View Slide

  21. 地図!
    clusters <- tibble(
    country = median_by_country |> rownames(),
    cluster = cluster_tree |> cutree(k=6)
    )
    library(ggrepel)
    library(sf)
    library(rnaturalearth)
    world_map <- ne_countries(
    scale = "small", returnclass = "sf"
    ) |>
    left_join(
    clusters |>
    mutate(
    country = country |>
    str_replace("USA", "United States") |>
    str_replace("South Korea", "Republic of Korea")
    ),
    by=join_by(name_long == country)
    )
    world_map |>
    ggplot() +
    geom_sf(aes(fill = as.factor(cluster))) +
    theme_light() +
    labs(fill = "cluster") +
    scale_colour_brewer(palette = "Dark2")

    View Slide

  22. View Slide

  23. Enjoy!
    次回「2010年代 King Crimson のセットリスト分析」でお会いしま
    しょう!


    View Slide