日米のSpotifyにおけるヒットチャートの動向の差異

21EC001 青木杜馬

目次

1. 序論

1.1 研究背景と目的と目的

近年、音楽市場は定額ストリーミングサービス(以下、サブスク)の普及により急速に変化している。世界の音楽市場では、CDやレコードなどのフィジカル媒体が減少し、デジタル配信やストリーミングサービスが主要な収益源となっている。世界の音楽業界の収益推移を図1.1に示す。

一方で、日本の音楽市場においてもサブスクの利用は拡大しているが、依然としてCD売上が収益の大部分を占めている。これは、音楽市場規模で世界1位のアメリカが収益の約8割をサブスクで得ている状況と対照的である。Spotify Chartsのウィークリーチャートの各週の総再生回数の推移を図1.2、2023年の日本の音楽収益割合を図1.3、アメリカの音楽収益割合を図1.4に示す。

このように、日本は世界第2位の音楽市場を持ちながら、収益構造においてアメリカとは顕著な違いが見られる。

本研究では「日本とアメリカの音楽市場におけるヒット曲の特性」を比較・分析することで、両国の市場構造や音楽消費の特徴を明らかにすることを目的とする。

分析には、Spotifyが提供する「Spotify Charts」のデータを用い、2017年8月から2024年8月までの約7年分のウィークリーチャートを対象とする。

世界の音楽業界の収益推移

図1.1 世界の音楽業界の収益推移 1999-2023(US$billions)
出典:IFPI | GLOBAL MUSIC REPORT 2024

ウィークリーチャートの合計再生回数推移

図1.2 ウィークリーチャートの合計再生回数推移

日本の音楽収益

図1.3 日本の音楽収益割合(2023)
出典:RIAJ | The Recoding Industry in Japan 2024

アメリカの音楽収益

図1.4 アメリカの音楽収益割合(2023)
出典:RIAA | 2023 Year-End Music Industry Revenue Report

2. 準備

2.1 Spotify

Spotifyとは、数千万もの音楽やポッドキャスト、ビデオを楽しめる世界最大手の音楽配信サービスである。音楽再生などの基本機能は無料であるが、Spotify Premiumにアップグレードすることもできる。

Spotify ホーム画面

図2.1 Spotify ホーム画面

2.2 Spotify Charts

Spotify Chartsとは、Spotifyが提供している再生回数を元にしたランキングである。73か国と地域、さらにそれらを合算したグローバルチャートが含まれている。デイリーチャートは通常、チャート期間の翌日に東部標準時(EST)午後6時(協定世界時(UTC)午後10時)より前に公開され、ウィークリーチャートは、すべてのタイムゾーンがウィークリーチャートの期間を迎えた後に発表される。ウィークリーチャートの期間は金曜日に始まり、次の木曜日に終わる。

Spotify Chartsでは、ウィークリーチャートやデイリーチャートなどの複数の種類のチャートが存在し、これらのチャートは、Spotifyのアカウントを有していれば、Spotify Charts(https://charts.spotify.com)にて無料で閲覧できる。また、Weekly Top Songs, Daily Top Songs, Daily Viral Songsはcsv形式で取得することができる。

Spotify Charts ホーム画面

図2.2 Spotify Charts ホーム画面

2.3 Zipfの法則(ジップの法則)

ジップの法則とは、アメリカの言語学者のジョージ・キングズリー・ジフが発見した経験則であり、サイズや頻度の大きさで順位kを付けた場合、k番目のサイズは、1番目のサイズの1/kに比例するとされるものである。この法則は、自然言語処理の分野では単語の出現頻度の分布として知られており、最も頻出する単語の出現回数が2番目の単語の約2倍、3番目の単語の約3倍になることが観測される。

本研究では、Spotify Chartsの順位ごとの平均再生回数がジップの法則に従うかを検証した。

2.4 確率密度関数

確率密度関数とは、確率、または確率密度を計算する関数である。連続確率変数Xについて、ある関数y=f(x)が下式を満たすとき、f(x)はXについての確率密度関数となる。 \[ f(x) \geq 0, \quad \int_{-\infty}^{\infty} f(x)\, dx = 1 \]

このとき、確率変数Xについて、以下のように表現できる。 \[ P(a \leq x \leq b) = \int_{a}^{b} f(x) \,dx \]

例えば、金融データではリターンの分布、医学では患者の体温の分布、機械学習では特徴量の分布が解析対象となる。

本研究では、順位ごとの次週順位の分布を解析する際に、この確率密度関数を推定するための手法としてカーネル密度推定を採用した。

2.5 カーネル密度推定

カーネル密度推定(Kernel Density Estimation, KDE)とは、確率密度関数を推定するための非パラメトリック手法である。カーネル密度推定では、以下のようにして確率密度関数f(x)を推定する。\[ f(x) = \frac{1}{n h} \sum_{i=1}^{n} K\left(\frac{x - x_i}{h}\right) \]

ここで、

カーネル関数は、各データ点周辺の密度を滑らかにするための関数であり、以下のようなものが使用される。

バンド幅は推定結果に大きく影響を与え、バンド幅が小さいと分布が細かくなり、バンド幅が大きいと分布が滑らかになる。

本研究では、Spotify Chartsの順位データを基に、次週順位を非パラメトリックに推定し、楽曲の順位変動の傾向を把握した。Pythonのデータ分析ライブラリであるseabornを用いて、この推定を実行した。Pythonのseaborn.kdeplot()では、デフォルトでバンド幅を調整するアルゴリズムが適用される。

3. データの概要と収集方法

3.1 データの概要

本研究では、日本およびアメリカのSpotifyウィークリーチャートデータを対象とし、2017年8月25日から同年8月30日までの集計分を起点とし、2024年8月23日から同年8月29日までの集計分に至る、約7年間にわたるデータを使用した。このデータは、Spotify公式サイト(https://charts.spotify.com)で提供されており、各週のトップ200位のランキングが記録されている。

Spotify Chartsのcsvファイルに含まれる要素を表1に示す。

表1 Spotify Charts 取得ファイルの要素と説明
要素 説明
rank 順位
uri 楽曲の一意識別子
artist_name アーティスト名
song_name 曲名
source レーベル名
peak_rank 最高順位
previous_rank 先週の順位
weeks_on_chart 連続チャートイン数
streams 再生回数

3.2 収集方法

Spotify Chartsのウィークリーチャートは、Spotify公式サイト(https://charts.spotify.com)から、各国・地域ごとにcsv形式でダウンロードできる。本研究では、日本およびアメリカのチャートを選択し、各週のデータを取得した。

以下に、データ収集の具体的な手順を示す。

  1. Spotify Charts公式サイト(https://charts.spotify.comにアクセスする。
  2. 右上の「log in」ボタンをクリックし、Spotifyアカウントでログインする。
  3. サイト上部の「Global」タブを選択肢、データを取得したい国を選択する。
  4. 「Weekly Top 200」を選択する。
  5. データを取得したい週を指定し、表の右上に表示される「ダウンロードアイコン(↓)」をクリックする。
  6. ダウンロードされたcsvファイルは「regional-国名-weekly-年-月-日.csv」の形式で保存される。

4. 現状

4.1 年間チャートイン曲数の推移

取得したウィークリーチャートを用いて、2018年から2023年の年間チャートイン曲数の推移を図3に示す。

年間チャート数

図3 年間チャートイン曲数推移

図3より、日本の年間チャートイン曲数は、2018年の830曲から2023年の624曲へと徐々に減少しており、楽曲の入れ替わりが少ない傾向が見られる。

アメリカでは、年間チャート曲数が2018年から2020年にかけて増加した後、2021年以降は減少傾向にある。しかし、日本よりも多くの曲がチャートインしており、楽曲の入れ替わりが比較的活発であるといえる。

5. 分析概要と結果

5.1 緒言

本研究では、日本とアメリカのSpotify Chartsのウィークリーチャートを用いた分析を行った。データ処理と分析にはPythonを使用し、プログラムコードは付録に記載する。

5.2 データの収集と前処理

本研究で使用したデータは以下の手順でデータの収集と前処理を行った。

  1. 期間内のウィークリーチャートの取得
    ウィークリーチャート例
    図4.1 取得したウィークリーチャートの例
  2. 国ごとのデータ分類
  3. データの整形(列の並び替え)
    列の並び替え
    図4.2 列を並び替えたCSVファイルの例
  4. 曲ごとのデータ結合
    処理後のチャート
    図4.3 処理後のチャートの例

5.3 順位ごとの平均変動幅

5.3.1 概要

本分析では、Spotify Chartsのウィークリーチャートを用いて、順位ごとに次週順位と平均変動幅を算出し、可視化を行い日本とアメリカのチャートにおける楽曲の安定性と動向の違いを明らかにすることを目的とする。

5.3.2 手法

以下の手順で分析を行った。

  1. 各週のSpotify Chartsから、次週順位との差の絶対値を算出した。
  2. 算出した値の順位ごとの平均を求めた。
  3. 日本とアメリカの順位ごとの平均変動幅を可視化し、特徴の違いを確認した。
  4. 最も決定係数の高い近似式を採用し、順位ごと平均変動幅をモデル化した。
5.3.3 結果

各国の順位ごとの平均変動幅を可視化したグラフを図4.1に示す。

平均変動幅

図4.1 日米の順位ごとの平均変動幅分布

図4.1は、日本とアメリカごとの平均変動幅を同一のグラフに示したである。また、図4.2および図4.3は、それぞれ日本とアメリカのデータを独立して示し、最も適した近似式をプロットしたものである。

日本の結果1

図4.2 日本の順位ごと平均変動幅と累乗近似

アメリカの結果1

図4.3 アメリカの平均変動幅と対数近似

図4.2より、日本では順位が低くなるにつれ変動幅が大きくなる傾向が見られたが、アメリカは全体的に変動幅が大きく、特に下位の楽曲ほど変動が激しいことがわかる。表2.1に算出した近似式および決定係数を示す。

表2.1 近似式と決定係数
近似式 決定係数
日本 \[ y = 5.92 e-01 \times x^{0.637} \] 0.8985
アメリカ \[ y = 3.42 \ln(x) + 1.58 \] 0.8236

5.4 順位ごとの平均再生回数

5.4.1 概要

本分析では、Spotify Chartsのウィークリーチャートを用いて、順位ごとの再生回数の平均を算出し、日本とアメリカのチャートの特性を比較した。再生回数は順位が低くなるにつれて減少する傾向があるが、その減少の度合いが国ごとに違いが生じるのかを明らかにすることを目的とする。

5.4.2 手法

以下の手順で分析を行った。

  1. 各週Spotify Chartsから、順位ごとの再生回数を抽出した。
  2. 順位ごと平均再生回数を算出し、グラフを描画し可視化した。
  3. 順位ごとの平均再生回数がジップの法則に従うかを検証した。順位xと再生回数yの関係をy = ax-sの形式でモデル化し、最小二乗法を用いてパラメータasを推定した。さらに、データの適合性を確認するため、対数スケールのプロットを作成した。
  4. 最も決定係数が高い近似式を採用し、再生回数の傾向をモデル化した。特に、ジップの法則が適用可能な順位を明確にするため、相対誤差の計算を行い、その結果をもとにモデルの妥当性を評価した。
5.4.3 結果

図4.4および図4.5は、それぞれ日本とアメリカの順位ごとの平均再生回数を示したものである。図4.6および図4.7は、同データを両対数グラフに変換し、近似式をプロットしたものである。

日本の結果2

図4.4 日本の順位ごと平均再生回数

アメリカの結果2

図4.5 アメリカの順位ごと平均再生回数

日本の結果3

図4.6 日本の順位ごとの平均再生回数と近似式(両対数)

アメリカの結果3

図4.7 アメリカの順位ごとの平均再生回数と近似式(両対数)

表2.2に算出した近似式と決定係数を示す。

表2.2 近似式と決定係数
近似式 決定係数
日本 \[ y = 2.08 e \times 06 x^{-0.435} \] 0.9114
アメリカ \[ y = 1.61 e \times 07 x^{-0.411} \] 0.9864

これらの結果より、日本とアメリカの特徴として、再生回数の分布や楽曲の人気の集中度に差異があることがわかった。日本では指数が-0.435であり、再生回数が上位の楽曲に集中しているのに対し、アメリカでは指数は-0.411と低く、より多くの楽曲が均等に再生されていることがわかった。また、ジップの法則が適用可能な順位範囲においても、日本は61位に対しアメリカは200位までが適合する結果になった。

5.5 次週順位確率密度関数

5.5.1 概要

本分析は、Spotify Chartsのウィークリーチャートを用いて、カーネル密度推定を活用して、順位ごとの次週順位の確率密度関数を算出した。さらに、この分布を基に、日本とアメリカのチャート特性の違いを比較した。

5.5.2 手法

以下の手順で分析を行った。

  1. Spotify Chartsから、各順位が次週に何位になるのかを記録したデータを作成した。
  2. 各順位についての次週順位の確率密度関数をカーネル密度推定を用いて算出して、グラフで可視化した。
  3. 標準化された確率密度関数を重ねることで、類似性を可視化した。
  4. 分布の形状から、次週順位分布を2つのグループ(上位・下位)に分類し、さらにグラフを作成した。
5.5.3 結果

各順位の確率密度関数を重ねた日本のグラフを図4.8、アメリカのグラフを図4.9に示す。また、標準化を行い重ねた日本のグラフを図4.10、アメリカのグラフを図4.11に示す。

確率密度

図4.8 順位ごとの確率密度関数

確率密度2

図4.9 順位ごとの確率密度関数

日本の確率密度

図4.10 標準化を行った確率密度関数 日本

アメリカの確率密度

図4.11 標準化を行った確率密度関数 アメリカ

図4.10および図4.11より、標準化された分布には大きく2つの山が見られた。これにより分布には2つの特徴的なパターンが存在することがわかった。この観察結果を基に、次週順位分布を2つのグループに分類する試みを行った。図4.12と図4.13に日本の分布を上位175曲と下位25曲で分類したグラフを示す。図4.14と図4.15にアメリカの分布を上位165曲と下位35曲で分類したグラフを示す。

日本の上位

図4.12 上位175曲の確率密度関数 日本

日本の下位

図4.13 下位25曲の確率密度関数 日本

アメリカの上位

図4.14 上位165曲の確率密度関数 アメリカ

アメリカの下位

図4.15 下位25曲の確率密度関数 アメリカ

図4.12より、日本の1位から175位までの楽曲の確率密度関数のピークはわずかに左側に位置しており、これらの楽曲は次週に順位を上げる傾向があることがわかった。

図4.13より、日本の176位から200位までの楽曲の確率密度関数のピークはわずかに右側に位置しており、これらの楽曲は次週に順位を下げる傾向があることがわかった。

図4.14より、アメリカの1位から165位までの楽曲の確率密度関数のピークは日本と同様にわずかに左側に位置しており、これらの楽曲は次週順位を上げる傾向があることがわかった。

図4.15より、アメリカの166位から200位までの楽曲の確率密度関数はピークも日本と同様にわずかに右側に位置しており、これらの楽曲は次週に順位を下げる傾向があることがわかった。

6. 考察

6.1 順位ごとの平均変動幅

日本とアメリカのウィークリーチャートにおける順位ごとの平均変動幅を算出し、可視化を行った。日本では、上位から下位にかけて緩やかに変動幅が増加する傾向がみられた。

一方で、アメリカでは、日本に比べて全体的に変動幅が大きいことがわかった。また、チャートの観察を通じて、アメリカのチャートではテイラー・スウィフトなどの有名アーティストがアルバムをリリースすると、アルバム収録曲が一斉にチャート上位を占めることある点が印象的だった。この現象は、アルバムリリースがチャートの動きに与える影響を示している可能性がある。日本のチャートでは、こうしたアルバム単位の一斉ランクインはあまり見られないことから、両国の音楽市場の特性の違いが反映されているのではないかと考えられる。

6.2 順位ごとの平均再生回数

日本とアメリカそれぞれの順位ごとの平均再生回数を算出し、両対数グラフに変換し可視化を行った結果、日本とアメリカともに順位が低くなるにつれて再生回数が減少し、Zipfの法則に従う傾向が見られた。図4.4および図4.6を詳細に観察すると、日本では上位50位付近で再生回数に明確な差が見られ、50位以内の楽曲はそれ以下の楽曲と比べて再生回数が顕著に高い傾向があることがわかる。この結果は、日本のランキングにおいて「上位楽曲の独占的な人気」が存在していることを示していることがわかる。

これに対し、アメリカでは順位に応じた再生回数の減少が比較的滑らかであり、特定の順位で再生回数が大きく変化するような顕著な差は見られない。この点から、アメリカのランキングでは幅広い楽曲が聴かれる傾向が示唆される。すなわち、日本の市場では上位50位以内に「ヒット曲」が集中する一方で、アメリカでは再生回数がより多様な楽曲に分散していると考えられる。

6.3 次週順位確率密度関数

日本とアメリカの各順位の次週順位の分布にカーネル密度推定を用いて分析を行った。日本とアメリカともに標準化した分布において大きく分けて2パターンの傾向があることがわかった。日本のランキングは特定の順位に曲が固定されやすく、安定性が高いことが分かります。アメリカでは順位変動の幅が大きく、市場の流動性が高いといえます。この結果は、日米の音楽市場における流行の持続性や消費者の嗜好の違いを反映している可能性がある。

7. おわりに

本研究では、Spotifyのウィークリーチャートのデータを基に、日本とアメリカの音楽市場におけるランキング動向を比較と分析を行った。順位ごとの平均変動幅、平均再生回数、次週順位確率密度関数という3つの観点から、それぞれの市場の特性を明らかにした。

参考文献

付録A

本研究で使用したプログラムコードを載せ、説明を行う。

A.1 データの前処理に用いたコード

A.1.1 データの整形(列の入れ替え)で使用したコード

Spotifyのチャートデータを、使用しやすいようにデータの整形を行った。本処理では、以下のコードを使用して、指定された列順に並び替え新たに保存した。

    import pandas as pd
    import os

    # CSVファイルが入ったフォルダーのパスを指定
    folder_path = "./"

    # すべてのCSVファイルを取得
    csv_files = [f for f in os.listdir(folder_path) if f.endswith('.csv')]

    # 出力フォルダーのパスを指定
    output_folder_path = os.path.join(folder_path, "ordered")

    # 出力フォルダーが存在しない場合は作成
    os.makedirs(output_folder_path, exist_ok=True)

    # 列の並び順を指定
    desired_order = ['uri', 'rank', 'artist_names', 'track_name', 'streams', 'weeks_on_chart', 'source', 'peak_rank', 'previous_rank']

    # 各CSVファイルを読み込み、列を並び替え
    for file in csv_files:
      file_path = os.path.join(folder_path, file)
    
      try:
          # CSVファイルを読み込み(エンコーディングを指定)
          df = pd.read_csv(file_path, encoding='utf-8')
        
         # 1行目の値を読み込み、列の順序を指定された順序に並び替え
          ordered_df = df[desired_order]
        
          # 並び替えたデータフレームを新しいファイル名で保存
          ordered_file_path = os.path.join(output_folder_path, f"ordered_{file}")
         ordered_df.to_csv(ordered_file_path, index=False, encoding='utf-8-sig')
          print(f"Columns in {file} reordered and saved to {ordered_file_path}")
        
      except Exception as e:
        print(f"Failed to process {file_path}: {e}")

    print("Processing complete.")
  
A.1.2 曲ごとのデータ統合に使用したプログラムコード

以下は、フォルダー内のウィークリーチャートcsvファイルを読込み、曲ごとに統合したフレームを作成するコードである。このコードは、各曲の順位と再生回数を週ごとに統合した形で出力する。

  def FtoF(folder_path):
    import os
    import pandas as pd

    files = os.listdir(folder_path)  # フォルダ内のファイルリスト
    result = []  # 結果を格納するリスト

    for file_index, file in enumerate(files):
        try:
            file_path = os.path.join(folder_path, file)
            df = pd.read_csv(file_path)

            # A列からF列を抽出
            data_columns = df.iloc[:, :6].values

            for row in data_columns:
                uri = row[0]
                rank = row[1]
                artist_name = row[2]
                song_name = row[3]
                play_count = row[4]

                found = False
                for res in result:
                    if res[1] == artist_name and res[2] == song_name:
                        position = 3 + file_index * 2

                        while len(res) <= position + 1:
                            res.append('')

                        res[position] = rank
                        res[position + 1] = play_count
                        res[0] = uri  # URIを更新
                        found = True
                        break

                # 新しいエントリを追加
                if not found:
                    new_entry = [uri, artist_name, song_name] + [''] * (file_index * 2)
                    new_entry.extend([rank, play_count])
                    result.append(new_entry)

        except Exception as e:
            print(f"Failed to open or process {file}: {e}")

    if result:
        max_columns = max(len(res) for res in result)  # 最大列数を計算

        # 列名を生成
        columns = ['uri', 'artist_names', 'track_name'] + \
                  [f'rank{i // 2 + 1}' if i % 2 == 0 else f'streams{i // 2 + 1}' for i in range(max_columns - 3)]

        # 各行の列数を最大列数に揃える
        for res in result:
            while len(res) < max_columns:
                res.append('')

        # DataFrameに変換
        result_df = pd.DataFrame(result, columns=columns)
        result_df.columns = result_df.columns.str.lower()
        return result_df

    print("結果が空です。")
    return pd.DataFrame()  # 空のDataFrameを返す
  
A.2.2 順位ごとの次週順位をまとめるコード

このコードは、各週のチャート順位データをもとに、ある順位が次週にどの順位になるのかを集計するためのコードである。

  def TNR(rank_df):
    import pandas as pd

    # 200位までの順位を考慮
    max_rank = 200

    # 結果を格納するデータフレームを作成
    result_df = pd.DataFrame(
        index=range(1, max_rank + 1),
        columns=[f"next_week{i+1}" for i in range(len(rank_df.columns) - 1)]
    )
    result_df.index.name = "rank"

    # 次週の順位を集計
    for week in range(len(rank_df.columns) - 1):
        current_week = rank_df.columns[week]
        next_week = rank_df.columns[week + 1]

        # 現在の週の順位と次週の順位を取得
        for rank in range(1, max_rank + 1):
            # 現在の週で該当順位の次週の順位を抽出
            next_rank_values = rank_df[next_week][rank_df[current_week] == rank]

            # 次週の順位が存在しない場合は201を代入
            if not next_rank_values.empty:
                result_df.loc[rank, f"next_week{week+1}"] = next_rank_values.iloc[0]
            else:
                result_df.loc[rank, f"next_week{week+1}"] = 201  # 空白を201で補完

    # 結果を数値型に変換
    result_df = result_df.apply(pd.to_numeric, errors=('coerce')).fillna(201)

    return result_df
  
A.2.3 順位ごとの平均変動幅の算出と可視化

このコードは、順位ごとの平均変動幅の算出と可視化に使用した。

    import matplotlib.pyplot as plt
    import pandas as pd
    
    def plot_fluctuation(abs_rank_JP, abs_rank_US):
        """
        日本とアメリカの順位ごとの平均変動幅を可視化する
        :param abs_rank_JP: 日本の平均変動幅データフレーム
        :param abs_rank_US: アメリカの平均変動幅データフレーム
        """
        plt.figure(figsize=(12, 6))
        
        # 日本のデータをプロット
        plt.plot(abs_rank_JP['rank'], abs_rank_JP['RCA'], label='日本', marker='o', linestyle='-', alpha=0.7)
        
        # アメリカのデータをプロット
        plt.plot(abs_rank_US['rank'], abs_rank_US['RCA'], label='アメリカ', marker='x', linestyle='--', alpha=0.7)
        
        # グラフの設定
        plt.title('順位ごとの平均変動幅', fontsize=14)
        plt.xlabel('順位', fontsize=12)
        plt.ylabel('平均変動幅', fontsize=12)
        plt.legend(fontsize=12)
        plt.grid(True, linestyle='--', alpha=0.6)
        plt.tight_layout()
        
        # グラフの表示
        plt.show()
    
    # プログラム全体の統合
    if __name__ == "__main__":
        # 必要なデータを読み込み、計算
        rank_columns = [col for col in rank_JP.columns if col.startswith('rank')]
        rank_JP = rank_JP.apply(pd.to_numeric, errors='coerce')
        abs_rank_JP = CRF(rank_JP, rank_columns)
        abs_rank_JP = abs_rank_JP.mean(axis=1)
        abs_rank_JP = pd.DataFrame({
            'rank': abs_rank_JP.index+1,
            'RCA': abs_rank_JP.values
        })
    
        rank_US = rank_US.apply(pd.to_numeric, errors='coerce')
        abs_rank_US = CRF(rank_US, rank_columns)
        abs_rank_US = abs_rank_US.mean(axis=1)
        abs_rank_US = pd.DataFrame({
            'rank': abs_rank_US.index+1,
            'RCA': abs_rank_US.values
        })
        
        # 描画機能の呼び出し
        plot_fluctuation(abs_rank_JP, abs_rank_US)
    
平均変動幅の近似

このコードは、平均変動幅を累乗関数および対数関数で近似し、それぞれの近似式および決定係数を算出します。また、近似曲線をデータをもとに可視化します。

      # 必要なライブラリのインポート
      import pandas as pd
      import numpy as np
      import matplotlib.pyplot as plt
      from scipy.optimize import curve_fit
      plt.rcParams['font.family'] = 'Meiryo'
      
      # データの再読み込み
      JP_df = pd.read_csv("./JP/順位毎の平均変動幅_JP.csv")
      US_df = pd.read_csv("./US/順位毎の平均変動幅_US.csv")
      
      # 累乗関数と対数関数の定義
      def power_law(x, a, b):
          return a * np.power(x, b)
      
      def log_function(x, a, b):
          return a * np.log(x) + b
      
      # XとYのデータ
      x_JP = JP_df['rank']
      y_JP = JP_df['RCA']
      x_US = US_df['rank']
      y_US = US_df['RCA']
      
      # 日本:累乗近似
      popt_JP, _ = curve_fit(power_law, x_JP, y_JP)
      y_fit_JP = power_law(x_JP, *popt_JP)
      r2_JP = 1 - np.sum((y_JP - y_fit_JP)**2) / np.sum((y_JP - np.mean(y_JP))**2)
      
      # アメリカ:対数近似
      popt_US, _ = curve_fit(log_function, x_US, y_US)
      y_fit_US = log_function(x_US, *popt_US)
      r2_US = 1 - np.sum((y_US - y_fit_US)**2) / np.sum((y_US - np.mean(y_US))**2)
      
      # 近似式と決定係数の出力
      jp_eq = f"日本の累乗近似式: y = {popt_JP[0]:.2e} * x^{popt_JP[1]:.3f}, R^2 = {r2_JP:.4f}"
      us_eq = f"アメリカの対数近似式: y = {popt_US[0]:.2f}ln(x) + {popt_US[1]:.2f}, R^2 = {r2_US:.4f}"
      print(jp_eq)
      print(us_eq)
      
      # グラフ描画
      plt.figure(figsize=(10, 6))
      plt.scatter(x_US, y_US, label="アメリカ (実データ)")
      plt.plot(x_US, y_fit_US, color="red", label=f"対数近似 (アメリカ)")
      plt.xlabel("順位")
      plt.ylabel("平均変動幅")
      plt.legend()
      plt.grid(True)
      plt.title("アメリカ 順位毎の平均変動幅(対数近似)")
      plt.show()
    

A.3 順位ごとの平均再生回数に使用したプログラムコード

A.3.1 再生回数情報の抽出

本関数ExtPCは、楽曲の再生回数情報を含むデータフレーム(file_df)から、再生回数のみを抽出して新しいデータフレームとして返すものである。

    def ExtPC(file_df):
      #列番号を指定
      count_columns = list(range(4, len(file_df.columns), 2))
    
      #上記の列番号を抽出
      count_df = file_df.iloc[:, count_columns]
      count_df.columns = [f"streams{i+1}" for i in range(len(count_columns))]
    
      #返却
      return count_df
    
A.3.2 順位ごとの平均再生回数を計算し、可視化するコード

本関数PCA_rankは、ウィークリーチャートデータから順位ごとの平均再生回数を計算し、可視化するものである。

    def PCA_rank(PCR_df, country_name):
      import matplotlib.pyplot as plt
  
      # 各順位の平均再生回数を計算
      PCR_ave = PCR_df.mean(axis='columns')  # 行方向に平均を計算
  
      # 可視化
      plt.figure(figsize=(12, 6))
      plt.plot(PCR_ave, marker='o', label=f"{country_name} 平均再生回数")
      plt.xlabel("順位", fontsize=12)
      plt.ylabel("平均再生回数", fontsize=12)
      plt.legend(loc='upper right', fontsize=10)
      plt.grid(True, which="both", linestyle='--', alpha=0.7)
      plt.title(f"順位毎の平均再生回数 ({country_name})", fontsize=14)
      plt.tight_layout()
      plt.show()
  
A.3.3 近似曲線追加

このコードは、両対数グラフに変換し、近似曲線を追加するコード。

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.optimize import curve_fit
    from sklearn.metrics import r2_score
    plt.rcParams['font.family']='Meiryo'
    
    # データの読み込み
    PCA_JP = pd.read_csv("./JP/順位毎の平均再生回数_JP.csv")
    PCA_US = pd.read_csv("./US/順位毎の平均再生回数_US.csv")
    
    # 累乗近似関数
    def power_law(x, a, b):
        return a * np.power(x, b)
    
    # 近似とプロット関数
    def plot_and_fit_power_law(df, country_name):
        x = df['rank']
        y = df['PC_avg']
        
        # 累乗近似
        popt, pcov = curve_fit(power_law, x, y)
        a, b = popt
        
        # 近似データ
        y_fit = power_law(x, *popt)
        
        # 決定係数 (R^2)
        r2 = r2_score(y, y_fit)
        
        # プロット
        plt.figure(figsize=(10, 6))
        plt.scatter(x, y, label="実データ", alpha=0.7)
        plt.plot(x, y_fit, color='red', label=f"累乗近似: y={a:.2e}x^{b:.3f}\nR²={r2:.4f}")
        plt.title(f"{country_name} 順位毎の平均再生回数(累乗近似)", fontsize=14)
        plt.xlabel("順位", fontsize=12)
        plt.ylabel("平均再生回数", fontsize=12)
        plt.yscale('log')
        plt.xscale('log')
        plt.legend()
        plt.grid(True)
        plt.show()
        
        print(f"{country_name} 近似式: y = {a:.2e} * x^{b:.3f}")
        print(f"{country_name} 決定係数 (R²): {r2:.4f}")
    
    # 日本のデータでプロットと近似
    plot_and_fit_power_law(PCA_JP, "日本")
    
    # アメリカのデータでプロットと近似
    plot_and_fit_power_law(PCA_US, "アメリカ")
    

A.4 確率密度関数に関するコード

A.4.1 カーネル密度推定を行うコード

このコードは、次週順位データをもとにカーネル密度推定を行おうコード。

      import pandas as pd
      import numpy as np
      import seaborn as sns
      import matplotlib.pyplot as plt
      plt.rcParams['font.family']='Meiryo'
      
      # 次週順位データの読み込み
      next_rank_JP = pd.read_csv("./.csv", index_col=0)
      
      plt.figure(figsize=(12, 8))
      
      for rank in next_rank_JP.index:
          rank_data = next_rank_JP.loc[rank].dropna()
      
          # KDEプロット (標準化なし)
          sns.kdeplot(rank_data, label=f"{rank}位", alpha=0.4)
      
      plt.title("日本 次週順位の確率密度関数", fontsize=14)
      plt.xlabel("次週順位", fontsize=12)
      plt.ylabel("密度", fontsize=12)
      plt.grid(True, linestyle='--', alpha=0.7)
      plt.xlim(1, 201)  # 順位の範囲
      plt.yticks([0.0, 0.02, 0.04, 0.06, 0.08, 0.10, 0.12, 0.14, 0.16])
      plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
      plt.show()
    
A.4.2 確率密度関数を重ねるコード

このコードは、A.4.1で作成したものを重ねて表示するコード。

      import pandas as pd
      import numpy as np
      import seaborn as sns
      import matplotlib.pyplot as plt
      from scipy.stats import norm
      plt.rcParams['font.family']='Meiryo'
      
      # データの読み込み
      next_rank_JP = pd.read_csv("./JP/次週順位_JP.csv", index_col=0)
      
      # 標準化して重ねる
      plt.figure(figsize=(12, 8))
      
      for rank in next_rank_JP.index:
          rank_data = next_rank_JP.loc[rank].dropna()
      
          # 標準化 (Zスコア)
          standardized_data = (rank_data - rank_data.mean()) / rank_data.std()
          sns.kdeplot(standardized_data, alpha=0.4, label=f"{rank}位", linewidth=1)
      # グラフの設定
      plt.title("日本 確率密度関数", fontsize=14)
      plt.xlabel("標準化された次週順位", fontsize=12)
      plt.ylabel("密度", fontsize=12)
      plt.grid(True, linestyle='--', alpha=0.7)
      plt.xlim(-5, 5)
      plt.legend(title="現在の順位", bbox_to_anchor=(1.05, 1), loc='upper left')
      plt.tight_layout()
      plt.show()
    
A.4.3 指定順位の確率密度関数を抽出するコード

このコードは上位と下位に分類わけする際に使用した。

      import pandas as pd
      import numpy as np
      import seaborn as sns
      import matplotlib.pyplot as plt
      from scipy.stats import norm
      plt.rcParams['font.family']='Meiryo'
      
      # データの読み込み
      next_rank_US = pd.read_csv("./US/次週順位_US.csv", index_col=0)
      
      # 上位・下位でグラフを分ける
      plt.figure(figsize=(12, 8))
      
      # 上位100位 (1~100位)
      for rank in next_rank_US.index:
          if rank <= 165:  # 上位100位をフィルタリング
              rank_data = next_rank_US.loc[rank].dropna()
              standardized_data = (rank_data - rank_data.mean()) / rank_data.std()
              sns.kdeplot(standardized_data, alpha=0.5, label=f"{rank}位", linewidth=1)
      
      plt.title("アメリカ 確率密度関数 (1位~165位)", fontsize=14)
      plt.xlabel("標準化された次週順位", fontsize=12)
      plt.ylabel("密度", fontsize=12)
      plt.grid(True, linestyle='--', alpha=0.7)
      plt.xlim(-5, 5)
      plt.legend(title="現在の順位", bbox_to_anchor=(1.05, 1), loc='upper left')
      plt.tight_layout()
      plt.show()
      
      # 下位100位 (101~200位)
      plt.figure(figsize=(12, 8))
      
      for rank in next_rank_US.index:
          if rank > 165:  # 下位101~200位をフィルタリング
              rank_data = next_rank_US.loc[rank].dropna()
              standardized_data = (rank_data - rank_data.mean()) / rank_data.std()
              sns.kdeplot(standardized_data, alpha=0.5, label=f"{rank}位", linewidth=1)
      
      plt.title("アメリカ 確率密度関数 (166位~200位)", fontsize=14)
      plt.xlabel("標準化された次週順位", fontsize=12)
      plt.ylabel("密度", fontsize=12)
      plt.grid(True, linestyle='--', alpha=0.7)
      plt.xlim(-5, 5)
      plt.legend(title="現在の順位", bbox_to_anchor=(1.05, 1), loc='upper left')
      plt.tight_layout()
      plt.show()