【Python/Pandas】Webページ上のデータを読み込んで、動く棒グラフを作成する方法|AKB48 シングル選抜回数
こんにちは。今回は、Web上に公開されているAKB48歴代シングルにおける選抜メンバーの一覧表を読み込んで、メンバーごとの選抜回数の推移を集計し、動く棒グラフ(Bar Chart Race)で可視化したいと思います。
AKB48のディスコグラフィー/選抜メンバーの一覧 - エケペディア
事前準備
今回も、ツールはPythonを使用します。Pythonのセットアップ方法は過去の記事を参照ください。
データサイエンティストを目指す方向け Pythonセットアップ方法(Windows版) - Kevin's Data Analytics Blog
また、Pythonを使って動く棒グラフを作成するためには、PCにいくつかのソフトウェアをインストールする必要があります。こちらも手順については、過去の記事でまとめていますので、併せてご参照ください。
Pythonで動く棒グラフを作成する方法|Bar Chart Race - Kevin's Data Analytics Blog
Webページ上のデータの読み込み
Pythonの実行環境を開いたら、まず、Pandasライブラリをインポートします。
import pandas as pd
Pandasライブラリが用意しているpd.io.html.read_html()という関数を使用すると、指定したURLから表形式のデータだけを抜き出してくれます。
target_url = 'https://48pedia.org/AKB48%E3%81%AE%E3%83%87%E3%82%A3%E3%82%B9%E3%82%B3%E3%82%B0%E3%83%A9%E3%83%95%E3%82%A3%E3%83%BC/%E9%81%B8%E6%8A%9C%E3%83%A1%E3%83%B3%E3%83%90%E3%83%BC%E3%81%AE%E4%B8%80%E8%A6%A7'
tables = pd.io.html.read_html(target_url)
実行すると、tablesという変数に、URL内の表データがリスト形式で保存されるので、1番目(リストの番号は0始まりなので0)を指定して、dfという変数に格納します。
df = tables[0]
変数名だけで実行すると、変数の中身をみることができます。
df
データの読み込みが出来ました。
データの加工
動く棒グラフを書くためにデータを加工します。
不要な列の削除
del 関数を使用して、列を削除します。今回は、一番左の「所属」列と一番右の「選抜回数」列は使用しないので、それぞれ、列番号を指定して削除します。
del df[df.columns[61]] del df[df.columns[0]]
不要な行の削除
「名前」もしくは「定員」から始まる行を削除します。
df = df[df[df.columns[0]]!='名前'] df = df[df[df.columns[0]]!='定員']
列名の加工
列名が3階層(発売年、枚数、曲名)になっているため、1階層にまとめます。また、元のサイトでは、曲名は縦書きですが、これを横書きにするために、記号文字を変換します。replace関数を使って、「|」→「ー」、「 `」→「、」とします。
df.columns = [df.columns[i][0]+'年\n'+df.columns[i][1]+'. '+df.columns[i][2] for i in range(len(df.columns))] df.columns = [df.columns[i].replace('|','ー').replace(' `','、') for i in range(len(df.columns))]
データ値の変換
欠損値は選抜されなかったことを表すため「0」に、「●」と「★」は選抜されたことを表すため「1」に変換します。欠損値の置き換えは、fillna関数を使用します。
df = df.fillna(0).replace('●',1).replace('★',1)
行と列の入れ替え
元のデータは、左から右の順番で古いシングルから新しいシングルのデータが格納されています。動く棒グラフを作成するために、上から下に時系列が進むように行と列を入れ替えます。このような操作を行列の転置といい、データの変数に「.T」をつけて実行すると転置されます。また、転置前の行インデックスが転置後の列名になりますので、set_index関数を使用して、転置前に1列目のメンバーの名前をインデックスに指定します。
df = df.set_index(df.columns[0]).T
ここまでのデータ加工のイメージは以下のとおりです。
累積和の計算
最後に、cumsum関数を使用して、列ごとに縦方向に累積和を計算します。これにより各データの値は、該当行のシングル曲の時点における、それまでの合計の選抜回数を表します。
これで、動く棒グラフを作成するためのデータ準備ができました。
動く棒グラフの作成
加工後のデータを利用して、動く棒グラフを作成します。
今回はデータに日本語を含むので、japanize_matplotlibというPythonライブラリをインストールして、Pythonのグラフ描画機能を日本語データに対応させる必要があります。もし、まだインストールしていない場合は、コマンドプロンプトから以下のコマンドを実行します。
pip install japanize_matplotlib
Pythonの実行環境に戻って、ライブラリをインポートします。
import bar_chart_race as bcr import japanize_matplotlib
bar_chart_race関数に、先ほど加工したデータ「df」を指定して、動く棒グラフを作成します。パラメータの値はお好みで調整ください。
bcr.bar_chart_race( df=df ,fixed_max=True ,title='AKB48 歴代メンバー シングル選抜回数 ランキング推移 2006.02-2021.06 ' ,n_bars=27 ,bar_size=.8 ,period_label={'x': .975, 'y': .075, 'ha': 'right', 'va': 'center', 'size': 13} ,filter_column_colors=True )
グラフのカスタマイズ
スマホ表示用に動画を縦長にする
fig_kwargsというパラメタで動画サイズを指定します。スマホ用の動画は、横と縦の比率が9:16が適していますので、文字サイズとのバランスを考慮して、それぞれ1/2にして4.5:8とします。
bcr.bar_chart_race( df=df ,fixed_max=True ,title='AKB48 歴代メンバー シングル選抜回数 ランキング推移 2006.02-2021.06 ' ,n_bars=27 ,bar_size=.8 ,period_label={'x': .975, 'y': .05, 'ha': 'right', 'va': 'center', 'size': 11} ,fig_kwargs={'figsize': (4.5, 8), 'dpi': 72} ,filter_column_colors=True )
棒の色を指定する
例として、メンバーの所属チームに応じて、棒の色を変えてみたいと思います。
まずは、前準備として、Excelファイルに各メンバーの所属チームをまとめます。複数所属している場合は、代表的なチームを選んでいます。なお、メンバーの並び順をウェブから取得したテーブルとExcelファイルで一致させる必要がある点にご注意ください。
pd.read_excel関数を使用して、こちらのExcelファイルを読み込みます。
member_team=pd.read_excel('../data/member_team.xlsx')
「color」という列を追加して、「team」列の値に応じて色を指定します。replace関数を使用して、'A'などのチーム名を'red'などの色の名称に変えていきます。
member_team['color']=member_team['team'].replace('A','red').replace('K','lime').replace('B','aqua').replace('SKE','orange').replace('NMB','gold').replace('HKT','dimgrey').replace(4,'skyblue') # チーム不明のメンバーはwhiteで補完 member_team['color']=member_team['color'].fillna('white') member_team
filter_column_colorsをFalseにして、colorsに先ほど作成したデータの「color」列の値を指定します。
bcr.bar_chart_race( df=df ,fixed_max=True ,title='AKB48 歴代メンバー シングル選抜回数 2006.2-2021.6 ' ,n_bars=27 ,bar_size=.8 ,period_label={'x': .975, 'y': .05, 'ha': 'right', 'va': 'center', 'size': 11} ,fig_kwargs={'figsize': (4.5, 8), 'dpi': 72} ,filter_column_colors=False ,colors=member_team['color'] )