広告/統計/アニメ/映画 等に関するブログ

広告/統計/アニメ/映画 等に関するブログ

GoogleAnalyticsの週次レポートを自動化する方法

Google Analyticsはとても人気があり、アクセス解析はしないけれどサイトの訪問状況の概要を知りたい、というニーズに於いては最も手軽で便利なツールです。

一方で、使い方を検索しても、公式ガイドのコピペのようなサイトばかりヒットしてしまい、なかなかあと一歩、知りたい情報にたどり着けないことも多いのではないでしょうか?特に、週次レポートを課せられ毎回毎回ブラウジングから作成している人も多いのではないか?と思い、レポート自動化のコツをまとめておくことにしました。

レポート自動化の意義

レポートを自動化していない最悪のケースでは以下の工程が発生しているはずです。

  1. Google Analyticsのページへログインする(アカウントを切り替える)」
  2. 左のメニューから「行動>●●」とクリックしていく
  3. 表示されたレポートにディメンションを追加していく
  4. 期間を設定して集計する
  5. 表示が1ページで切れているので5000行にする
  6. レポートをエクセルでダウンロード
  7. エクセルのデータをエクセルでグラフ化
  8. パワポにエクセルのグラフを貼る

2,3 部分の解消やフィルターが必要な方は「カスタムレポート」も使っていると思いますが、いずれにせよこんなことを毎週月曜日にやっていたら週明け早々非生産的です。

Google スプレッドシートのアドオンでデータを取得する

Google スプレッドシートにはGoogle Analyticsと連携したアドオンがあります。アドオンを取得したらアドオンメニューで表示できるようになりますので、Create Reportから新しくレポートを作成します。1

f:id:yyhhyy:20191019182738j:plain

自分が知りたい指標を選んでいきます。英語ですが対照表も検索すれば出てきます。

例えば、

  • 知りたい数値類
    • Page Views
    • New User
    • User
  • 知りたい軸
    • 週の単位
    • 流入
    • ページのタイトル

などを設定します

f:id:yyhhyy:20191019183002j:plain

無事レポートができるとこのような画面になります。ここでは期間設定を変更したり、フィルターやセグメントもこのシート上で変更できます。ディメンションを追加することも可能です。2

f:id:yyhhyy:20191019183628j:plain

作成したらアドオンから「Run Report」を押すと、別シートに集計結果が出力されます

f:id:yyhhyy:20191019183753j:plain

f:id:yyhhyy:20191019183908j:plain

このときスプレッドシート側で何行の何列から何行の何列まで使っているか?をメモしておく必要があります。

Google Data Portal でデータをグラフ化する

データソースの連携

グーグルのサービスの中では比較的マイナーかもしれません。Google Data Portal(旧:Google Data Studio)からレポートを作ります。

f:id:yyhhyy:20191019184143j:plain

するとレポート作成画面ができますが、右下の「新しいデータソースを作成」から、先程のスプレッドシートのデータを設定していきます。

f:id:yyhhyy:20191019184251j:plain

凝ったことしないのであればGoogle Analyticsを直接連携しても良いのですが、普段カスタムレポートでフィルターを細かく設定している人や、Google Analyticsのレポートそのままではなくスプレッドシート上で簡単な前処理が必要な場合などを考えると、現時点のサービスのクオリティではスプレッドシート連携の方が便利でしょう。

f:id:yyhhyy:20191019184423j:plain

データソースの選択で、自分のスプレッドシートの「シート」を選択し、データが記載された範囲を入力します。この際、週次レポートなどであればどんどん下に行が増えていくはずですので、必要以上に多くしておくほうがベターです。

f:id:yyhhyy:20191019185029j:plain

注意点

たまに数値が数値として認識されていないことがありますので、各データの設定は必ず確認します。

f:id:yyhhyy:20191019185231j:plain

また、今回ISOの週を指標に選びましたのでこれも設定しておきます。そうでないとグラフ化の際に謎の6桁の数字が並んでしまいます。

f:id:yyhhyy:20191019185214j:plain

データのグラフへの描画

いよいよデータのグラフ化です。今回は週次レポートに求められるケースをピックアップしています。

時系列棒グラフ

先ず、週単位のページビューを時系列でグラフ化してみましょう。「アクセスが伸びているのか?落ちているのか?最近上がったのはいつか?」といったことは、意思決定の有無とは別に、よく質問される項目だと思われます。

「グラフを追加」から時系列グラフを選びます。

f:id:yyhhyy:20191019185630j:plain

デフォルトでは折れ線グラフですが、週単位集計のページビューは非連続データだと思っていますので、個人的に棒グラフにするべきものだと思っています。右側のパネルで変更ができます。

f:id:yyhhyy:20191019185842j:plain

f:id:yyhhyy:20191019185900j:plain

軸のフォントのサイズなどを調整して見やすいグラフにしてきます。エクセルのように細かく設定はできませんがデフォルトでも見やすく作られているので、許容範囲でしょう。

f:id:yyhhyy:20191019190039j:plain

表とスコアカード

「どのページがよく見られれているのか?」も問われやすい項目です。表の中に棒グラフと数値が入るパターンを選びましょう。残念ながら表示できる行が往々にして制限が出ます。どこかで足切りを良しとする必要があります。

f:id:yyhhyy:20191019190530j:plain

また、「トータルで今、何人来ているんだ?」もよく聞かれることですので、「スコアカード」でPV,UUなどを表示しています。

棒グラフ

また、流入元の分析であれば、流入元別に棒グラフも必要かもしれません。私の拙いサイトでは何もパラメータを付与していませんが、ここでGoogle推奨のパラメータを表記していれば、綺麗に分類できるはずです。

f:id:yyhhyy:20191019190648j:plain

スケジュール化

Data Portal

Google Data Portalの結果は、このページ自体を共有設定することもできますが、なかなか現実的には外部企業とダッシュボード共有はしないと思われます。ダウンロードしてPDFにすることができますので、このPDFを共有するようにします

f:id:yyhhyy:20191019190946j:plain

共有から自分のアカウントに転送できます。この際には、PDFが添付されたメールになります。

f:id:yyhhyy:20191019191028j:plain

この先は、自分でメールを送るもよし、Gmailに転送設定するもよしです。ただしファイル名が文字化けされるので注意してください。

Google スプレッドシート

また、Data Portalのスケジュール化と同時に、Google スプレッドシートの方でも定期レポート化設定が必要です。Schedule Report から設定します。

f:id:yyhhyy:20191019191310j:plain

Data Portalよりも前の時間帯で更新されるようにしておきます。

f:id:yyhhyy:20191019191427j:plain

以上で設定は終わりです。

後は、毎週決まった時刻にこのPDFがメールで来るようになります。


Google Data Studioについての本は少なく、私もスプレッドシートをソースにできると知ったのはこの本でした。この本では、説明相手によって出すべきレポートが異なることなど、もう少し前段階から説明されているただの紹介本とは異なる良い本です。

同じグラフ作成・レポート作成という意味では以下の本がシンプルで良いでしょう。3

最後に、Google Analyticsに関する本も紹介しておきます。正直Google Analyticsに関してはすぐに指標も変わりますので、書籍は概略を掴む程度で、後はひたすら実際に触って、ヘルプの解答をGoogle検索して、というのが良いでしょう。


  1. 連携方法を紹介していると煩雑になりますのでそこまではググッて下さい。そんなに難しくありません。

  2. 詳細を知りたければこちらもググると出てきます。

  3. 私はこの本を読んでから、色盲色弱への配慮として強調色をブルーに変えました。なかなか「私、色盲なんです」と自ら申告はして頂けないものです。特に赤はグレーと見分けがつかないことで有名で、強調色としていつまでも使うのは恥ずかしいことです。

技術的な知見がある人こそが「重要なマーケティング」をできる

最近立て続けに中国企業の成長を探る本を読み、モヤモヤとしていたことが瓦解しました。

このエントリーにまとめたことは上、2つの本を読めば実質的に解決します

マーケティングという言葉の幻想について

最近「マーケティング」という言葉が反乱していて、色々な人がポジショントークをしているように感じます。もともと定義がしっかり決まっている職種でもないのと、日本の場合は「なんか凄そう」というイメージもついているせいか、言葉を拡大解釈して自分のセールスに使っている人も多い印象があります。

様々な立場にとっての「マーケティング

私が関わって来た中で、マーケティングという言葉は大きく以下の3つの使われ方があるように思います

以降、簡潔に紹介しますが、少し横道なので以下3ブロックは読み飛ばしても問題ありません

戦略コンサルタントの使う「マーケティング

マッキンゼーやボストンコンサルティングといった所謂戦略コンサルタントは、製品のラインナップを確認し、

  • ライフサイクル上、衰退期に居る商品部門を縮小する
  • カニバリゼーションを起こしている商品部門を解体する
  • 販売チャンネルを見直し営業の人員配分を変える
  • 競合優位性の薄いブランドの売却する

といった形で、大きな視点での改善を提案していきます。彼らはこのようなレベルで「マーケティング」という言葉を使います。よくみるSWOT分析や製品のライフサイクルなどは彼らの提唱によるものだったと記憶していますが、会社全体を視野に入れて事業部の力配分にメスを入れることを期待されています。

コンサルタントだけを生業にしてきた人は、分析力を買われているのだと思っている節があるようなのですが、傍目から見ていると分析力自体は普通で、そんなことよりも外部の人だからこそできる部門横断のヒアリングと取り潰しという内部でやると軋轢が生まれそうな組織と組織の壁を壊す精神的なストレス代行に高い報酬が支払われているように見えます。

BtoBソリューション提供企業が使う「マーケティング

最近「マーケティング」という言葉を反乱させているのはこちらのタイプの方々のように思います。

顧客にセールスするのが営業それをサポートするのがマーケティング

簡単に言うとそういう意図で使われているようです

  • PRで自社製品が必要とされる社会的気運を高める
  • ホワイトペーパーを作り展示会に出展しサイトやメルマガのコンテンツで見込み顧客のリストと優先順位をつけて営業に渡す

私は広告代理店の人間なので「それはプロモーションなのでは?」と思ってしまうのですが、この用法は間違っているわけではなく、伝統的な企業でも「~マーケティング」という子会社は、だいたい「販売用子会社」ということがあります。

最近はBtoBセールス分野のソリューション市場が活発なため、「SNSマーケティング」や「WEBマーケティング」といった形で、「マーケティング」という用語が氾濫しています

広告代理店が使う「マーケティング

これも恐らく使い方としてあっているわけではないのですが、巷の書店に平積みされる広告代理店の人や出身者が書いた本にも「マーケティング」ということが乱立しています。

  • プロモーションの戦略(とくにBtoC)
    • ターゲットインサイトの調査と分析
    • コミュニケーションの費用を投下すべき戦略ターゲットの選定
    • 3C分析やPEST分析を踏まえた、世間の文脈へのブランドのポジションの再定義

という意味で使われていることが多いと思います。

戦略コンサルタント目線では「Price、Place、Product、Promotion」がマーケティングの4つのPであって「Promotion」に特化する場合は、マーケティングと呼ぶと誤解を招くと思いますが、一般の人にはもやはこっちの意味での「マーケティング」の方が馴染みがあるかもしれません。

「製品企画時点でターゲットは決まっているべき」なのですが、実態としては既存製品の延長で世に出ている商品も多く、「商品は何も変わらないのだけど見せ方や売り込む相手を変えてもう一度、売れる商品に返り咲きたい」というニーズは極めて多いです。果たして製品の見直しをせずにできる範囲は限られているのではないか?とも思いますが、他部門と軋轢を生むことなく既存プロセスの延長で拡販できるという精神的負担の少なさが、このニーズを生み出しているのでしょう。

より重要な視点は「売れる仕組み作り」

それぞれの立場から「マーケティングとは何か?」を哲学者のように議論し続けることも趣味としては良いのだと思いますが、より重要なことは、「どうやって売れる仕組み作りをするか?」ということだと思います。商品企画寄りであろうと販売促進寄りであろうとプロモーション寄りであろうと、最終的な目的地は、自社製品を都合よく市場で生き残れせることがゴールのはずです。

そしてそのレベルを目標にすると、どうしてもビジネスモデル自体を改善しないと到達しないことに気がついていくはずです。

企業の社会的存在意義とビジネスモデルの本質

ところで企業が社会に存在する意義は何でしょうか?最近読んだ本にわかりやすい説明がありましたが、

社会の効率をより良くする

という解釈がもっともしっくりくるのではないでしょうか?

一人一人が農作業から道具作りまで全て自給自足していると多忙で死んでしまいますが、より得意な人に集積し役割分担することで社会全体の効率が上がっていく、お互いの便益の提供の媒体として「費用」を払う。企業が高い報酬を得ることが許されるのは、足元を見て暴利を貪るからではなく、その費用を払ってでもやってもらった方がトータルでは安いからということでしょう。

徹底的に効率化をはかる中国企業

以下は、冒頭に挙げた2冊の本からの受け売りですが、

  • Xiaomiは80%のユーザーの80%の満足を満たすことで安くて良いものを提供することに成功した
  • Alibabaのリアル店舗は中間流通業者を極力排除し自分たちの強みを活かせる店舗で勝負をした
    • ECサイトでユーザーを把握しているのでどのエリアに高所得者が居てどの商品が売れるのか知っている
    • 思いついてすぐ用意できる30分という時間に注目した30分以内配達サービスのために売れそうな商品を近くの倉庫に持ってくる

など、伸びている中国企業は徹底的にコストダウンと顧客サービス向上を両立させています。

  • 日本の企業は、ユーザー満足度を上げるために不必要に製品価格を上げてしまう
  • 米国のデータ活用企業はユーザーの購買履歴をサービス向上には使わずにメルマガやポップアップ広告に使う

といった残念な方向へ進んでいますが、生き残るのは中国企業のように効率化を如何に達成するか?に集中している企業でしょう。

安くて良いものは当然ながら口コミで広まっていきますので、プロモーションで不必要にイメージアップにコストをかける必要もありません。

技術的価値提供ができる投資家

余談として紹介しますが、Xiaomiは投資スタンスも独特です。「ただお金を出す」のではなく、「自社の技術やブランドが相手のメリットになるか?」で投資先を決めています。

  • Xiaomiのブランドを冠するとローンチ時に売れやすくなる
  • Xiaomiの要素技術を提供すると伸びる

などの視点があるため、ベンチャー企業側も、ただお金を出すだけで製品開発に協力してくれない投資家よりもメリットが多く、ただのベンチャーキャピタルよりもXiaomiと組むことを選ぶことがあるようです。

当たるか当たらないかわからないけれどリスクマネーを投じなければならない文系だけの投資会社には真似をすることは難しいと考えると、今後投資家という世界も変わってくるでしょう。

技術を理解した人だけが「売れる仕組み作り」に貢献できる

Xiaomiの例もさることながら、中国以外の企業でも、更には昔から「技術を理解した人が商品を売れるしょうにできる」という事象は同じようです

という本では、「技術開発部門の人がリーダーとなって、ユーザー調査をし市場の課題を把握し、自分の知識の足りない要素技術も他部門や外部の人と折衝し、社内の意思決定者の説得をし、商品ローンチ後のサポートと改善まで把握して商品を世に送り出していくさま」が様々な事例とともに描かれています。

終わりに(自身の実感として)

広告代理店のセールパーソンとして関わる中でも実感をしていますが、ターゲットのインサイトを深堀りし、プロモーション上でこの人はコストをかけて対応すべきターゲットだ、この人は違う、といった判断をしているのは、意外にも製品企画の立場の人であったりします。本業は製品企画であり製品の開発・改善の方が業務量は多いのですが、意欲的にコミュニケーションの戦略にも関わってきます。とても大変だと思いますが、結局のところ製品の技術改善やプロセスの改善ができる人だけが「売れる仕組み作り」の全体を俯瞰することができるのでしょう。

マーケティングとは何か?」の議論も大切ですが、このようなポジションの人が今後、企業の中心を担っていくのだと思います。

Pythonでアンケート調査のクラスター分析と決定木分析を行う

アンケート調査の分析をするのはマーケティング担当者で、恐らく大学時代は社会学や心理学といった文系出身だと思います。昔ならSPSS、最近ならRだと思います。

一方で、Pythonはどちらかというと情報学系の人やシステムエンジニアが使うツール(言語)でPythonでアンケート分析を真っ向からしている書籍は存外少ないものです。最近私はRからPythonへの全面的な移行を考えているのですが、備忘録も兼ねて、Pythonでアンケート調査を行ってみました。

事前準備・前処理

先ずは予め読み込んでおいた方が良いLibrary類をインポートしておきます。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

seabornのテーマをデフォルトで選ぶようにしておきます。

#sns.set()
sns.set(font="Noto Sans CJK JP")

データの準備

アンケート調査のクラスター分析のためには集計されたクロス表ではなく、その前のローデータ(0,1の回答レコードの行列)が必要なのですが、なかなか個票データを公開しているところがなく、UCIのサイトから拝借することにしました。1

http://archive.ics.uci.edu/ml/datasets.php

ある都市に対する満足度の評価です

http://archive.ics.uci.edu/ml/datasets/Somerville+Happiness+Survey

提供されているCSVファイルがWindows文字コードだったようですのでエンコードの指定をして読み込みます2

df = pd.read_csv("data/SomervilleHappinessSurvey2015_2.csv",
                 encoding="cp932",
                 header=0)
df.head(3)
D X1 X2 X3 X4 X5 X6
0 0 3 3 3 4 2 4
1 0 3 2 3 5 4 3
2 1 5 3 3 3 3 5

各設問についてはMCUのサイトに以下の補足があります。 今のままではわかりにくいので簡単な列名に変えたいと思います3

D = decision attribute (D) with values 0 (unhappy) and 1 (happy) 
X1 = the availability of information about the city services 
X2 = the cost of housing 
X3 = the overall quality of public schools 
X4 = your trust in the local police 
X5 = the maintenance of streets and sidewalks 
X6 = the availability of social community events 

Attributes X1 to X6 have values 1 to 5.
df = df.rename(columns={
    "D":"幸福かどうか",
    "X1":"行政サービス情報へのアクセスしやすさ",
    "X2":"住宅供給の高さ",
    "X3":"公立の学校の全般的な質の良さ",
    "X4":"地域警察への信頼の高さ",
    "X5":"道路・歩道のメンテナンス状況",
    "X6":"地域社会行事の利用のしやすさ"})
df.head(3)
幸福かどうか 行政サービス情報へのアクセスしやすさ 住宅供給の高さ 公立の学校の全般的な質の良さ 地域警察への信頼の高さ 道路・歩道のメンテナンス状況 地域社会行事の利用のしやすさ
0 0 3 3 3 4 2 4
1 0 3 2 3 5 4 3
2 1 5 3 3 3 3 5

何を分析するか?」をまだ決めていませんでしたが、例えば、「幸福度の高い人は市のどこを評価しているのか?」を探ることにしてみましょう。

その場合、「幸福だ」と答えているデータだけに絞る必要があります。

df_happy = df[df["幸福かどうか"]==1]
df_happy.head(3)
幸福かどうか 行政サービス情報へのアクセスしやすさ 住宅供給の高さ 公立の学校の全般的な質の良さ 地域警察への信頼の高さ 道路・歩道のメンテナンス状況 地域社会行事の利用のしやすさ
2 1 5 3 3 3 3 5
5 1 5 5 3 5 5 5
7 1 5 4 4 4 4 5

念のためにこれで何件あるか確認しましょう。あまりデータが少なすぎると分析の信憑性が下がります

len(df_happy)
77

また、幸福ではない人のデータは今回の分析では使わないので、「幸福かどうか」の列を削除します。

更に、インデックスの番号を新しいデータに対して振り直します。

df_happy = df_happy.drop("幸福かどうか", axis=1)
df_happy = df_happy.reset_index(drop=True)
df_happy.head(3)
行政サービス情報へのアクセスしやすさ 住宅供給の高さ 公立の学校の全般的な質の良さ 地域警察への信頼の高さ 道路・歩道のメンテナンス状況 地域社会行事の利用のしやすさ
0 5 3 3 3 3 5
1 5 5 3 5 5 5
2 5 4 4 4 4 5

データの標準化

5段階スケールできいている各設問について、「1」に集中しているものや、「5」に集中しているものがある筈です。4

df_happy.describe()
行政サービス情報へのアクセスしやすさ 住宅供給の高さ 公立の学校の全般的な質の良さ 地域警察への信頼の高さ 道路・歩道のメンテナンス状況 地域社会行事の利用のしやすさ
count 77.000000 77.000000 77.000000 77.000000 77.000000 77.000000
mean 4.545455 2.558442 3.415584 3.792208 3.831169 4.389610
std 0.679502 1.117958 1.004603 0.878660 1.056342 0.763576
min 3.000000 1.000000 1.000000 1.000000 1.000000 1.000000
25% 4.000000 2.000000 3.000000 3.000000 3.000000 4.000000
50% 5.000000 2.000000 3.000000 4.000000 4.000000 5.000000
75% 5.000000 3.000000 4.000000 4.000000 5.000000 5.000000
max 5.000000 5.000000 5.000000 5.000000 5.000000 5.000000

どの列も分散を等しくすることで、情報量を同じにできます。5

from sklearn import preprocessing
ss = preprocessing.StandardScaler()
df_happy_s = pd.DataFrame(ss.fit_transform(df_happy))
df_happy_s.head(3)
0 1 2 3 4 5
0 0.673326 0.397559 -0.416393 -0.907521 -0.791997 0.804625
1 0.673326 2.198267 -0.416393 1.383598 1.113745 0.804625
2 0.673326 1.297913 0.585552 0.238038 0.160874 0.804625

また列名が消えてしまいましたので振り直します。

幸い、先程の「df_happy」の列名をそのままコピペすればいいだけです

print(df_happy.columns)
Index(['行政サービス情報へのアクセスしやすさ', '住宅供給の高さ', '公立の学校の全般的な質の良さ', '地域警察への信頼の高さ',
       '道路・歩道のメンテナンス状況', '地域社会行事の利用のしやすさ'],
      dtype='object')
df_happy_s.columns = df_happy.columns
df_happy_s.head(3)
行政サービス情報へのアクセスしやすさ 住宅供給の高さ 公立の学校の全般的な質の良さ 地域警察への信頼の高さ 道路・歩道のメンテナンス状況 地域社会行事の利用のしやすさ
0 0.673326 0.397559 -0.416393 -0.907521 -0.791997 0.804625
1 0.673326 2.198267 -0.416393 1.383598 1.113745 0.804625
2 0.673326 1.297913 0.585552 0.238038 0.160874 0.804625

分析

因子分析

設問が多岐に渡るときは、設問を縮約すべきで、そういうときは因子分析を行いますが、今回は既に6問と少ないので行いません。

↓自分がやるまでもなく丁寧にまとめられた記事がありました。因子得点は fit_transformで出てきます。また、事前に自分で標準化していないと変な値になるので因子分析だけをするときも注意して下さい。

hk29.hatenablog.jp

scikit-learn.org

クラスター分析

「幸福度の高い人は市のどこを評価しているのか?」を探ると言っても、人によって重視ポイントが異なります。それを幾つかにタイプ分けをして把握するためにクラスター分析を行います

適切なクラスター数を探す

適切なクラスター数を決める手法に完全な決まりはありませんが、見た目で判断するために、階層的クラスタ分析を一度行うという手法がママあります。

from scipy.cluster.hierarchy import linkage, dendrogram

統計に細かい人にとってはどの手法を選ぶか?は重要だと思いますが、比較的一般的なユークリッド距離、ウォード法で階層的クラスター分析を行います

df_happy_s_hclust = linkage(df_happy_s,metric="euclidean",method="ward")
plt.figure(figsize=(12,8))
dendrogram(df_happy_s_hclust)
plt.savefig('figure_1.png')
plt.show()

f:id:yyhhyy:20190707224159p:plain

どの辺りで線をひくか?はかなり恣意的ですが、この場合は4グループぐらいが適切でしょうか?

f:id:yyhhyy:20190707224239p:plain

k平均法によるクラスター分析

実務においてはクラスターに分かれればそれでいいというわけではなく、解釈の容易性が求められます。それぞれのクラスターにはどういう違いがあるのか?そういったことを知るには、kmenasクラスター分析の方が便利です

from sklearn.cluster import KMeans

クラスター数を4つと決めたので引数に入れて関数を作成

km = KMeans(n_clusters=4,random_state=42)

skit-learnは直接pandasデータフレームを読み込まないのでnumpyの行列に変換する必要がある

df_happy_s_ar = df_happy_s.values
display(df_happy_s_ar)
array([[ 0.67332598,  0.39755886, -0.41639284, -0.90752108, -0.79199672,
         0.80462467],
       [ 0.67332598,  2.19826665, -0.41639284,  1.38359771,  1.11374539,
         0.80462467],
       [ 0.67332598,  1.29791276,  0.58555244,  0.23803832,  0.16087433,
         0.80462467],
       [ 0.67332598, -0.50279503,  0.58555244,  1.38359771,  1.11374539,
         0.80462467],
       [-2.28930833, -0.50279503,  0.58555244, -0.90752108,  0.16087433,
        -0.51359022],
       [ 0.67332598, -1.40314893,  0.58555244, -0.90752108,  0.16087433,
         0.80462467],
       [-0.80799118, -0.50279503, -0.41639284, -0.90752108,  0.16087433,
        -0.51359022],
       [-0.80799118, -0.50279503, -0.41639284, -0.90752108,  0.16087433,
        -0.51359022],
       [ 0.67332598, -1.40314893, -1.41833812,  1.38359771, -1.74486778,
        -0.51359022],
       [-0.80799118,  0.39755886, -0.41639284, -0.90752108, -0.79199672,
        -0.51359022],
       [-2.28930833,  0.39755886, -0.41639284,  1.38359771,  1.11374539,
         0.80462467],
       [-2.28930833,  0.39755886, -2.4202834 , -0.90752108, -0.79199672,
        -0.51359022],
       [-2.28930833,  0.39755886, -2.4202834 , -0.90752108, -0.79199672,
        -0.51359022],
       [ 0.67332598,  0.39755886, -0.41639284, -0.90752108,  1.11374539,
        -1.83180511],
       [-2.28930833, -0.50279503,  0.58555244,  0.23803832,  0.16087433,
         0.80462467],
       [-2.28930833, -0.50279503,  0.58555244,  0.23803832,  0.16087433,
         0.80462467],
       [-0.80799118, -1.40314893, -0.41639284, -3.19863987, -2.69773883,
        -0.51359022],
       [ 0.67332598,  0.39755886,  0.58555244, -0.90752108,  0.16087433,
         0.80462467],
       [ 0.67332598,  0.39755886,  0.58555244, -0.90752108,  0.16087433,
         0.80462467],
       [ 0.67332598, -0.50279503, -0.41639284, -0.90752108, -1.74486778,
         0.80462467],
       [-0.80799118,  1.29791276, -0.41639284,  0.23803832, -1.74486778,
        -0.51359022],
       [-0.80799118, -0.50279503,  0.58555244, -0.90752108, -1.74486778,
        -0.51359022],
       [-2.28930833, -1.40314893, -1.41833812,  0.23803832, -0.79199672,
         0.80462467],
       [ 0.67332598,  0.39755886,  0.58555244,  0.23803832,  0.16087433,
         0.80462467],
       [ 0.67332598,  0.39755886, -0.41639284,  0.23803832,  0.16087433,
         0.80462467],
       [ 0.67332598, -0.50279503,  1.58749772,  1.38359771,  1.11374539,
        -1.83180511],
       [ 0.67332598, -1.40314893, -0.41639284, -0.90752108,  0.16087433,
        -0.51359022],
       [ 0.67332598, -1.40314893, -0.41639284, -0.90752108,  0.16087433,
        -0.51359022],
       [ 0.67332598, -1.40314893, -0.41639284, -0.90752108,  0.16087433,
        -0.51359022],
       [ 0.67332598, -0.50279503,  0.58555244, -0.90752108,  0.16087433,
         0.80462467],
       [ 0.67332598, -0.50279503,  0.58555244, -0.90752108,  0.16087433,
         0.80462467],
       [-0.80799118,  0.39755886, -1.41833812,  0.23803832, -0.79199672,
        -0.51359022],
       [-0.80799118,  0.39755886, -1.41833812,  0.23803832, -0.79199672,
        -0.51359022],
       [ 0.67332598,  0.39755886,  1.58749772,  1.38359771,  0.16087433,
         0.80462467],
       [ 0.67332598, -0.50279503,  0.58555244, -2.05308048, -1.74486778,
        -0.51359022],
       [-0.80799118,  1.29791276, -0.41639284, -0.90752108, -1.74486778,
         0.80462467],
       [ 0.67332598, -0.50279503,  0.58555244,  0.23803832,  1.11374539,
         0.80462467],
       [ 0.67332598, -0.50279503,  0.58555244,  0.23803832,  1.11374539,
         0.80462467],
       [-0.80799118, -1.40314893, -0.41639284,  0.23803832,  0.16087433,
        -0.51359022],
       [-0.80799118, -1.40314893, -0.41639284,  0.23803832,  0.16087433,
        -0.51359022],
       [ 0.67332598, -1.40314893,  1.58749772,  1.38359771,  1.11374539,
         0.80462467],
       [ 0.67332598,  1.29791276,  1.58749772,  1.38359771,  1.11374539,
         0.80462467],
       [-0.80799118, -0.50279503, -1.41833812,  0.23803832,  0.16087433,
         0.80462467],
       [-0.80799118,  0.39755886, -0.41639284,  0.23803832, -0.79199672,
        -0.51359022],
       [-0.80799118,  0.39755886, -0.41639284,  0.23803832, -1.74486778,
        -0.51359022],
       [ 0.67332598, -0.50279503,  1.58749772,  1.38359771,  1.11374539,
         0.80462467],
       [ 0.67332598, -0.50279503, -0.41639284,  1.38359771,  1.11374539,
         0.80462467],
       [ 0.67332598,  2.19826665,  1.58749772,  1.38359771,  1.11374539,
         0.80462467],
       [-0.80799118,  0.39755886, -1.41833812,  0.23803832,  0.16087433,
        -0.51359022],
       [-0.80799118, -0.50279503,  0.58555244,  0.23803832,  0.16087433,
        -0.51359022],
       [ 0.67332598,  0.39755886, -1.41833812,  0.23803832,  0.16087433,
        -0.51359022],
       [ 0.67332598, -0.50279503, -0.41639284,  0.23803832,  0.16087433,
         0.80462467],
       [ 0.67332598,  0.39755886, -0.41639284, -0.90752108,  1.11374539,
         0.80462467],
       [ 0.67332598, -0.50279503,  0.58555244,  1.38359771,  0.16087433,
         0.80462467],
       [ 0.67332598, -1.40314893, -0.41639284,  0.23803832,  1.11374539,
         0.80462467],
       [ 0.67332598,  1.29791276,  1.58749772,  1.38359771,  1.11374539,
        -0.51359022],
       [ 0.67332598,  2.19826665, -0.41639284,  0.23803832,  0.16087433,
         0.80462467],
       [ 0.67332598,  1.29791276,  0.58555244, -0.90752108, -0.79199672,
        -0.51359022],
       [ 0.67332598,  1.29791276,  0.58555244,  0.23803832,  0.16087433,
         0.80462467],
       [ 0.67332598,  2.19826665,  1.58749772,  1.38359771,  1.11374539,
         0.80462467],
       [ 0.67332598,  1.29791276,  1.58749772,  0.23803832,  1.11374539,
        -0.51359022],
       [-0.80799118, -0.50279503, -0.41639284,  0.23803832, -0.79199672,
        -1.83180511],
       [ 0.67332598, -0.50279503, -1.41833812,  0.23803832,  1.11374539,
         0.80462467],
       [ 0.67332598,  0.39755886, -1.41833812,  0.23803832,  0.16087433,
         0.80462467],
       [ 0.67332598,  0.39755886,  0.58555244, -0.90752108,  0.16087433,
        -1.83180511],
       [-0.80799118,  0.39755886,  0.58555244,  0.23803832, -0.79199672,
        -0.51359022],
       [ 0.67332598, -1.40314893,  0.58555244, -0.90752108,  1.11374539,
         0.80462467],
       [ 0.67332598, -1.40314893,  1.58749772, -0.90752108,  1.11374539,
         0.80462467],
       [ 0.67332598,  0.39755886,  0.58555244,  0.23803832,  0.16087433,
        -0.51359022],
       [ 0.67332598, -0.50279503,  0.58555244,  0.23803832, -1.74486778,
        -1.83180511],
       [-2.28930833,  1.29791276,  0.58555244,  1.38359771, -2.69773883,
        -1.83180511],
       [ 0.67332598, -1.40314893,  1.58749772,  1.38359771,  1.11374539,
         0.80462467],
       [-0.80799118,  0.39755886, -0.41639284,  0.23803832,  0.16087433,
        -0.51359022],
       [ 0.67332598,  2.19826665, -2.4202834 , -3.19863987,  1.11374539,
        -4.4682349 ],
       [ 0.67332598, -0.50279503, -0.41639284,  0.23803832,  0.16087433,
        -1.83180511],
       [ 0.67332598, -0.50279503, -0.41639284,  0.23803832, -1.74486778,
         0.80462467],
       [ 0.67332598,  0.39755886, -0.41639284,  0.23803832,  0.16087433,
         0.80462467]])

kmeansを適用した結果のグルーピングの配列が出力として渡される

df_happy_s_ar_pred = km.fit_predict(df_happy_s_ar)
display(df_happy_s_ar_pred)
array([3, 3, 3, 0, 1, 0, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 2, 1, 2,
       1, 3, 3, 0, 2, 2, 2, 0, 0, 1, 1, 3, 2, 1, 0, 0, 2, 2, 0, 3, 1, 1,
       1, 0, 0, 3, 1, 1, 2, 0, 3, 0, 0, 3, 3, 3, 3, 3, 3, 2, 0, 3, 2, 1,
       0, 0, 3, 2, 1, 0, 1, 2, 2, 1, 3])

割り振られた結果を元の(標準化する前の)データにクラスターIDとして一列追加する

df_happy_clust = df_happy[:]
df_happy_clust["cluster_ID"] = df_happy_s_ar_pred
df_happy_clust.head(3)
行政サービス情報へのアクセスしやすさ 住宅供給の高さ 公立の学校の全般的な質の良さ 地域警察への信頼の高さ 道路・歩道のメンテナンス状況 地域社会行事の利用のしやすさ cluster_ID
0 5 3 3 3 3 5 3
1 5 5 3 5 5 5 3
2 5 4 4 4 4 5 3

因みにこのままではクラスターIDも数値として扱われてしまいます

print(df_happy_clust.dtypes)
行政サービス情報へのアクセスしやすさ    int64
住宅供給の高さ               int64
公立の学校の全般的な質の良さ        int64
地域警察への信頼の高さ           int64
道路・歩道のメンテナンス状況        int64
地域社会行事の利用のしやすさ        int64
cluster_ID            int32
dtype: object

カテゴリカル変数に変えておきましょう

df_happy_clust["cluster_ID"] = df_happy_clust["cluster_ID"].astype("category")
print(df_happy_clust.dtypes)
行政サービス情報へのアクセスしやすさ       int64
住宅供給の高さ                  int64
公立の学校の全般的な質の良さ           int64
地域警察への信頼の高さ              int64
道路・歩道のメンテナンス状況           int64
地域社会行事の利用のしやすさ           int64
cluster_ID            category
dtype: object

クラスタが何人くらいになったのかを把握しておきます

print(df_happy_clust["cluster_ID"].value_counts())
1    22
3    20
2    18
0    17
Name: cluster_ID, dtype: int64

まぁまぁまどのクラスターも同じ人数ですね

クラスターの分析

クラスターでグルーピングし、回答の片寄りがどこに出たのか?を確認します。

今回は、間隔尺度の設問でしたので単純に平均するのは乱暴ですので、踏まえて更にデータを再構築する必要があります。

df_happy_clust = df_happy_clust[:].astype("category")
print(df_happy_clust.dtypes)
行政サービス情報へのアクセスしやすさ    category
住宅供給の高さ               category
公立の学校の全般的な質の良さ        category
地域警察への信頼の高さ           category
道路・歩道のメンテナンス状況        category
地域社会行事の利用のしやすさ        category
cluster_ID            category
dtype: object

ダミー変数化したい列を指定するために列名を取得してリスト化、更にクラスタIDを除きます

dummy_list = list(df_happy_clust.columns)[0:-1]
print(dummy_list)
['行政サービス情報へのアクセスしやすさ', '住宅供給の高さ', '公立の学校の全般的な質の良さ', '地域警察への信頼の高さ', '道路・歩道のメンテナンス状況', '地域社会行事の利用のしやすさ']

ダミー変数化したい列名を指定して全ての設問をダミー変数化します6

df_happy_clust_dmy = pd.get_dummies(df_happy_clust,columns=dummy_list)
df_happy_clust_dmy.head(3)
cluster_ID 行政サービス情報へのアクセスしやすさ_3 行政サービス情報へのアクセスしやすさ_4 行政サービス情報へのアクセスしやすさ_5 住宅供給の高さ_1 住宅供給の高さ_2 住宅供給の高さ_3 住宅供給の高さ_4 住宅供給の高さ_5 公立の学校の全般的な質の良さ_1 ... 地域警察への信頼の高さ_5 道路・歩道のメンテナンス状況_1 道路・歩道のメンテナンス状況_2 道路・歩道のメンテナンス状況_3 道路・歩道のメンテナンス状況_4 道路・歩道のメンテナンス状況_5 地域社会行事の利用のしやすさ_1 地域社会行事の利用のしやすさ_3 地域社会行事の利用のしやすさ_4 地域社会行事の利用のしやすさ_5
0 3 0 0 1 0 0 1 0 0 0 ... 0 0 0 1 0 0 0 0 0 1
1 3 0 0 1 0 0 0 0 1 0 ... 1 0 0 0 0 1 0 0 0 1
2 3 0 0 1 0 0 0 1 0 0 ... 0 0 0 0 1 0 0 0 0 1

3 rows × 28 columns

クラスタIDでグループ化し数値を集約します

df_happy_clust_dmy_gp = df_happy_clust_dmy.groupby("cluster_ID")

グループ別に各設問の回答者数の合計を出します

df_happy_clust_dmy_gp_g = df_happy_clust_dmy_gp.sum().T
display(df_happy_clust_dmy_gp_g)
cluster_ID 0 1 2 3
行政サービス情報へのアクセスしやすさ_3 0 8 0 0
行政サービス情報へのアクセスしやすさ_4 0 12 7 0
行政サービス情報へのアクセスしやすさ_5 17 2 11 20
住宅供給の高さ_1 6 2 6 0
住宅供給の高さ_2 11 6 8 0
住宅供給の高さ_3 0 11 3 10
住宅供給の高さ_4 0 3 0 6
住宅供給の高さ_5 0 0 1 4
公立の学校の全般的な質の良さ_1 0 2 1 0
公立の学校の全般的な質の良さ_2 1 6 1 1
公立の学校の全般的な質の良さ_3 3 8 12 6
公立の学校の全般的な質の良さ_4 8 6 4 7
公立の学校の全般的な質の良さ_5 5 0 0 6
地域警察への信頼の高さ_1 0 0 2 0
地域警察への信頼の高さ_2 0 0 1 0
地域警察への信頼の高さ_3 5 5 9 5
地域警察への信頼の高さ_4 5 14 6 9
地域警察への信頼の高さ_5 7 3 0 6
道路・歩道のメンテナンス状況_1 0 1 1 0
道路・歩道のメンテナンス状況_2 0 5 4 0
道路・歩道のメンテナンス状況_3 0 8 1 2
道路・歩道のメンテナンス状況_4 5 7 10 11
道路・歩道のメンテナンス状況_5 12 1 2 7
地域社会行事の利用のしやすさ_1 0 0 1 0
地域社会行事の利用のしやすさ_3 1 1 5 0
地域社会行事の利用のしやすさ_4 0 14 11 4
地域社会行事の利用のしやすさ_5 16 7 1 16

最近読んだ本で知ったのですが、Jupyter notebookであれば、HTML上で各セルに棒グラフを入れたり、数値によって色をつけたりしてくれるので、是非活用したほうが良いです。

pandas.pydata.org

df_happy_clust_dmy_gp_g.style.bar(color="#4285F4")
cluster_ID 0 1 2 3
行政サービス情報へのアクセスしやすさ_3 0 8 0 0
行政サービス情報へのアクセスしやすさ_4 0 12 7 0
行政サービス情報へのアクセスしやすさ_5 17 2 11 20
住宅供給の高さ_1 6 2 6 0
住宅供給の高さ_2 11 6 8 0
住宅供給の高さ_3 0 11 3 10
住宅供給の高さ_4 0 3 0 6
住宅供給の高さ_5 0 0 1 4
公立の学校の全般的な質の良さ_1 0 2 1 0
公立の学校の全般的な質の良さ_2 1 6 1 1
公立の学校の全般的な質の良さ_3 3 8 12 6
公立の学校の全般的な質の良さ_4 8 6 4 7
公立の学校の全般的な質の良さ_5 5 0 0 6
地域警察への信頼の高さ_1 0 0 2 0
地域警察への信頼の高さ_2 0 0 1 0
地域警察への信頼の高さ_3 5 5 9 5
地域警察への信頼の高さ_4 5 14 6 9
地域警察への信頼の高さ_5 7 3 0 6
道路・歩道のメンテナンス状況_1 0 1 1 0
道路・歩道のメンテナンス状況_2 0 5 4 0
道路・歩道のメンテナンス状況_3 0 8 1 2
道路・歩道のメンテナンス状況_4 5 7 10 11
道路・歩道のメンテナンス状況_5 12 1 2 7
地域社会行事の利用のしやすさ_1 0 0 1 0
地域社会行事の利用のしやすさ_3 1 1 5 0
地域社会行事の利用のしやすさ_4 0 14 11 4
地域社会行事の利用のしやすさ_5 16 7 1 16

このままの表が表示できない環境のであれば、グラフ化する必要があります。

plt.figure(figsize=(12,8))
sns.clustermap(df_happy_clust_dmy_gp_g,cmap="viridis")
plt.savefig('figure_2.png')
plt.show()

f:id:yyhhyy:20190707224604p:plain

ただ、無理にグラフにするより、スプレッドシートに吐き出して条件付き書式で見た方が楽だとは思います

df_happy_clust_dmy_gp_g.to_csv("df_happy_clust_dmy_gp_g.csv")

クラスター分析で各クラスターについての説明を考えるのはいつも恣意的ですが、例えば以下のように分類できます。

  • クラスター「0」はどの項目にも高い評価で満足し、住宅も高くないと感じているようです。
  • クラスター「1」は学校の質は低いと思っていますが、市には満足をしている。
  • クラスター「2」は警察の質は低いと思っていますが、市には満足しています。
  • クラスター「3」は住宅は高いと感じていますが、市には満足している。

それぞれ幸福度は高いわけですから、評価が低い箇所についてはあまり気にしていないということと同義だと判断しました。もちろんこれ以外の着眼点で分けるのも良いでしょう。

決定木分析

最後に、仮に今後新しく取得されたアンケートデータから、各クラスターに回答者を振り分けたい。となれば、様々な機械学習手法が可能なのですが、一般的な人にも理解して貰いやすい、という意味では、決定木分析が良いのではないかと思います。

先ず先程のデータをラベルとデータとにわけ、どちらもnumpyの配列にします

y = np.array(df_happy_clust["cluster_ID"].values)
display(y)
array([3, 3, 3, 0, 1, 0, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 2, 1, 2,
       1, 3, 3, 0, 2, 2, 2, 0, 0, 1, 1, 3, 2, 1, 0, 0, 2, 2, 0, 3, 1, 1,
       1, 0, 0, 3, 1, 1, 2, 0, 3, 0, 0, 3, 3, 3, 3, 3, 3, 2, 0, 3, 2, 1,
       0, 0, 3, 2, 1, 0, 1, 2, 2, 1, 3], dtype=int64)
X = df_happy_clust.drop("cluster_ID",axis=1).values
display(X)
array([[5, 3, 3, 3, 3, 5],
       [5, 5, 3, 5, 5, 5],
       [5, 4, 4, 4, 4, 5],
       [5, 2, 4, 5, 5, 5],
       [3, 2, 4, 3, 4, 4],
       [5, 1, 4, 3, 4, 5],
       [4, 2, 3, 3, 4, 4],
       [4, 2, 3, 3, 4, 4],
       [5, 1, 2, 5, 2, 4],
       [4, 3, 3, 3, 3, 4],
       [3, 3, 3, 5, 5, 5],
       [3, 3, 1, 3, 3, 4],
       [3, 3, 1, 3, 3, 4],
       [5, 3, 3, 3, 5, 3],
       [3, 2, 4, 4, 4, 5],
       [3, 2, 4, 4, 4, 5],
       [4, 1, 3, 1, 1, 4],
       [5, 3, 4, 3, 4, 5],
       [5, 3, 4, 3, 4, 5],
       [5, 2, 3, 3, 2, 5],
       [4, 4, 3, 4, 2, 4],
       [4, 2, 4, 3, 2, 4],
       [3, 1, 2, 4, 3, 5],
       [5, 3, 4, 4, 4, 5],
       [5, 3, 3, 4, 4, 5],
       [5, 2, 5, 5, 5, 3],
       [5, 1, 3, 3, 4, 4],
       [5, 1, 3, 3, 4, 4],
       [5, 1, 3, 3, 4, 4],
       [5, 2, 4, 3, 4, 5],
       [5, 2, 4, 3, 4, 5],
       [4, 3, 2, 4, 3, 4],
       [4, 3, 2, 4, 3, 4],
       [5, 3, 5, 5, 4, 5],
       [5, 2, 4, 2, 2, 4],
       [4, 4, 3, 3, 2, 5],
       [5, 2, 4, 4, 5, 5],
       [5, 2, 4, 4, 5, 5],
       [4, 1, 3, 4, 4, 4],
       [4, 1, 3, 4, 4, 4],
       [5, 1, 5, 5, 5, 5],
       [5, 4, 5, 5, 5, 5],
       [4, 2, 2, 4, 4, 5],
       [4, 3, 3, 4, 3, 4],
       [4, 3, 3, 4, 2, 4],
       [5, 2, 5, 5, 5, 5],
       [5, 2, 3, 5, 5, 5],
       [5, 5, 5, 5, 5, 5],
       [4, 3, 2, 4, 4, 4],
       [4, 2, 4, 4, 4, 4],
       [5, 3, 2, 4, 4, 4],
       [5, 2, 3, 4, 4, 5],
       [5, 3, 3, 3, 5, 5],
       [5, 2, 4, 5, 4, 5],
       [5, 1, 3, 4, 5, 5],
       [5, 4, 5, 5, 5, 4],
       [5, 5, 3, 4, 4, 5],
       [5, 4, 4, 3, 3, 4],
       [5, 4, 4, 4, 4, 5],
       [5, 5, 5, 5, 5, 5],
       [5, 4, 5, 4, 5, 4],
       [4, 2, 3, 4, 3, 3],
       [5, 2, 2, 4, 5, 5],
       [5, 3, 2, 4, 4, 5],
       [5, 3, 4, 3, 4, 3],
       [4, 3, 4, 4, 3, 4],
       [5, 1, 4, 3, 5, 5],
       [5, 1, 5, 3, 5, 5],
       [5, 3, 4, 4, 4, 4],
       [5, 2, 4, 4, 2, 3],
       [3, 4, 4, 5, 1, 3],
       [5, 1, 5, 5, 5, 5],
       [4, 3, 3, 4, 4, 4],
       [5, 5, 1, 1, 5, 1],
       [5, 2, 3, 4, 4, 3],
       [5, 2, 3, 4, 2, 5],
       [5, 3, 3, 4, 4, 5]], dtype=object)
from sklearn import tree

決定木は永遠に細かくできます。仮の数字で段階を決めてしまいます。

dtree = tree.DecisionTreeClassifier(max_depth=4)
dtree = dtree.fit(X,y)

作ったモデルがどの程度の精度なのか?確認してみます

dtree_pred = dtree.predict(X)
display(dtree_pred)
array([3, 3, 3, 0, 1, 0, 2, 2, 0, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 1, 1, 2,
       1, 3, 3, 0, 2, 2, 2, 0, 0, 1, 1, 3, 2, 1, 0, 0, 2, 2, 0, 3, 1, 1,
       1, 0, 0, 3, 1, 2, 2, 0, 3, 0, 0, 3, 3, 3, 3, 3, 3, 2, 0, 2, 2, 1,
       0, 0, 3, 2, 1, 0, 1, 2, 2, 1, 3], dtype=int64)

ラベルとどれくらいあっているか?正直、過学習してそうですが、今は面倒なのでこのまま進めます

sum(dtree_pred == y) / len(y)
0.948051948051948

jupyter notebookならgraphvizパッケージを使って可視化が可能です7

import pydotplus
from IPython.display import Image
from graphviz import Digraph

配列にした際に特徴量の名称が消えてしまっているので、列名を代入し、またクラスター名も数字のままだとエラーになるため、ラベルデータはastypeを使って文字列(string)に変更しておきます

dot_data = tree.export_graphviz(dtree,out_file=None,
                                feature_names = df_happy_clust.columns[0:-1],
                                class_names = y.astype("str"))

日本語を出力するにあたっては随分苦労しましたがこのブログが正解のようです。

mk-55.hatenablog.com

graph = pydotplus.graph_from_dot_data(dot_data)

graph.set_fontname('Noto Sans CJK JP')
for node in graph.get_nodes():
    node.set_fontname('Noto Sans CJK JP')
for e in graph.get_edges():
    e.set_fontname('Noto Sans CJK JP')

graph.write_png("dtree.png")
Image(graph.create_png())

f:id:yyhhyy:20190707224750p:plain

例えば、「行政サービス情報のアクセスしやすさ」が4.5以上つまり5で、「住宅供給の高さ」が2.5以上だとクラスター3だと判断されるわけですが、実際のデータと比べても特徴をよく表しています。

df_happy_clust_dmy[df_happy_clust_dmy["cluster_ID"]==3].sum().head(10)
cluster_ID              60.0
行政サービス情報へのアクセスしやすさ_3     0.0
行政サービス情報へのアクセスしやすさ_4     0.0
行政サービス情報へのアクセスしやすさ_5    20.0
住宅供給の高さ_1                0.0
住宅供給の高さ_2                0.0
住宅供給の高さ_3               10.0
住宅供給の高さ_4                6.0
住宅供給の高さ_5                4.0
公立の学校の全般的な質の良さ_1         0.0
dtype: float64

f:id:yyhhyy:20190707224827p:plain

分析からわかること

例えば、市であれば自分たちが改善できることできなことがあるはずで、対応できることを対応する場合、どのクラスタの市民に残って欲しいか?増えて欲しいか?を絞り込むことができます。

また、重点ターゲットとなるクラスタを決めたら、決定木のジャッジのポイントを元に訴求項目を作れば、どういう謳い文句が必要か?ということが自ずと決まってきます。

今回はアンケート項目が少なかったので行えませんでしたが、通常はクラスタ分析に使った質問以外にもっと多くの質問をしているはずなので、その質問で決定木分析を行うと

  • どんなメディアに接触しているのか?
  • どんな世代に多いのか?
  • どんなことに興味関心があるのか?

など、広告でターゲティングしやすいセグメントを元に決定木分析を行うこともできるでしょう


決定木以外にも通常のグラフで日本語が文字化けします。seabornを使う場合は、最初にフォントを指定しておけば大丈夫のようで、今のところ私もこれでうまく行っています

qiita.com


最近、Pythonの本がすごくわかりやすいものが増えてきました。今まではエンジニア出身の人の本が多かったですが、徐々にデータサイエンス側に向けて適切な分量の本が増えています

コンパクトにまとまっているけれどそれでも応用範囲が広い

こちらはドリルのような本でも知っていると便利

Pythonではないもの、データサイエンスにとって必要なものは何か?を丁寧に解説していて、この本は最初の頃に読んでおきたかったと思う。


  1. ここのサイト、機械学習に特化しているので、どういう目的で?どういうタイプのデータが欲しいか?をチェックしていくとデータの候補がセレクトされるんですよ!凄いですね。

  2. 因みにこのファイル文字コードが特殊でうまく読み込めず、一度Googleスプレッドシートで読み込んで再保存しています。

  3. 「cost_of_housing」と5段階スケールで聞いてますが、これは5の方が価格が高い、という意味ですかね?

  4. 片寄っていることの確認のため簡便にdescribeを使っていますが、そもそも間隔尺度は通常は平均などは意味がないので出しません。

  5. 学校のテストの偏差値と根本的な考えは同じです。みんなの点数が高かった数学の90点と、みんなの点数が低かった英語の90点とは平等に足すことはできません。

  6. drop_first=True」は今回は指定しない。

  7. Win10ではgraphivzのdot.exeをシステム環境PATHで通す必要があるようです。

本当のところヒットなんて誰にもわからない

先日読んだ本で積年の疑問が解決しました。

ゴッホが有名な印象派になれた理由

この本は様々なヒットについて解説がありました。中でも最も面白かったのは、「カイユボット事件」1です。

印象派の売れなかった作品を買ってあげていたカイユボットは、死後、遺産を美術館に寄贈してくれ、という遺言をし、ルノワールが尽力して彼の作品群を収めました。

当時、芸術的価値を世間一般に認められていなかった印象派のこの事件はメディアを通じて話題になり、「物議をかます印象派の作品とはどういうものだろうか?」と多くの人が訪れ、結果的にこのとき収蔵された作家群が、後々の印象派の主要人物だった、と認識されるようになるのです。

たまたま売れない作品の多かったゴッホの作品はカイユボットの遺産に含まれていたため、後世で評価されるに至ったのです。

ジャスティンビーバーがツイートしたら売れる

ヒットさせる最も簡単な方法は「ジャスティンビーバーにツイートしてもらう」ということ。かつてそれで一躍有名になった動画がありました。

shiba710.hateblo.jp

ゴッホがメディアを通じて有名になったのと同様。「既に伝播力のある誰かに紹介して貰う」ことがヒットに欠かせないポイントです。

例えば、アートのオークション市場でも有名なコレクターが評価した新人作家が評価され価格が上がっていくメカニズムです。

ヒットとはアンコントローラブル

狙ってジャスティンビーバーに紹介して貰えたりテイラー・スウィフトに賛同して貰えたらそれほど嬉しいことはありませんが、実際には意図的にできることではありません。

お墨付きと露出の繰り返し

しかし、人は社会的な生物なので、権威のある人のお墨付きに流れるということは永遠に続く法則ではないでしょうか?

フェラーリは創業以来広告をしていない」という都市伝説がありますが、彼らはF1のスポンサードをして社名の露出には余念がありませんし、雑誌の試乗レポートのお願いは常にしているようです。同じ車メーカーでも高級車メーカーは沢山ありますが、フェラーリは通常の広告枠とは違う形で効果的に宣伝費を投下しているということです。

IT企業が野球に投資する理由

同じように「ソフトバンク」という企業は知名度がありますが、ソフトバンクの本業が何なのか?わかる人はいないのではないでしょうか?携帯通信キャリアとしてはヴォーダフォンの国内事業を買収してからなのでその前から本業があるはずなのですが、、、プロ野球球団運営を始めてから知名度があがり、何ものなのか?わからなくても親近感があるのではないでしょうか?

どの本に書いてあったか忘れましたがいくつか読んだスポーツ関連のビジネス本に記載がありました。球団経営は大きな宣伝になるもののあくまで子会社の事業ということになるので宣伝費(販売管理費)として費用計上されない、というメリットがあるそうです。なぜソフトバンクDeNA楽天も球団を買うのか謎でしたが、会計知識がある人にとっては合理的な行為に写るのでしょう。


アートのオークション市場のメカニズムについてはこちら。

何度も紹介しますが車メーカーのブランディングについてはこの本が本当に腑に落ちます。


  1. 事件そのものについてはこのサイトが参考になります。(印象派支援、support as patron|カイユボット.net

コモディティ化する”マーケティング”と、コピーライター平賀源内

最近「Webマーケティング」「SNSマーケティング」といった「○○マーケター」と名乗る方をよく見ます。実際に仕事上でも「マーケティングの話をします~」という前置きもよく聞くようになりました。

一方で、日々の仕事の中で「マーケティング(狭義)」の限界も感じます。

マーケティングフレームワークについて

マーケティングフレームワークはすっかり一般化した

  • PEST分析
  • 3C分析
  • SWOT分析
  • 商品ライフサイクル
  • ライフタイムバリュー

etc...

といった用語を日常的に使う人が、SNS上でもリアルな取引先でも増えた印象があります。「外資コンサルタントが教える~」といった類の本が数多く出版され誰もが一度は読んだことがあるのでしょう。

フレームワークの限界

広告代理店の本業は、媒体枠の調達であり広告表現制作です。マーケティング戦略部分は通常、広告主側にいるマーケターがオリエンテーションとしてまとめます。しかし、往々にして”代理店さんの自由な発想を妨げたくない”という理由で、マーケティング戦略部分も白紙の状態でオリエンテーションとなるケースも多々あります。そういうときは、広告代理店側でもマーケティング戦略をまとめることがあります。1

その日々の中で感じるのが、マーケティングフレームワークモデルは恣意的である ということです。

SWOT分析の「強み」に何を入れるか・入れないか、資料作成者の意のままです。

「シェアが伸びていない」という事象を 「伸び代がある機会である」と捉えるか 「競合が伸びてきた驚異である」と捉えるか、 資料のストーリーの持って行き方次第です

フレームワークを埋めるだけなら特に知識は要らない

更に「フレームワーク」の危険なところは、特に勉強していない素人でも書き方を知っていれば書けてしまうということです。

調査分析の支援業務なら

といった能力や経験が必要なので、今日明日、本を読んで先輩のカバン持ちをしていた人が実行できることではありません。

一方で、SWOT分析マスを埋めることであれば、広告主にヒアリングしてオープンデータを漁れば、それっぽく埋めることはできます。

必要なのは解決策を作る力

コピーライター平賀源内

嘘か本当かわかりませんが、「土用の丑の日」に、元気が出るからうなぎを食べろ、という風習を作ったのは、平賀源内だと言われています。

結果、うなぎが絶滅危惧種になるなど消費が歪になってしまったことは失敗と言わざるを得ませんが、今までうなぎを食べる習慣がなかった人を振り返らせたという意味では、素晴らしい仕事だと言えるでしょう。

これがコピーライターの仕事の一つです

フレームワークで「土用の丑の日」は出てこない

誰もが注目していなかったうなぎについて、江戸時代の人が、SWOTでも3CでもPESTでも良いですが、何かフレームワークモデルで分析したところで、注目されていない興味を持ってくれる人はいないという月並みな結論が出てくるだけだったのではないでしょうか?

  • うなぎを食べると元気が出るってことにしちゃえ
  • 食べるきっかけに特別な日にしちゃえ

という結論が自動的に出てくるとは」思えません。

  • 夏バテに注目してから夏バテで苦労している人がこんなに居ます!ここにマーケットがあります!
  • うなぎは夏バテに効くエビデンスあります!

といったことを後から裏付けることはできますが、その逆はできません

人の価値観を変えることはできているか?

今まで振り向いてくれなかった人を振り向かせることが売れるようにする仕組みでは重要です。そしてその解決策は一つではありません。

例えば、iPhoneはボタンを無くしたから売れたのでしょうか?そんな端末は以前にもありました。「ボタンが邪魔だと思う人が一定数居る」までは調査データのとりまとめで思いついたとしても、実際に買わせるにはスワイプする行為が何かカッコ良かったとかアップルコンピューターはデザイナーなどイケてる人が支持してるといった憧れも必要だったはずです。2

プロダクトが解決することもあれば、キャッチコピーが解決することもあれば、デザインが解決することもあれば、社長のカリスマ性が解決することもあります。

しかし、フレームワークの整理からはいずれの解決策も登場しません。フレームワークは解決策を思いついた後の説得方法だという順番を間違えてはいけないでしょう。

コンサルタントとアドバイザーは異なる

実行力のあるコンサルタント

所謂戦略系コンサルティングファームの人は経営層と対面していますので、執行役員以下と相対する広告代理店では余り現場でお目にかかることがありません。

一方で、「~コンサル」といった個別の事業の専門家の方とはご一緒することがあります。総じて言えるのはコンサルタントは実行しているということです。

  • ヒアリングが必要ならフットワーク軽く聞きに行く
  • 事業計画のシミュレーションを作る
  • クライアントの縦割り組織を渡り歩いて根回しする
  • 足りないパートナーが居たら見つけてきてアサインする

といった解決策を策定することにコミットしていて、マーケティングフレームワークによる説明などは使っていない申し訳程度に入っているだけです

「年商○億」の人は、コンサルタントですか?アドバイザーですか?

  • 自分はもう充分稼いだんで支援する側に回ります!
  • ~コンサルするんで有料セミナーやりまーす!

知見の共有やTipsの共有はセミナーでも勉強会でも積極的な人が共有してくれますし、支援会社の人が業務として本を出したりWhitePaperにしてリード獲得しています。

フレームワークに限界があるように、残念ながらマーケティングにおける解決策のフェーズは再現性がないことが殆どです。踊らされないで実施経験を積むしか今のところ手段はないのでは?と思います。

「狭義のマーケティング」(=マーケティングフレームワーク)はかなりコモディティ化してしまいましたが、発言者が本当にコンサルタントなのか?ただの安全な立場から語るだけのアドバイザーなのか?見極めが必要でしょう。


フレームワークは戦略コンサルティングファームが作った説得手法に過ぎません。一方で、広告代理店のクリエイターも、プロモーションの一部しか担当していないのにすべて自分が解決したかのように本を書きます。

マーケティング業務について知るには、広告主側の本を読んだ方が良いでしょう。

→ 「確率思考の戦略論」の方が面白いのですが(ディリクレ分布やポワソン分布が出てきます)、組織改革しないと進まないものは進まないという現実は常に意識した方が良いでしょう。

→ 有名な広告制作会社に居たこともある人で今は広告主側にいる人、「宣伝費は自分のお金がだと思え」という例えはよく使わせて頂いています。

→ 森岡さんの別の本でも書いてありますがマーケターは「コスト」や「キャッシュフロー」「生産体制」も意識しなければ務まりません。好きなアドバイスをして終わりではなく、それが量産できるか?納期はいつか?そんなことも必要な視点です。


  1. 広告代理店の場合は、組織改革・販売チャネル改革・ポートフォリオの変更・価格改定といったことまでは踏み込まず、特定のブランド(商材)のプロモーション戦略策定が中心です。たまに商品開発としてネーミング・パッケージ・フレーバー開発”協力”があります。

  2. 誰もちゃんとなんでiPhoneを買ったのか?最初に買った人や、後追いで買った人を調査してませんね。そういば。

広告代理店の仕事は「マーケティング」ではなく「プロジェクトマネジメント」である

広告代理店出身の人が記事で「マーケティング」と言うとき、大抵は「プロモーション」のことにとどまっています。ビジネスを経験したことがない学生にとって、広告代理店がマーケティング業務をしていると誤解して入社してしまのは良くないことだと思います。

広告代理店は「マーケティング支援会社」である

最近極めて赤裸々な本が出版されました。広告代理店の営業マンの実感からも全くズレを感じない優れたヒアリングレポートです。

この中で「事業会社」と「支援会社」という区分がされていましたが、言い得て妙な表現だと思います。

例えば、広告代理店ができないマーケティング業務

  • ブランドのポートフォリオを変更する(生産する商品ラインナップを削る)
  • 価格設定を変える(値引きしない)
  • 流通チャンネルを変える(卸との条件を変える)

このような業務は「事業会社」が行うことで、せいぜい関わるとしたら「経営コンサルタント」が協力する程度で、「広告代理店が絡む」ということはありません。

マーケティングを4つの軸に分けることがありますが、 - Product - Price - Place - Promotion

このうち広告代理店が関わるのはプロモーション「だけ」となります。 1

何ならプロモーションの中の一部しかやってない

もっと言うと、 - 調査 - 店頭プロモーション - 広報

など、プロモーションの領域であっても、事業会社から広告代理店を通さずに、専門の支援会社に発注されることのケースの方が多いとすら言えます。2

そもそも「利益相反」している

マーケティングを担当している」と嘯く広告代理店の人にとって最も厳しい点はここでしょう。

広告代理店は、 - ペイドメディア(広告枠)を調達したときの媒体社へのバックマージン(コミッション) - 広告等に使用する制作物(例えばテレビCM)やキャンペーンの運営管理などの代行業務における管理手数料

などが収益源です。

本当にマーケティングをする事業会社の立場であれば、広告を買うお金は一円でも安くしたいですし、広告制作も自社内で制作して完結し人件費に見合うなら外注する必要はありません。

となると、自ずと広告代理店と事業会社は利益が相反することになります。3

では一体、広告代理店は何をしているのか?

プロモーションにおける - 広告クリエイティブ制作 - 広告枠買付け - キャンペーンの設計や事務局業務

が主な仕事です

実行するのは別の人

ただし、広告代理店の中に「デザイナー」や「監督」はいません。 - 広告クリエイティブ制作→制作会社(例えば、葵プロモーションなど) - 広告枠買付け→媒体社(例えば、フジテレビなど) - キャンペーンの設計や事務局業務→各種プロモーション協力の会社(いっぱいあります)

と全て実行部隊は外部にいます。

行っているのは「プロジェクトマネジメント」

事業会社と制作会社・媒体社・協力会社の間に入って何をしているのか?というとプロジェクトマネジメントです。

  • 事業会社の中で決裁権限を持つ人は誰か?
  • その人にプロモーション企画を通すために必要なリサーチや資料は何か?
  • 宣伝予算の中で最適な媒体費・制作費の配分は何か?
  • 商品ターゲットを踏まえた制作物のディレクションは?
  • キャンペーンスタートまでの進行スケジュールは?
  • 媒体社の広告原稿審査・タレント事務所の意見・監督の意見など途中で意見が割れそうなポイントは?

といった、広告制作の関係者の利害関係調整とリソース配分とスケジュール管理、という業務になります。全て事業会社で内製できるのであればしてしまってよいのですが、4 手が回らないので広告代理店に外部発注している、ということになります。

何のスキルを持ちたいか?

大企業であればあるほど広告代理店への依存度が高く、ハズキルーペのような特殊な事例を除けば、テレビタレントを起用した広告制作は、広告代理店や制作会社でないと企画立案の機会はないでしょう。一方で、マーケティングをしたくても大企業の中ではほぼ関係者への説明で一日が終わる、ということも稀ではありませんし、事業会社では定期的な異動もつきものです。

世の中にでるニュース記事ではついつい広告会社のクリエイターなど一部の職種の人の露出が目立つため、大半の人が何をしているのかはつかみ取りにくいと思いますので、少しでも学生の参考なれば幸いです。


その他、最近の中でマーケティングの実態がわかりやすい本がこれです。事業部を畳んだり、経営コンサルティングに近い領域もマーケティングの仕事です。


  1. 製品の開発コンセプトで協力することはありますが、実際にどのような材料で?何なら量産化できて価格も抑えられるのか?そういった製品企画開発の本流ではなく、女子高生をアサインして意見を貰った、とかそういう関わり方が限度です。

  2. もちろん、統合的なキャンペーンという形で「一時的に」係るケースはあります。

  3. 因みに、少しでも広告代理店に払うお金を少なくするために、Web広告用のバナーを内製したり、TVCMの納品用テープのプリントをスタジオに直発注したり、TVSPOTの買い付けで相見積もりをして1%でも多く調達を値引きする、など様々な企業努力を事業会社はされています。

  4. 因みに人数の少ない中小企業ほど全て内製でやってしまいます。記者会見の会場抑え・進行台本制作・メディアキャラバンなど自社でやってしまうところは少なくありません。

ブランディングで売上拡大という幻想

宣伝部やプロモーション部或いはその仕事を請ける広告代理店、様々な人が「自社製品をブランディングして売上を拡大したい」と思っていないだろうか?

だが そもそもブランディングして売れる数を増やすという発想自体が間違っている

プレミアムなブランディングについて

プレミアムブランドは利益率重視であって収益重視ではない

最近読んだ『マツダBMWを超える日』という本の中に重要な指摘があった。

フォルクスワーゲンの収益の33%は台数では21%のアウディが稼いでいる

ここに全てのヒントが集約されていると言っても過言ではない。

プレミアムなブランドは売れる数こそ少なくても、利益率が高いため多額の利益を残すのだ。

STPは売る数を減らす行為

ブランディングしたいんです」「他社と差別化したいんです」と広告主が言ったとき、「ターゲットを絞って尖らせましょう!」「人々は1つのパーセプションしか憶えられません!」と広告代理店のマーケターやクリエイターは言う。

しかしこれは広告主の応えになっていないのではないか?「いやーそんなもったいないことはしないでよ。みんなに買って欲しいのよ」「みんなに売れる良いメッセージが欲しいのよ」と。

ブランディングの基本であるSTP(Segmentation、Targeting、Positioning)は、その名にある通り、市場を分割する。必然的に狙う消費者の数は絞られるし、売れる数も減る可能性がある。

だが、広告代理店の人も「売れる数を増やしたい」と思ってSTPを提案している。これはどういうことだろうか?

マスブランドについて

リーチの問題との混同

何故、「ブランディングしたいんです」という宿題に対してSTPを提案するのか?何かすれ違っているようでいて、いったい何がすれ違っているのか?

  • 広告主の思う「ブランディングしたい」→「みんなに好かれたい。(売れる数は維持もしくは増やしたい)」
  • 広告代理店の思う「ブランディングする」→「価値を理解する人に正確に情報を伝えて買って貰う。(情報が届いていなかった人に届けば売れる数は増える。)」

図解した方がわかりやすいかもしれない。 f:id:yyhhyy:20180909212645p:plain この考え方は一理あるのだが、あくまで特定の人に刺さる商品価値を持ったプレミアムブランドが取れる戦略だ。広告主側が「みんなに好かれたい」と思って作っている商品では当てはまらない。

実際にはこういうことだ。 f:id:yyhhyy:20180909212656p:plain 今まで売れていなかったのは、宣伝で言っていることの意味がわからなかったのかもしれないし、そもそも広告の投下量が少なかっただけかもしれない。

逆に、既にこれまで充分にメッセージ開発もし、多額の広告費もかけていたというのであれば、それはもう限界まで来てしまっていてこれ以上伸ばせない、ということでもある。

殆どの商品はマスブランドである

「みんなに好かれたい」「ターゲットを絞りたくない、もったいない」ということであれば、それはマスブランドである。マスブランドの場合は、好意度の獲得量(Preference)が重要になってくる。 f:id:yyhhyy:20180909212711p:plain コモディティ化した市場の中で、競合より良い印象を、競合より多くの広告投下をし続けないことには、忘れ去られてしまう。レッドオーシャンだから販売管理費はかかるのだ。

まとめ

すれ違いが起きる仕組み

得てして広告代理店の人は「ブランディング」という言葉を聞くと「プレミアムブランド」の戦略を描いてしまう。全てのブランドが「ブランディングすれば下駄をはかせられる」「高い価格でも買って貰えるようになる」そう勘違いしているのだ。

しかし実際には、ポルシェやAudiのように尖った商品でなければそういったことは起きない。

彼らの心理を予測すると、ついつい贅沢なプレミアムブランドみたいなかっこいい仕事に憧れてしまうので、全ての商品ブランドに対して「差別化してターゲットを絞りましょう!」などと応えてしまうのだろう。

(テレビ+Youtube)×タレントニコパチ に愚直にお金をかけるべきであって、競合ブランドに勝ちたければこの図式に対して競合ブランドよりも努力を注ぎ込むべきだ。それ以外は怠慢である。

勘違いさせてしまう代表例

もう1つ、勘違いの原因がある。

広告代理店の人は何故かアップルコンピュータ製品が好きだということだ。デザイナーでないのにアップルコンピュータ製品を使う人もいる。

以前、アップルのファンについて書いたことがある。

yyhhyy.hatenablog.com

初期のMacintoshファンや実利として必要としているデザイナーが買っているMacPro系がある一方で、MacBook Airや新しいMacBookなどの廉価モデルを買っている人や、iPhoneを買っている人はマス層だ。Macintoshは一見プレミアムブランドでありながらマス市場も獲得しているように見えるが、実際にはマスブランドであり、大量に広告投下をすることでも有名だ。

では何故ブランドだと感じるのか?というと、クリエイターが指示していることが好感度に貢献しているだけだ。

Appleはプレミアムなブランドだ」と勘違いしているが、「Appleは好感度が極めて高い」と言った方が現実に即しているだろう。

一度も広告をしたことがないフェラーリなどのプレミアムブランドや一部の広告枠にしか出さないラグジュアリーブランドとは異なる存在なのだが、混同してしまうのだろう。


プレミアムブランドについて参考になる本

Preferenceについて参考になる本