Python seabornを利用して「競合」の定義を考える

アイスタイルで分析基盤の構築・運用を行っているやすです。
主にBigQuery, Digdag, Embulk, Redashといった技術を日々の業務では取り扱っています。最近は化粧品メーカー向けマーケティング支援サービス「ブランドオフィシャル」のETL処理部分を担当しています。

以下、ブランドオフィシャルについての記事や資料です。
【同僚対談】”伝えるべきこと”と”届けるべき人”をつなげる 『ブランドオフィシャル』vol.1
サービスの紹介資料

モチベーション

ブランドオフィシャルでは「気になるブランド」という機能があり、ユーザーさんが自ブランド以外で、どこのブランドに
関心があるかを見ることができます。ブランドのマーケティング担当は「競合」がどこかを把握することで、施策などの差別化を行い、より自ブランドに関心を持ってもらえるようマーケティングを考えることができます。

気になるブランド

この「競合」の精度を高めていきたい、そのためには競合とはどういう状況なのかを探りたいというのが今回のモチベーションです。

方法① 共有ユーザーの数

@cosmeにはブランドに関連する商品のページや記事のページがあります。各ブランドに紐づくページを見た際のログはGoogle Analyticsで計測し、360 Suiteの機能としてBigQueryに蓄積されています。

今回はそのアクセスログを使用して、ページビュー数がトップ50のブランドにのみ絞りました。競合を探すためにブランドに関連するページを見たユーザーが、他ブランドのページも見ていた場合にブランド同士の共有ユーザーと定義し、BigQueryで集計しました。

以下の表は集計した結果をCSVに吐き出した例です。brand_id がブランド名、compare_brand_id が共有しているかの対象となるブランド、share_UU が共有しているユーザー数です。brand_idcompare_brand_id が同じ場合はその1ブランドに関連するページを見たUU数です。異なる組み合わせの場合はその2ブランドどちらもに関連するページを見たUU数です。

brand_id compare_brand_id share_UU
A A 100
A B 80
A C 20

コード

可視化にはPythonのseabornライブラリを使用します。今回はブランドの組み合わせから構成され、共有ユーザーの数が多い組み合わせを発見したかったのでヒートマップで出力しました。
参考:seaborn.heatmap — seaborn 0.9.0 documentation

import matplotlib.pyplot as plt
import seaborn as sea
import numpy as np
import pandas as pd

df_brand_share = pd.read_csv('brand_shares_users.csv')

sea.clustermap(data,method='ward',z_score=0,figsize=(20, 15),cmap='Greens')

df_brand_share_pivot=pd.pivot_table(data=df_brand_share_sort, values='share_user_num',columns='brand_id', index='compare_brand_id', aggfunc=np.max)

plt.subplots(figsize=(20,15))
sea.heatmap(df_brand_share_pivot,cmap='Greens')

結果

行ブランドAと列ブランドAのマスは、ブランドAに関連するページを見たユーザーの数です。行ブランドAと列ブランドBのマスは、ブランドAとブランドBに関連するページを見たユーザー(共有ユーザー)の数です。

ブランドAは人気のようで、他ブランドと共有しているユーザー数が全体的に多いと読み取れます。ブランドCはブランドAほど人気はないですが、他ブランドと共有しているユーザー数も少ないように見えます。

方法② 共有ユーザーの数をZスコアで見てみる

方法①でわかったことから、「人気ブランドはユーザー数が多いので、他ブランドとも共有しているユーザー数が多いが、そのブランドは多くの他ブランドと競合ということ意味なのか」という疑問が生まれました。そこでブランドごとに共有ユーザーの数をZスコア化し、ブランドのユーザー数が大小に関係なく、他ブランドとの共有がどのくらいかを比較できます。

コード

seabornのclustermapにはパラメータとしてZスコア化してくれるz_scoreが用意されています。z_score=0は行に対して、z_score=1は列に対してZスコア化を行います。方法①で

sea.heatmap(df_brand_share_pivot,cmap='Greens')

としていた部分を

sea.clustermap(df_brand_share_pivot,z_score=0,cmap='Greens')

に変えます。

参考:seaborn.clustermap — seaborn 0.9.0 documentation

結果

今回のZスコアは自ブランドのファンのユーザー数に対して、他ブランドに浮気しないユーザーが多い状態だと、Zスコアが高くなりより濃い緑にプロットされます。先ほどのブランドAとブランドCを見てみると、ブランドAよりもブランドCの方が濃い緑でプロットされています。

ブランドAとブランドCを比較すると、ブランドAよりもブランドCの方がZスコアが高く、ブランドCの方が他ブランドに浮気しないユーザーが多い状態となっていることがわかります。ブランドAは方法①ではユーザー数が多い結果となりましたが、他のブランドに浮気してしまうユーザーも多いことがわかりました。

またclustermapはデンドログラムを出力します。ブランドAが属する青枠はメイクアップ系ブランドと呼ばれるブランドがカテゴリされていました。ブランドCが属する赤枠はスキンケアブランドがカテゴリされていたので、メイクアップ系の方が競合が多い領域といえる可能性があります。

おわり

BigQueryやPythonのseabornライブラリを使って、化粧品ブランドの「競合」の定義を考えてみました。
特に統計処理を書くことなく簡単にZスコア、デンドログラムをだせるclustermapは便利ですね。

今回はTOP50のブランドのみで行いましたが、@cosmeには約3万件のブランドが登録されているので、次回は範囲を広げつつも理解しやすいアウトプットで出したいです。

2016年 istyle 新卒入社 データエンジニア & アナリスト 好きな食べ物はハンバーガーとラーメンとタイ料理