matplotlibを利用して、20年間のクチコミデータを動的に可視化する

こんにちは。
アイスタイルアドベントカレンダー14日目は、R&D部のsugatoが担当します。

普段は事業部から依頼を貰い、主にBigQueryを利用したデータ抽出業務やRedashというBIツールの活用、
DigdagやEmbulkなどのETLアプリケーションを利用し、DWHとしてのBQ活用を進めています。

今後もそういった業務に励んでいく予定ですが、今年の4月にアイスタイルに入社するまでは、
データアナリストとしてのキャリアを2年ほど歩んできました。

ここ最近はあまり改善提案に繋がる分析業務に時間を割けておらず、
アドベントカレンダーを機に、何か可視化したいなと思ったのがこの記事のきっかけです。

注意点

分析を行う場合は、何かしらのアクションをする、という前提で集計なりを行い、
その結果を分かりやすく表示するためにグラフを作ることがほとんどです。

今回は目的と手段が逆になっており、とりあえず可視化したい!!が先行しているので、
正直なところアウトプットの解釈性はほぼ0です。

またデータの都合上、マスキング等を行っていますのでご了承ください。

D3.js(もどき)に関して

Twitterやyoutube等で見かける機会が増えたのですが、リンク先のような
棒グラフのランキングが入れ替わる動画を見たことがある人も多いのではないでしょうか?
gapminder

これを見て面白そうだし、とりあえずやってみるか!となったのが今回のきっかけです。
もどき、と記載したのは、本来Javascriptを利用するところをpythonのみで作成したためです。

データの用意

以下コードでJupyter Notebook上からBigQueryの実行結果をdfに渡します。

import pandas as pd
query = '''
# standardSQL
BigQueryで実行するクエリ
'''
df = pd.read_gbq(query, 'BQのプロジェクト名', dialect='standard')

作成したテーブルは下記のような形で縦持ちです。

番号というのは商品カテゴリに対して色を指定する際に利用するのですが、
Windows関数を用いてカテゴリごとに、日付の昇順に対して番号を振ったものです。

商品カテゴリ 日付(月) 番号 クチコミ件数
A 2018-01-01 1 10
A 2018-02-01 2 15
A 2018-03-01 3 12
B 2018-01-01 1 18

商品カテゴリ別に配色し、matplotlibで作画をする

商品カテゴリ別の配色を作成してあげます。
RGBで適当に渡しています。

# この時、商品カテゴリとデータの型が一致する形で作成した方が楽です
colors = dict(zip(
    [A,B,C,・・・ ,i],
    ['#F20C0C', '#F2460C', '#F27F0C',・・・, '#F20CF2']
))

全件表示する前に、1月分の結果を表示します。
こちらでうまく実行できていれば、次の部分で動画にしていきます。

fig, ax = plt.subplots(figsize=(15, 8))

# 商品カテゴリ別ごとのクチコミ件数を出しても良いのですが、上位20位までに限定しました。
def draw_barchart_TOP20(番号):
    dff  = (df[df['番号'].eq(番号)]
            .sort_values(by='クチコミ件数', ascending=True)
            .tail(20))

    ax.clear() # 現在表示されているグラフを削除する。これにより、紙芝居として作画しています。

    calender = df[['日付(月)','番号']]
    modify_calender = calender[calender['番号'].eq(番号)] #Dfに対してunique()は使用できないので関数に渡す
    modify_calender_key = modify_calender['日付(月)'].unique() #Dfに対してunique()するとlist型に変換されるので、月の頭を取得
    modify_month = modify_calender_key[0]

    # グラフ上の表示部分
    ax.barh(dff['商品カテゴリ'].astype(str), dff['クチコミ件数'],width, color=[colors[x] for x in dff['商品カテゴリ']])

    # グラフ上にテキストを表示させている部分
    for i, (クチコミ件数, 商品カテゴリ) in enumerate(zip(dff['クチコミ件数'], dff['商品カテゴリ'])):
        ax.text(クチコミ件数, str(商品カテゴリ), クチコミ件数, ha='left') # 商品カテゴリ商品のクチコミ件数
        ax.text(1, 0.4, modify_month, transform=ax.transAxes, size=30, ha='right') #動画にする際に、いつのクチコミなのかを右側に表示

    # 作画する際に、クチコミの件数に合わせてX軸の値が変化してしまうので上書きする
    plt.xlim(0, 任意の値を渡してください) #X軸の最小値と最大値
    plt.title('グラフのタイトル', fontname="MS Gothic", fontsize=18)

width=0.8
draw_barchart_TOP20(1)

月別クチコミ数

以下、アニメーションの部分になります。

import matplotlib.animation as animation
from IPython.display import HTML
fig, ax = plt.subplots(figsize=(15, 8))
# ここで処理をループさせる回数を設定します。
animator = animation.FuncAnimation(fig, draw_barchart_TOP20, frames=range(1, df['month'].nunique()))
HTML(animator.to_jshtml()) 

こちらを実行すると、下記のような形で結果が得られます。
下の方に設定されたボタンを押すことで、表示スピードを変えることもできます。

月別商品カテゴリ別クチコミ数

本来であれば、皆さんにボタンを押してもらったりカーソルを動かして楽しんでもらいたいのですが。。
データの都合上、その部分はカットしています。ごめんなさい。

gifで結果を張り付けてみたので、紙芝居で動いている感じが伝われば嬉しいです。

月別商品カテゴリ別クチコミ件数.gif

結果

動的に可視化できましたが、じゃあここから読み取れることがあるか?といわれると少ないです。
理由としては、月別の結果が流れてしまい傾向が読み取れないからだと思います。
(そもそも商品カテゴリを隠してしまっていますし。。)

折れ線グラフや商品カテゴリ別の積み上げ棒グラフでも良いのですが、クチコミ件数の増減が明確に出てしまうため、ヒートマップを作成して何となく傾向を評価してみたいと思います。

ヒートマップの作成

再度、BigQueryを利用して、ヒートマップを作成するためのデータを作成しました。
SQLに慣れ過ぎて、pythonでのデータ加工を避けがちなのが自分の中で課題です。

1年間の平均クチコミ数を集計した後に、該当月のクチコミ数に対する偏差値を算出しています。
ある月に多くクチコミがされているのかを評価する上で、比較的多くの人に馴染みがあると思い採用しました。

年度 クチコミ件数 平均クチコミ件数 偏差値
2001 1 10 13 41
2001 2 15 13 55
2001 3 12 13 48
2002 1 18 20 45

こちらを下記のheatmap_dfに渡してあげます。

import numpy as np
heatmap = pd.pivot_table(data=heatmap_df, values='偏差値', 
                         columns='年度', index='月', aggfunc=np.mean)

import seaborn as sns
sns.heatmap(heatmap)

右の縦軸が偏差値なので、白い方が1年の中で多くクチコミされている月になります。
(1999年にサービスが開始されたため、1999、2000年のデータは評価対象外として省いています。)

年度別月別クチコミ数の偏差値

パッとこれを眺めると、3月、10月は1年の中でもクチコミをされている印象を受けます。
2001年、2002年ごろは12月の偏差値が高いですが、サービスとして成長過程であったことが影響として大きいと思われます。
1年を通して、徐々にクチコミ件数が増加していれば後半の12月は偏差値が高くなるはずなので。

もう少し考察すると、そもそもクチコミ件数だけで評価して良いんだっけ?? ということが気になったりします。
理由としては、商品を買う⇒クチコミを行う、という流れがメインであるはずだからです。
(さらに考えると、あるユーザーが今までクチコミをしたことがない商品をどれだけ買ったのか?を評価する必要があるかもしれません。)

分析を行う時は、比較項目や裏側で動いているロジックが近しいのか??を意識するようにしています。

もし上記の内容を考慮して値をならした場合、このヒートマップも変化するかもしれませんね。

あとがき

クチコミのデータを動的に可視化する、というタイトルでお送りしましたが、手段ありきで分析を行うべきではないなと改めて感じました。

また紙芝居形式なので思ったよりもぬるぬる動かず、フロント側で綺麗に表示させたい欲が強まりました。
ただ、ファイル一つで動的に可視化したデータを共有できるのは良いなあと思います。

私自身アイスタイルへ中途入社を決めた理由は、ECや実際の店舗での購買ログ、Webやアプリのアクセスログを横断的に分析できるプラットホームに魅力を感じたためでした。
ここ最近は基盤などの整備を優先的に行っていますが、横断的な分析も進めてたいと思っています。

ですが、正直なところ人がまだまだ足りない、というのが現状です。
興味を持たれた方は、下のリンクからエントリーしてみてください。一緒に分析しましょう!!

明日の「アイスタイル Advent Calendar 2019」15日目は、bannotさんの「非ツイッター民が黒い画面からツイートしてみた件について」です。

中途採用 | istyle 株式会社アイスタイル

参考リンク

Bar Chart Race in Python with Matplotlib

2019年4月中途入社。 主にデータ分析業務に携わってきましたが、現在は分析基盤の構築や整備を担当。日本酒好きなヤクルトファン。

コメントを残す