Python:Matplotlibでグラフ作成2(Google Colaboratory)

グラフ作成第2弾の目標は,動画の視聴回数の累積回数と3日間の視聴回数を重ねて表示するものです。

目標とするグラフ

1日複数回の視聴回数が記録されてるCSVから3日間ごとにデータを取り出して,差分を求めて併記します.
3日間にした理由は,記録時間が毎日同じではなかったので,その差を吸収するため,かつ,できるだけ変化が可視化できるようにするためです.(試行錯誤の賜物)

入力データ(CSV)

処理は以下のとおりです

  1. 読み込んだCSVから不要部分を削除(不要な列と視聴回数がNaNの行)

  2. 日時を日付型に変換(読み込んだままでは文字列)

  3. 3日ごとに最初の行を取り出す

  4. 3日間の視聴回数を算出(前の行からの差分を計算)

  5. 累積視聴回数を折れ線グラフに,3日間の視聴回数を棒グラフにする

  6. スケールが異なるので第2軸を採用するが,メインデータは累積視聴回数(折れ線グラフ)なので,累積視聴回数を第1軸にしたい

  7. 3日間の視聴回数(棒グラフ)を第2軸にすると,棒グラフが前面となり,折れ線グラフが見えない.そのため,棒グラフを半透明にする.

  8. 凡例を表示

  9. 第1縦軸の軸ラベルの単位を万回にしたい

  10. 第2縦軸の軸ラベルに3桁カンマを入れたい

  11. グラフ内にコメントを入れたい

(1)グラフに日本語を表示させるためのライブラリをインストール
Colaboratoryは,Googleサーバ上の仮想環境なので,毎回のインストール作業が必要

!pip install japanize_matplotlib

Successfullyと表示されれば,インストール成功

(2)データの読み込み
ファイルはUTF-8CSV形式で保存

import pandas as pd

df = pd.read_csv('/content/データ.csv')
df.tail()
データを読み込んだ結果


(3)DataFrameの不要部分の削除

df = df.drop('日' , axis=1)
df = df.drop('時間' , axis=1)
df = df.drop('Unnamed: 4' , axis=1)
df.head()
不要な列を削除した結果

drop で列を削除.列名を指定しました.列方向の削除なので
axis=1
の記述が必要です.


(4)NaNを含む行の削除
データは手作業で収集されたものなので,欠損はどうしても発生します.(手作業でなくても欠損は出ますが)

df = df.dropna(axis=0)
df.head()
NaNを含む行を削除した結果

(5)文字列を日付型に変換

X_dt = pd.to_datetime(df['日時'])
df['DateTime'] = X_dt
df.head()
日付型に変換した結果

'日時'の列が YYYY/MM/DD HH:MM の形式であったため,指定なく変換することができました.
形式がデフォルトでない場合は、formatで指定します.
(※形式の指定はstrftime documentationを参照)

(6)3日ごとに最初の行を取り出す

df_day = df.groupby(pd.Grouper(key="DateTime", freq="3D" , label='left' , closed='left')).first()
df_day.head()
3日ごとの最初の値を取り出した結果


groupbyは特定の列の値で集計するメソッドです.
例)typeごとに合計を求める

例)typeごとに合計を求める

分類のキーが日付の場合は,日ごと,週ごと,月ごとなどを指定することができます.
pd.Grouper(key='列名', freq="周期" )を利用します.
周期の指定は以下のとおり

Grouperの周期の指定文字

他にビジネス日などもあります.詳細はDocument
3日の指定をするときは、'3D'とします.

groupbyに利用できる集計関数は以下のとおり

groupby()で集計に利用できる関数

ここでは、3日ごとに集計して,その最初のデータを収集したいので,first()利用します.

groupbyで3日ごとにグループ分けした最初のデータ

(7)前の行との差を求める
3日間で何回の視聴があったかを調べるには、前の行との差分を求めることになります.

df_day['diff'] = df_day['視聴回数'].diff()
df_day.head()
Siries'視聴回数'の差分を求めた結果

diff()の引数に何行前(または後)を指定することもできます.

これでデータが揃いました

(8)グラフを描く
それではグラフを描きましょう.

import matplotlib.pyplot as plt
import japanize_matplotlib

fig , ax  = plt.subplots(figsize=(10,6))
ax2 = ax.twinx()
ax.plot(X_dt , df['視聴回数'] , color='red' , linewidth=3 , label='累積視聴回数(1軸)')
ax2.bar(df_day.index , df_day['diff'] , width=3 , label='3日間の視聴回数(2軸)' , color=(0,0,1,0.3) , edgecolor='white' , align='edge')
line1 , label1 = ax.get_legend_handles_labels()
line2 , label2 = ax2.get_legend_handles_labels()
plt.legend(handles = line1 + line2 , labels=label1+label2 , loc='center right')
ax.grid()
ax.set_yticks(list(range(3800000 , 4510000 , 100000)) , list(range(380 , 451 , 10)))
ax2.set_yticks(list(range(0 , 35100 , 5000)) , ['{:,}'.format(i) for i in range(0 , 35100 , 5000)])
ax.set_title('「Eiskunstläufer Yuzuru Hanyu mit dem Auftritt seines Lebens | Sportschau」視聴回数' , color='darkblue')
ax.set_ylabel('累積視聴回数(万回)')
ax2.set_ylabel('3日間の視聴回数(回)')
ax.text(df.iloc[0]['DateTime'] , 4505000 , 'データ提供:クシャナ様')
ax.text(df.iloc[0]['DateTime'] , 4300000 , '「動画が確実に残るように\nみんな観よう!」\nという海外ファンの呼びかけ\nに呼応した羽生ファンの力')
ax.text(df.iloc[4500]['DateTime'] , 4380000 , 'ドイツのほぼサッカーの動画の中\n450万回視聴されました')

ax2 = ax.twinx()
x軸を共通にしたax2 を生成します.これで第2軸の指定ができます.
ここでは,メインのグラフである累積視聴回数を第1軸にしたいところです.第2軸に表示する3日間の視聴回数は,言わばヒストグラムなので棒と棒の隙間を無くした棒グラフを描きます.

ここから棒グラフのパラメータの指定です.
3日分なので,棒の幅は3になります.(横軸である日付は1日が1なので)
width=3
しかし,ベトっと塗りつぶされてしまうとグラフの締まりが悪いので棒に枠線をつけて,棒と棒の区切りを表すことにしました.
edgecolor='white'

第1軸を先に描画するので、第1軸が折れ線グラフ,第2軸が棒グラフだと,棒グラフに塗りつぶされて折れ線グラフが隠れてしまいます.
そこで、棒グラフの棒を半透明にします.それには色をFGBAで指定します.ここでは青い線の透明度30%で描きました.
color=(0,0,1,0.3)

3日間の先頭のデータを採用していまうので、横軸に対する棒の位置を左端にします.指定にはedgeとcenterの選択肢があり,デフォルトはcenter
align='edge'

折れ線グラフのパラメータとしては,色の指定と太さの指定をしています.
color='red'
linewidth=3

凡例を指定します.
第2軸にしたときは,凡例がそれぞれになってしまうので,ラベルとハンドラを取り出して合わせて表示します.
line1 , label1 = ax.get_legend_handles_labels()
line2 , label2 = ax2.get_legend_handles_labels()

locパラメータで凡例の表示場所を指定することができます.
plt.legend(handles = line1 + line2 , labels=label1+label2 , loc='center right')
指定できる位置は以下のとおり

凡例を表示する位置

他にbestというキーワードもあって,要はお任せ,ということなのでしょうけれど,イマイチ「ベスト」ではなかったです.
他に座標を指定することもできます.
loc=(x座標 , y座標)

第2縦軸には3桁カンマを表示したいです.
ax2.set_yticks(データの値のリスト , 表示する文字列のリスト)
データの値のリストは0から35000まで5000ごと
list(range(0 , 35100 , 5000)
表示する文字列のリストはformat関数と内包表記を利用して
['{:,}'.format(i) for i in range(0 , 35100 , 5000)]

描いたグラフ

コンスタントに視聴しないと動画が削除されてしまうそうですので,ときどき可視化してエールとしたいと思います.

カッコイイので観てね