kaekenのTech探究ブログ

主に情報科学/情報技術全般(最近は、特にData Science、機械学習、深層学習、統計学、Python、数学、ビッグデータ)に関する知見をポストします。

OR(Operations Research)の概要・事例・Pythonサンプル

f:id:kaeken:20210913220623p:plain

OR概要

  • ORとは:最適化手法の一種。
オペレーションズ・リサーチ(英語:operations research、米)、
オペレーショナル・リサーチ(英語:operational research、英[1]、略称:OR)は、
数学的・統計的モデル、アルゴリズムの利用などによって、
さまざまな計画に際して最も効率的になるよう決定する科学的技法である。
複雑なシステムの分析などにおける意思決定を支援し、
また意思決定の根拠を他人に説明するためのツールである。
またゲーム理論や金融工学などもORの応用として誕生したものであり、
ORは政府、軍隊、国際機関、企業、非営利法人など、
さまざまな組織に意思決定のための数学的技術として使用されている。

ORの研究では、
線形計画法(linear programing)、動的計画法、順列組み合わせ、確率、
最適化および待ち行列理論、微分方程式、線形代数学など
の数学的研究を踏まえて現実の問題を数理モデルに置き換える。
そのことで、合理化された意思決定が可能となるだけでなく、
定量的な問題についても最適化を行うことができる。
近年は計算機を用いて煩雑な計算を実行する方法を研究することが多い。

また、ORは特定の領域の問題だけでなく幅広い領域に応用することが可能であり、
学際的な研究分野であるとも言える。

オペレーションズ・リサーチ - Wikipedia

OR活用事例

ORを探せ!
一暮らしに溶け込むOR-
オペレーションズ·リサーチ(Operations Research:OR)は
「困っていること」(=問題)を科学的に解決するための「問題解
決学」です。問題の分析と解決のための計画立案やその実施、意
思決定を助ける「実学」なのです。OR は問題のありかや応用の範
囲を選びません。そのため、OR の研究を進める日本OR 学会は
経済学,経営学,理学·工学·農学·医学,芸術など文系
理系を問わず、ありとあらゆる分野の会員から構成されています。
工場
エネルギー供給·配分
設備稼働を制御し、
エネルギー需給を最適化して、
省エネ·省コストに貢献
最適制御、起動停止計画、シミュレーション
需要予測
生産計画·スケジューリング
人モノ·時間を効率良く使い、
品質コスト納期のバランスの
取れた計画運用をする
生産管理、在庫管理、数理最適化
サプライチェーン
天候の変化や報道、
社会動向などから
製品の需要を予測する
時系列解析、回帰分析
空港
ゲート割当て
整備員や乗員が動きやすい
ゲート割当てを計画する
発着スケジューリング
定時率、離散系シミュレーション、
整数最適化
安全で効率的な離発着の
スケジュールを計画·実行
数理最適化、
荷物
マルチェージェントシミュレーション
詰込み
貨物やコンテナの積載率を上げる
食欲(どんよく)法、
切出し詰込み、
メタヒューリスイスる
収益管理
航空運賃をコントロールして
収益を最大化する
乗務員スケジューリング
動的価格設定、
vベニューマネジメント
乗務員の勤務スケジュールを
計画する
シフトスケジューリング、
集合被覆
ダイヤ
鉄道
ロ~m
電車のダイヤを決め、
遅延時にも速く回復させる
時空間ネットワーク、数理最適化、
運行スケジューサング
「乗り換え経路案内
速く楽に安く目的地に着く
経路をすぐに教える
グラフネットワーク、
最短路、ダイクストラ法
改札待ち行列
乗務員スタジューリング
始発から終電までの運転手や車掌、
駅員、保守担当者の仕事内容を決める
駅構内の混雑をシミュレーションして
乗客の流れをスムーズにする
待ち行列、
マルチエージェントシミュレーション
シフトスケジューリング、集合被覆、列生成
スマートフォン
スマートフォン上で
好みの商品やサービスを自動表示
データマイニング、レコメンデーション、
グラフネットワーク、機械学習
ロロマッチング
理想のパートナーを見つける
お見合い問題、安定結婚問題
行政
避難計画·防災
災害時の住民の避難場所や
避難経路の計画をサポート
防災
マルチェージェントシミュレーション、
公共政策と)
施設配置
消防署の位置、
学校の開校や統廃合を
決めるための情報を提供する
道路計画
施設配置、都市計画、
集合被覆、公平性
インフラ整備計画
福祉
メンテナシス
道路や建物、交通など
インフラ整備の効果を予測する
スマートシティ、都姉計画、時系列解析に
AHP(階層的意思決定法)。
評価のOR
インフラのメンテナンスの
計画作り
異常検知、信頼性
オフィスビル
資産運用
空調(エネルギー)
エネルギー源を組み合わせて
空調をコントロール
リスクの少ない
資産ポートフォリオを提案する
金融工学、平均分散モデル、
ボラティリティ、時系列解析
Fin Tech
エネルギーミックス、
デマンド·レスポンス
エレベーター制御
プロジェクトスケジューリング
エレベーターの
作業の開始·終了、その間の手順を
見える化して最適なスケジュールを決める
停止階を決めて、
移動時間や混雑を減らす
群管理、シミュレーション
ガントチャート、フローダイアグラム、
アローダイヤグラム、クリティカルパス
コンビニ
レジ·待ち行列
アルバイトシフト
客を待たせず、スムーズに会計
希望を反映したシフト
待ち行列、確率分布、フォーク並び、
マルコフ連鎖モンテカルロ法、
リトルの公式 A
シフトスケジューリング、
平準化、公平性、バランス、
メタヒューリスティクス
こちらへ
どうぞ~!
日日
陳列動線
つい買いたくなる陳列に変えて
売上げ増
「棚割、カウンティング、動線分析、
マーケティング、消費者行動、
消費者心理、行動観察
POS ビッグデータ
配送(物流)最適化在庫管理
どの商品がいつ誰に売れたかを
把握して販売を予測
マーケティング、需要予測、
レコメンデーション、在庫管理、
データマイニング
タイミング良く発注して、
廃棄や返品を減らし、
商品や在庫を無駄なく管理
経済的発注量、機会費用、
ロットサイズ決定モデル、サプライチェーン

公益社団法人 日本オペレーションズ・リサーチ学会

Ptyhonサンプル

インストール

$ pip install ortools

線形計画法のサンプルコード

from ortools.linear_solver import pywraplp


def main():
    # Create the linear solver with the GLOP backend.
    solver = pywraplp.Solver.CreateSolver('GLOP')

    # Create the variables x and y.
    x = solver.NumVar(0, 1, 'x')
    y = solver.NumVar(0, 2, 'y')

    print('Number of variables =', solver.NumVariables())

    # Create a linear constraint, 0 <= x + y <= 2.
    ct = solver.Constraint(0, 2, 'ct')
    ct.SetCoefficient(x, 1)
    ct.SetCoefficient(y, 1)

    print('Number of constraints =', solver.NumConstraints())

    # Create the objective function, 3 * x + y.
    objective = solver.Objective()
    objective.SetCoefficient(x, 3)
    objective.SetCoefficient(y, 1)
    objective.SetMaximization()

    solver.Solve()

    print('Solution:')
    print('Objective value =', objective.Value())
    print('x =', x.solution_value())
    print('y =', y.solution_value())


if __name__ == '__main__':
    main()

実行結果

Number of variables = 2
Number of constraints = 1
Solution:
Objective value = 4.0
x = 1.0
y = 1.0

その他分析可能なトピック

https://developers.google.com/optimization/introduction/python#more-python-examples

Linear optimization
Constraint optimization
Mixed-integer optimization
Bin packing
Network flows
Assignment
Scheduling
Routing

通信課程の帝京大学理工学部情報科学科3年次振り返り

f:id:kaeken:20210314164153j:plain

Gerd AltmannによるPixabayからの画像

前提

  • 昨年2020年4月に、以下のとおり、工学学士号取得のため、通信制大学に入学しておりました

工学学士号を取得するため通信制の帝京大学理工学部情報科学科に3年次編入しました(2020.04) - kaekenのTech探究ブログ

  • 3年次編入した1年間をふりかえります

単位の話

  • 通信制も正規の大学なので、卒業に必要な単位総数は、124単位以上です

  • 過去に同じく通信制放送大学を卒業しておりますが、経済・経営系だったので、42単位しか認定されていませんでした

  • 3年次編入したため、2年間で卒業するまでに、82単位以上取得する必要がありました

  • そこで、1年間で最大限に単位を取得しようと思い、上限である24科目48単位履修登録しました

  • 結果として、1年間で23科目46単位取得しました(残念ながら1科目だけ落としてしまいました・・・)

  • また、特定の資格を取得していれば満点扱いで単位認定される科目を4科目8単位認定してもらいました

  • 3年編入時42単位 + 試験46単位 + 資格8単位 = 現在96単位取得済みです

  • 残り1年間で14科目28単位以上取得できれば、卒業可能です

  • 資格による単位認定を4科目8単位目指しています(今年受験予定)

  • 残り不足の10科目20単位以上を履修登録して取得予定です

  • また、卒業単位を揃えつつも、できればGPAを3.0(平均A判定)以上にしたいです(現状2.88ぐらいで、3弱)

  • 2021年度はできるだけ、A判定(80-89%)かS判定(90%-)を目指してがんばります

カリキュラムの話

  • 2020年度の全体のカリキュラム構成は以下

  • もともとITエンジニアとして15年以上職務経験があるので、比較的IT科目はそれほど対策しなくても単位取得できました

  • 一方で、数学科目と理論的な情報科学科目については、かなり苦戦して、芳しくない成績に終わりました

  • ただ、アセンブラや、情報理論や、言語理論や、グラフ理論や、離散数学や、線形代数微分積分など、日常業務では、まず使わない数学的理論的なところを学べてよかったです

  • 幾何学的な描画などを簡単にビジュアルが生成できるProcessingが楽しかったです

  • 2021年度は、力学・電磁気・電気回路・電子回路など物理系や、本業そのものであるWebアプリ開発などを中心に単位取得予定です

卒業後について

  • もし2021年度で無事に卒業できたら、もともとの目的だった、海外就労ビザに必要な学歴要件(職務と専攻の一致)はクリアできそうです

  • ただ、コロナ禍が世界的に収束する気配がなく、他国の経済状況については、2-3年は厳しい想定をしており、海外渡航・海外就労も厳しめかと思います

  • 逆に、コロナ禍のおかげで、リモートワーク状況が劇的に改善されましたので、日本を含めて世界中のリモートワーク企業が激増しました

  • となると、安全な日本にいながらにして、世界中のリモートワーク企業への転職チャンスは増えました(ある程度時差は考慮しないといけないですが)

  • さらに、日本もまだ油断はできないですが、欧米諸国と比較して、かなりマシな経済状況なので、短期的には、海外より国内にいるほうが相対的に良いと感じています

  • とはいえ、長期的には、一生日本語圏から出られない状況に甘んじているのもNGなので、海外進出する日本企業の動向をチェックしたり、コロナ禍被害のましな国であるシンガポール・オーストラリア・ニュージーランドなど、アジア/オセアニア英語圏もチェックしつつ、継続的に英語学習も進めていきたいと考えています

  • 次の最優先事項である英語習得後、さらに余裕があれば、海外のオンライン大学院も視野に入れて、グローバル基準で認められる学歴を積み上げていければと思います

個人開発(自社プロダクト)のメリット・デメリット・始め方・続け方・マネタイズについて

f:id:kaeken:20201004111346j:plain

tookapicによるPixabayからの画像

背景

  • 長年、フリーランスとして受託開発をやってきましたが、結局時間を切り売りしたり、成果物を切り売りするだけで、労働収益であることには変わりないです
  • 今後は、受託・プロパー業務と並行して、利益相反しない範囲で、自分の使いたいプロダクト開発・自分で収益化できるサービス開発、すなわち個人開発をしていきたいと思っています
  • 後述するように、個人開発のメリット・デメリット比較すると、メリットの方が優位なので、開始したいです
  • ただ、自分で全工程を管理する必要があるので、3日坊主になったり、目標達成や継続ができず挫折しやすいので、挫折防止する対策も必要です
  • 個人開発に関する調査をして、個人開発(自社プロダクト)のメリット・デメリット・始め方・続け方・マネタイズについて以下に整理しました
  • 個人開発経験者ではなく、あくまでも「これから個人開発を始めるために、個人開発未経験者が整理した内容」ですので、実際とのズレはご了承ください

個人開発のメリット

全工程を自分で自由に作ることができる

  • 企画段階から、要件定義・設計・実装・運用まで、自分で自由にデザインできます

作る過程で技術力が身に付く

  • 新しい技術に挑戦したり、異なる技術を組み合わせたり、実際に運用してみることで、プロダクト開発するための技術力を身に付けることができます

低リスクで開始できる

  • ドメインやサーバやAPI利用料など、スモールスタートすることができます

マネタイズできる

  • 広告を貼ったり、直接課金したり、マネタイズすることができます

就職・転職にプラスになる

  • 個人で全工程作成できる実力を証明でき、GitHub等にコード公開すれば、ポートフォリオとして就職時転職時に提示できます

個人開発のデメリット

プライベートの時間を費やす必要がある

  • 業務外のプライベートな時間を使って開発するので、楽しめる開発・明確な目標を持った開発をしないと、つらくなります

途中で挫折しやすい

  • 全工程を自分でやる場合、顧客も上司もおらず、途中で問題解決できなかったりして、リリースまでにこぎつける&運営を続けるのが大変で、挫折しやすいです

集客などビジネスサイドで苦戦する

  • 個人開発するエンジニアが思いつきでサービス企画する場合、事業企画やマーケティングを担当してきたビジネスサイドの方と比較して、ビジネスサイドで苦戦します

個人開発の始め方

始める際の注意

  • 技術を使うことが目的か、収益が目的かを決めます

    • 技術志向であれば、「その技術が活かせるサービス」「その技術を使わないとできないこと」に注力します
    • 収益志向であれば、徹底的に顧客分析からはじめて、顧客の解決したい真の課題を見つけることと、もっとも効果的な解決策を見つけることを最優先します
  • シンプルにスモールスタートします

    • 複雑さの回避:初めからあれこれやろうと複雑にすると、本質的でないところで詰まったりします
    • 時間浪費の回避:シンプルでないと、リリースまでに時間がかかり、時間がもったいないです
  • 環境を整えます

    • 作りたいプロダクトに応じて、PC、通信回線、ソフトウェア、その他必要なものを揃えます
    • ドメイン、サーバなど個人で契約するサービスも一覧にして、契約内容・契約期間・契約料金など管理できるようにします
  • イデア、タスク、コードを管理します

    • 個人開発する際には、たくさんアイデアやタスクが発生し、ソースコードが散逸しないように、使いなれたサービスで管理します

ステップ別参考記事

参考資料

個人開発コミュニティ

個人開発者(適宜追記)

情報収集リソース

工学学士号を取得するため通信制の帝京大学理工学部情報科学科に3年次編入しました(2020.04)

近況・背景

  • フルリモート案件対応しながら、去年2019年後半から今年2020年3月まで、海外を転々としてました
  • 東南アジア(フィリピン、ベトナム、タイ、マレーシア、シンガポールインドネシア)やオーストラリアを観光・視察(ノマドワーク環境)しつつ、短期間で移動していました

  • しかし、COVID-19の影響で、海外滞在はリスクが高いと考え日本に帰国し、渡航制限により、しばらく海外に出られません

  • しばらく海外に出られないのであれば、今のうちに国内でやれることをじっくりやろうと思いました

今後のスキルアップ戦略

  • 過去15年以上のITエンジニアキャリアをさらに強化するため、アカデミックな領域を攻めます
  • まず、将来的に海外で働く選択肢を考え、CS学位を取得しようと思いました
  • 海外で働きたいと考えているITエンジニア向けのこちらのブログ記事が非常に有益でした

www.jabba.cloud

  • また、今まで開発業務半分、分析業務半分でしたが、今後はデータサイエンスをベースにした分析業務をメインにしていきたいと考えています
  • データサイエンスをメインビジネスにするにあたって、エンジニアリング経験だけでは、数理的なモデリングスキルが不足してます
  • 15年前に通信制大学(こちらも通信制で、放送大学教養学部 産業と技術コース)を卒業しましたが、数理的なモデリングスキルが不足しています
  • 数理的なモデリングスキルを身につけるべく、働きながら通える理工系の通信制大学を調査したところ、帝京大学理工学部情報科学科が見つかりました

www.teikyo-u.ac.jp

  • 通信制なので、夜や休日など自由なタイミングで講義ビデオやテキストを使って学べて、学費は年15万円程度と格安です
  • 卒業すれば正式な工学の学士号を取得できます(Bachelor of Engineering)
  • 国際基準でGPAも算出され、英文卒業証明書にGPAを記載してくれることを確認済みです

科目詳細

  • カリキュラム構成はこちら

gyazo.com

  • 純粋なCS,ソフトウェアだけでなく、電気電子といったエレクトロニクス関係も学べます(IoTも基礎からやりたい方にはオススメかもです)
  • 学べる科目はシラバスをご参照ください

www.e-campus.gr.jp

https://i.gyazo.com/36292aa7c07ac33fdcb1bd76803c7184.png

入学した所感

  • ある程度きちんと勉強しないと、簡単には単位が取れないと感じました(個人差あり)
  • 以下追記予定

結論

  • 今の時点では、一番コスパのよい純粋な工学の学士号の取得方法かと思いました
  • ハードですが、引き続き楽しんで学んでいきます

2019.04沖縄合宿で参加した勉強会一覧

花粉症から逃れるため、

フルリモートで東京の仕事を対応しつつ、

自主的に2019.04沖縄合宿を実施しておりました。

1ヶ月弱滞在し、非常に有意義な合宿でした。

クラウド、サーバレスの盛り上がりを体感し、

今後は、クラウド・サーバレス関連も重点的にウォッチしていきます。

また、AI/機械学習/Pythonの勉強会も盛んで、

沖縄でのデータサイエンス界隈の盛り上がりも実感しました。

現地のエンジニアの方々と交流でき、

非常に有意義な時間を過ごせました。

以下、沖縄で参加した勉強会一覧です!

4/16 Firebase 勉強会 in 沖縄 × 大阪 - Togetter https://togetter.com/li/1338768

4/20 GCPUG Okinawa!#7 午前「GKEハンズオン」午後「GCPと課題解決と意思決定」 - connpass https://okipug.connpass.com/event/115468/

4/21 PythonBeginners沖縄 22 初心者向け勉強会 - connpass https://python-beginners-okinawa.connpass.com/event/125925/

4/22 BeginnersLab #2 ベイズ勉強会(5.Joint probabilityから) - connpass https://python-beginners-okinawa.connpass.com/event/127905/

4/24 AliEatersOkinawa Meetup #1 - Alibaba Cloud - - connpass https://alieaters-okinawa.connpass.com/event/125747/

AliEatersOkinawa Meetup #1 - Alibaba Cloud - - Togetter https://togetter.com/li/1342269

4/26 Serverless Meetup Ryukyu #1 - connpass https://serverless.connpass.com/event/126902/

Serverless Meetup Ryukyu #1 - Togetter https://togetter.com/li/1342014

4/27 BeginnersLab #3 ベイズ勉強会(8.ベイズの定理から) - connpass https://python-beginners-okinawa.connpass.com/event/129229/

4/27 Cloud on the BEACH 2019 勉強会の部 - JAWS-UG沖縄 | Doorkeeper https://jaws-ug-okinawa.doorkeeper.jp/events/88202

JAWS-UG沖縄 Cloud on the BEACH 2019のまとめ - Togetter https://togetter.com/li/1342523

4/28 PyData.Okinawa Meetup #39 PythonOCR - connpass https://pydataokinawa.connpass.com/event/126114/

また来年花粉症の季節に来ます!

R言語でWebページの総合ランキングを作る方法

概要

Webページを評価する切り口は、さまざまな種類があります。

  • 表示回数
  • クリック数
  • 直帰率
  • 平均滞在時間
  • CV数
  • 各種ソーシャルいいね数
  • ...

もし各切り口の単位がバラバラで、ボリュームも異なり、優劣がつけられない場合、

総合的に判断することが難しくなります。

このような場合、

多数の考慮すべき情報を統合し優先順位をつけて総合ランキングを作るには、

以下の標準化を使うことで、総合的な評価指標の作成が可能になります。

標準化:単位が異なる複数の変数から総合指標を作成する方法

標準化については、以下の分かりやすい説明記事をご覧ください。

https://bellcurve.jp/statistics/course/7801.html

標準化することで、まったく異なる対象(数学と国語など)を統一的に評価できるようになります。

今回は、以下の具体的な6種類のデータを使って、Webページの総合ランキングを作成する方法をご紹介します。

実装

準備

まず、上記データのエクセルファイルを準備します。

R言語では、エクセルファイルやCSVなど、さまざまなファイルを読み書きすることが可能です。

# xls/xlsx形式を読み込むreadxlパッケージのインストール
install.packages("readxl", dependencies = TRUE)

# パッケージの利用準備
library(readxl)

# エクセルファイルの1シート目を読み込む
data = read_excel("~/Downloads/page_log.xlsx", 1)

つぎに、読み込んだデータの中身を確認します。

データの列名の意味は、以下の通りです。

  • No : データ行番号
  • TITLE : ページタイトル
  • PV : ページビュー
  • New : 新規流入
  • Average Time : 平均滞在時間
  • FB : Facebookいいね数
  • Pocket : Pocket保存数
  • Hatebu : はてブ登録数
# 読み込んだデータサンプリング
head(data)

# A tibble: 6 x 8
     No TITLE      PV    New `Average Time`    FB Pocket Hatebu
  <dbl> <chr>   <dbl>  <dbl>          <dbl> <dbl>  <dbl>  <dbl>
1     1 title1 154321  86341            184   264     60     21
2     2 title2 132840 103214            243   457     32     48
3     3 title3 186789 364212            160   190     52     26
4     4 title4  66455  30547            130   256    105     50
5     5 title5 506487 430687            311   830   1651   1198
6     6 title6 303546 153054            164    74    124     68

桁数がバラバラです。

以下、各データの統計量です。

# 要約統計量の表示
summary(data)
       No           TITLE                 PV              New        
 Min.   : 1.00   Length:12          Min.   : 43088   Min.   :  2064  
 1st Qu.: 3.75   Class :character   1st Qu.:116244   1st Qu.: 41438  
 Median : 6.50   Mode  :character   Median :174461   Median : 88342  
 Mean   : 6.50                      Mean   :192949   Mean   :122096  
 3rd Qu.: 9.25                      3rd Qu.:236802   3rd Qu.:115923  
 Max.   :12.00                      Max.   :506487   Max.   :430687  
  Average Time         FB             Pocket            Hatebu       
 Min.   : 58.0   Min.   : 15.00   Min.   :  25.00   Min.   :   4.00  
 1st Qu.:158.5   1st Qu.: 86.75   1st Qu.:  43.25   1st Qu.:   9.25  
 Median :198.0   Median :131.00   Median :  52.00   Median :  23.50  
 Mean   :206.6   Mean   :215.00   Mean   : 192.25   Mean   : 123.83  
 3rd Qu.:278.5   3rd Qu.:258.00   3rd Qu.:  84.00   3rd Qu.:  48.50  
 Max.   :311.0   Max.   :830.00   Max.   :1651.00   Max.   :1198.00


# 必要なカラムだけの要約統計量の表示
summary(data[,3:8])
       PV              New          Average Time         FB        
 Min.   : 43088   Min.   :  2064   Min.   : 58.0   Min.   : 15.00  
 1st Qu.:116244   1st Qu.: 41438   1st Qu.:158.5   1st Qu.: 86.75  
 Median :174461   Median : 88342   Median :198.0   Median :131.00  
 Mean   :192949   Mean   :122096   Mean   :206.6   Mean   :215.00  
 3rd Qu.:236802   3rd Qu.:115923   3rd Qu.:278.5   3rd Qu.:258.00  
 Max.   :506487   Max.   :430687   Max.   :311.0   Max.   :830.00  
     Pocket            Hatebu       
 Min.   :  25.00   Min.   :   4.00  
 1st Qu.:  43.25   1st Qu.:   9.25  
 Median :  52.00   Median :  23.50  
 Mean   : 192.25   Mean   : 123.83  
 3rd Qu.:  84.00   3rd Qu.:  48.50  
 Max.   :1651.00   Max.   :1198.00

Rでの行列指定方法は、以下のとおりです。

# cf. Rでの行列指定方法

# 一般形式
var[行指定, 列指定]

# 全行, 3-8列目だけ選択
var[, 3:8]

# 全行, 2列目だけ削除
var[, -2]

# 5行目, 1-2列目だけ選択
var[5, 1:2]

さらに、標準偏差など統計量を一括取得するパッケージを導入します。

# cf. psychパッケージdescribeコマンドで標準偏差など統計量を一括取得

install.packages("psych", dependencies = TRUE)
library(psych)
describe(data)

             vars  n      mean        sd   median  trimmed       mad   min
No              1 12      6.50      3.61      6.5      6.5      4.45     1
TITLE*          2 12       NaN        NA       NA      NaN        NA   Inf
PV              3 12 192949.17 130451.96 174461.0 176581.5 110918.50 43088
New             4 12 122096.00 136667.32  88341.5 103240.1  74921.71  2064
Average Time    5 12    206.58     78.37    198.0    211.0     83.77    58
FB              6 12    215.00    228.75    131.0    173.5    114.16    15
Pocket          7 12    192.25    460.32     52.0     63.1     22.98    25
Hatebu          8 12    123.83    338.92     23.5     28.4     26.69     4
                max  range  skew kurtosis       se
No               12     11  0.00    -1.50     1.04
TITLE*         -Inf   -Inf    NA       NA       NA
PV           506487 463399  0.95     0.20 37658.24
New          430687 428623  1.26     0.12 39452.46
Average Time    311    253 -0.21    -1.22    22.62
FB              830    815  1.57     1.57    66.03
Pocket         1651   1626  2.63     5.41   132.88
Hatebu         1198   1194  2.63     5.42    97.84

標準化

それでは、まず各データをscale()で標準化します。

# scaleコマンドを使う
data_st <- scale(data[,3:8])

# describe
library(psych)
head(data_st)

              PV        New Average Time         FB     Pocket     Hatebu
[1,] -0.29611028 -0.2616207   -0.2881790  0.2142092 -0.2873013 -0.3034187
[2,] -0.46077626 -0.1381603    0.4647019  1.0579310 -0.3481288 -0.2237529
[3,] -0.04722173  1.7715720   -0.5944357 -0.1092904 -0.3046806 -0.2886658
[4,] -0.96966090 -0.6698675   -0.9772565  0.1792362 -0.1895428 -0.2178517
[5,]  2.40347350  2.2579721    1.3324291  2.6885437  3.1690042  3.1694223
[6,]  0.84779740  0.2265209   -0.5433929 -0.6163978 -0.1482670 -0.1647411

describe(data_st)

             vars  n mean sd median trimmed  mad   min  max range  skew
PV              1 12    0  1  -0.14   -0.13 0.85 -1.15 2.40  3.55  0.95
New             2 12    0  1  -0.25   -0.14 0.55 -0.88 2.26  3.14  1.26
Average Time    3 12    0  1  -0.11    0.06 1.07 -1.90 1.33  3.23 -0.21
FB              4 12    0  1  -0.37   -0.18 0.50 -0.87 2.69  3.56  1.57
Pocket          5 12    0  1  -0.30   -0.28 0.05 -0.36 3.17  3.53  2.63
Hatebu          6 12    0  1  -0.30   -0.28 0.08 -0.35 3.17  3.52  2.63
             kurtosis   se
PV               0.20 0.29
New              0.12 0.29
Average Time    -1.22 0.29
FB               1.57 0.29
Pocket           5.41 0.29
Hatebu           5.42 0.29

上記を確認すると、きちんと 平均(mean):0 標準偏差(sd):1 に標準化されていることがわかります。

参考までに、行列情報の表示方法です。

# cf. 行列情報の表示

# 行列の行数だけ表示
> nrow(data_st)
[1] 12

# 行列の列数だけ表示
> ncol(data_st)
[1] 6

標準化による総合指標を作成

標準化された6つの指標を合成して総合指標にします。

まず、データフレームに変換します。

data_st_df <- data.frame(data_st)
describe(data_st_df)
             vars  n mean sd median trimmed  mad   min  max range  skew
PV              1 12    0  1  -0.14   -0.13 0.85 -1.15 2.40  3.55  0.95
New             2 12    0  1  -0.25   -0.14 0.55 -0.88 2.26  3.14  1.26
Average.Time    3 12    0  1  -0.11    0.06 1.07 -1.90 1.33  3.23 -0.21
FB              4 12    0  1  -0.37   -0.18 0.50 -0.87 2.69  3.56  1.57
Pocket          5 12    0  1  -0.30   -0.28 0.05 -0.36 3.17  3.53  2.63
Hatebu          6 12    0  1  -0.30   -0.28 0.08 -0.35 3.17  3.52  2.63
             kurtosis   se
PV               0.20 0.29
New              0.12 0.29
Average.Time    -1.22 0.29
FB               1.57 0.29
Pocket           5.41 0.29
Hatebu           5.42 0.29

そして、すべて同じ重み付けをする場合、項目の平均を掛け合わせて、scoreカラムに代入します。

data_st_df$score <- apply(data_st_df[,1:6], 1, mean)
describe(data_st_df)
             vars  n mean   sd median trimmed  mad   min  max range  skew
PV              1 12    0 1.00  -0.14   -0.13 0.85 -1.15 2.40  3.55  0.95
New             2 12    0 1.00  -0.25   -0.14 0.55 -0.88 2.26  3.14  1.26
Average.Time    3 12    0 1.00  -0.11    0.06 1.07 -1.90 1.33  3.23 -0.21
FB              4 12    0 1.00  -0.37   -0.18 0.50 -0.87 2.69  3.56  1.57
Pocket          5 12    0 1.00  -0.30   -0.28 0.05 -0.36 3.17  3.53  2.63
Hatebu          6 12    0 1.00  -0.30   -0.28 0.08 -0.35 3.17  3.52  2.63
score           7 12    0 0.84  -0.14   -0.17 0.25 -0.82 2.50  3.33  2.10
             kurtosis   se
PV               0.20 0.29
New              0.12 0.29
Average.Time    -1.22 0.29
FB               1.57 0.29
Pocket           5.41 0.29
Hatebu           5.42 0.29
score            3.78 0.24

cf. apply(x, y, z)の使い方です。

apply(x, y, z)引数
x: データセット名
y: 1 ... 列方向に各行を計算
y: 2 ... 行方向に各列を計算
z: mean平均 or sd標準偏差 or sum合計 or ...

cf. PVだけ重要視するなど、もしすべて同じ重み付けをせず、異なる重み付けをしたい場合は、各指標ごとに固定の値で割り算します。

総合指標を結合

実データと標準化したデータをcbind()で結合します。

data_score <- cbind(data, data_st_df)
str(data_score)

'data.frame':  12 obs. of  15 variables:
 $ No          : num  1 2 3 4 5 6 7 8 9 10 ...
 $ TITLE       : chr  "title1" "title2" "title3" "title4" ...
 $ PV          : num  154321 132840 186789 66455 506487 ...
 $ New         : num  86341 103214 364212 30547 430687 ...
 $ Average Time: num  184 243 160 130 311 164 277 212 303 283 ...
 $ FB          : num  264 457 190 256 830 74 114 91 15 36 ...
 $ Pocket      : num  60 32 52 105 1651 ...
 $ Hatebu      : num  21 48 26 50 1198 ...
 $ PV          : num  -0.2961 -0.4608 -0.0472 -0.9697 2.4035 ...
 $ New         : num  -0.262 -0.138 1.772 -0.67 2.258 ...
 $ Average.Time: num  -0.288 0.465 -0.594 -0.977 1.332 ...
 $ FB          : num  0.214 1.058 -0.109 0.179 2.689 ...
 $ Pocket      : num  -0.287 -0.348 -0.305 -0.19 3.169 ...
 $ Hatebu      : num  -0.303 -0.224 -0.289 -0.218 3.169 ...
 $ score       : num  -0.2037 0.0586 0.0712 -0.4742 2.5035 ...
 

head(data_score, 3)

  No  TITLE     PV    New Average Time  FB Pocket Hatebu          PV
1  1 title1 154321  86341          184 264     60     21 -0.29611028
2  2 title2 132840 103214          243 457     32     48 -0.46077626
3  3 title3 186789 364212          160 190     52     26 -0.04722173
         New Average.Time         FB     Pocket     Hatebu       score
1 -0.2616207   -0.2881790  0.2142092 -0.2873013 -0.3034187 -0.20373681
2 -0.1381603    0.4647019  1.0579310 -0.3481288 -0.2237529  0.05863578
3  1.7715720   -0.5944357 -0.1092904 -0.3046806 -0.2886658  0.07121298

少し整形して、見やすくします。

data_score <- cbind(data[,1:2], data_st_df$score)
colnames(data_score) <- c("No", "Title", "Score")
data_score

   No   Title        Score
1   1  title1 -0.203736806
2   2  title2  0.058635784
3   3  title3  0.071212976
4   4  title4 -0.474157196
5   5  title5  2.503474161
6   6  title6 -0.066413432
7   7  title7 -0.028503090
8   8  title8 -0.212493302
9   9  title9  0.009188198
10 10 title10 -0.207829963
11 11 title11 -0.627226978
12 12 title12 -0.822150352

cf. cbind() / rbind()

cbind() ... col bind 列方向のバインド
rbind() ... row bind 行方向のバインド

ランキングの作成

総合指標をランキング形式に変換します。

data_score$rank <- rank(data_score$Score)
data_score
   No   Title        Score rank
1   1  title1 -0.203736806    6
2   2  title2  0.058635784   10
3   3  title3  0.071212976   11
4   4  title4 -0.474157196    3
5   5  title5  2.503474161   12
6   6  title6 -0.066413432    7
7   7  title7 -0.028503090    8
8   8  title8 -0.212493302    4
9   9  title9  0.009188198    9
10 10 title10 -0.207829963    5
11 11 title11 -0.627226978    2
12 12 title12 -0.822150352    1


# ランクづけを大きいものほど小さくする降順に変更
data_score$rank <- rank(-data_score$Score)
data_score
   No   Title        Score rank
1   1  title1 -0.203736806    7
2   2  title2  0.058635784    3
3   3  title3  0.071212976    2
4   4  title4 -0.474157196   10
5   5  title5  2.503474161    1
6   6  title6 -0.066413432    6
7   7  title7 -0.028503090    5
8   8  title8 -0.212493302    9
9   9  title9  0.009188198    4
10 10 title10 -0.207829963    8
11 11 title11 -0.627226978   11
12 12 title12 -0.822150352   12

# ランクづけした後に、ランク昇順で表示
result <- data_score[order(data_score$rank), ]
result
   No   Title        Score rank
5   5  title5  2.503474161    1
3   3  title3  0.071212976    2
2   2  title2  0.058635784    3
9   9  title9  0.009188198    4
7   7  title7 -0.028503090    5
6   6  title6 -0.066413432    6
1   1  title1 -0.203736806    7
10 10 title10 -0.207829963    8
8   8  title8 -0.212493302    9
4   4  title4 -0.474157196   10
11 11 title11 -0.627226978   11
12 12 title12 -0.822150352   12

以上、

Web記事の総合ランキングを作る方法をご紹介しました。

発展

各項目ごとに重み付けする値を変更したり、

さまざまな項目の組み合わせで総合ランキングを作成したりできます。

JupyterNotebookで対話的インターフェイスipywidgetsを使う方法

概要

JupyterNotebookは、セル単位でプログラムを実行できるインターフェイスがあります。

ただ、変数の値を少し変更したり、

設定条件を変えたい場合は、

セルの中身のプログラムを変更する必要があります。

もう少し、マウス操作や、直感的に数値変更できるようにするためには、

対話的インターフェイスipywidgetsエクステンションを導入することで、

Webフォームのように、インタラクティブウィジェットを導入することができます。

導入

インストール(conda)

conda環境がある場合、

以下のコマンドラインipywidgetsを導入できます。

$ conda --version
conda 4.6.4

$ conda install -c conda-forge ipywidgets==6.0.0

インストール(pip)

pipの場合、jupyterコマンドでextensionを有効化します。

$ pip --version
pip 18.1

$ pip install ipywidgets
$ jupyter nbextension enable --py widgetsnbextension

使用例

引数に整数値を与えてスライダを実装

引数に整数値を与えてスライダを表示する例です。

from ipywidgets import interact

@interact(i=1)
def f(i):
  return i*1024

さまざまなインターフェイス

スライダー以外に、テキストフォームやセレクトボックスやチェックボックスの事例です

@interact(i=1, f=0.1, s='text', l=['a','b'], is_checked=True)
def f(i, f, s, l, is_checked):
  return i, f, s, l, is_checked

住宅ローンの返済シミュレータ

明示的にクラス指定することで、スライダを呼び出すこともできます。

以下の事例は、住宅ローンの返済シミュレータです。

from ipywidgets import IntSlider, FloatSlider
import numpy as np

rate_slider = FloatSlider(min=1, max=5, step=0.1, value=2, description='金利')
nper_slider = IntSlider(min=1, max=35, step=1, value=20, description='返済期間')
pv_slider = IntSlider(
  min=5000000,
  max=50000000,
  step=1000000,
  value=20000000,
  description='返済金額')

@interact(rate=rate_slider, nper=nper_slider, pv=pv_slider)
def pmt(rate, nper, pv):
  rate = rate * 0.01 / 12
  nper = nper * 12
  payment = abs(int(np.pmt(rate, nper, pv)))
  print('月々の返済額: {:,d} 円'.format(payment))
  print('返済総額: {:,d} 円'.format(payment * nper))

ボタン押下時処理(イベントハンドリング)

ボタンウィジェット操作時の動作を設定する方法です。

from ipywidgets import Button
from IPython.display import display, clear_output
from datetime import datetime

def on_button_clicked(b):
  clear_output() # すでにある出力を削除
  print('{:%H時%M分%S秒}です'.format(datetime.now()))

b = Button(description='いま何時?')
b.on_click(on_button_clicked)
display(b)

使用可能なウィジェット一覧

以下コマンドでウィジェット一覧を確認できます。

from ipywidgets import Widget
from pprint import pprint

pprint(Widget.widget_types)

以下に実例が列挙されています。

https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20List.html

ウィジェットのレイアウト変更

ウィジェットのレイアウトを変えたい場合は、以下設定で変更できます。

from ipywidgets import HBox, VBox, Text, Dropdown

# HBox: 横並びに整列
# VBox: タテ並びに整列
# Accordion: アコーディオン表示
# Tabs: タブ表示

w1 = Text(value='Text')
w2 = Button(description='Button')
w3 = Dropdown(options=['1', '2'])
w4 = IntSlider()

VBox([ HBox([w1, w2]), HBox([w3, w4]) ])

可視化ツールとの連携

Matplotlibとの連携

ipywidgetsとMatplotlibなどの可視化ツールを連携する方法があり、

以下のようにドロップダウンで選択した値から棒グラフを出力する方法があります。

import os
import pandas as pd
import matplotlib.pyplot as plt

anime_genre_top10_csv = os.path.join('./anime_genre_top10.csv')
top10_df = pd.read_csv(anime_genre_top10_csv)

def plot_genre_members(b):
  clear_output()
  filter_by_type = top10_df.loc[top10_df['type'] == types.value]
  plot_data = filter_by_type.groupby('genre').sum()['members']
  ax = plot_data.plot.bar()
  ax.set_title(types.value)
  plt.show()

types = Dropdown(options=list(top10_df['type'].unique()))
submit_button = Button(description='グラフを表示')
submit_button.on_click(plot_genre_members)

HBox([types, submit_button])

Bokehとの連携

インタラクティブに操作できるBokehとも連携できます。

以下、連携したデータのプロットを表示/非表示切り替えたり、

画面上で操作できるようになります。

from bokeh.plotting import figure
from bokeh.io import output_notebook, push_notebook, show
from bokeh.palettes import d3
from IPython.display import display
from ipywidgets import ColorPicker, Checkbox, Tab

# 散布図を描画
def plot_scatter(_type):
    data = df.loc[df["type"] == _type, ["members", "rating"]]
    return p.circle(
        data["members"],
        data["rating"],
        legend=_type,
        color=colors[_type],
        line_color=None,
    )


# 図形の色を変更
def change_color(change):
    if change["new"]:
        description = change["owner"].description
        _type = description.split()[0]
        r[_type].glyph.fill_color = change["new"]
        push_notebook(handle=t)


# 図形の不透明度を変更
def change_alpha(change):
    if change["new"]:
        description = change["owner"].description
        _type = description.split()[0]
        r[_type].glyph.fill_alpha = change["new"]
        push_notebook(handle=t)


# 図形の表示 ・ 非表示の切り替え
def change_visible(change):
    description = change["owner"].description
    _type = description.split()[0]
    r[_type].visible = change["new"]
    push_notebook(handle=t)


# サンプルデータの取得
df = pd.read_csv(os.path.join("./anime_master.csv"))
types = sorted(set(df["type"]))  # ユニークなtype列
colors = dict(zip(types, d3["Category10"][6]))  # 配色
# グラフの描画
p = figure(plot_width=400, plot_height=400)
r = dict(zip(types, [plot_scatter(x) for x in types]))
p.legend.location = "bottom_right"
# カラーピッカ
color_picker = [ColorPicker(description=" ".join((x, "color:"))) for x in types]
# フロートスライダ
float_slider = [
    FloatSlider(description=" ".join((x, "alpha:")), min=0, max=1, value=1)
    for x in types
]
# チェックボックス
check_box = [Checkbox(description=" ".join((x, "visible:")), value=True) for x in types]
# イベントのハンドリング
for i, x in enumerate(types):
    color_picker[i].observe(change_color, names="value")
    float_slider[i].observe(change_alpha, names="value")
    check_box[i].observe(change_visible, names="value")
titles = dict(zip(range(len(types)), types))  # タブのタイトル
# タブに登録するウィジェット
children = [
    HBox([cp, fs, cb]) for cp, fs, cb in zip(color_picker, float_slider, check_box)
]
display(Tab(children=children, _titles=titles))
output_notebook()
t = show(p, notebook_handle=True)

以上、

JupyterNotebookで対話的インターフェイスipywidgetsを使う方法でした。

JupyterNotebookでRを使う方法

概要

データ分析など、ブラウザ上で対話的にPythonコマンドを実行できる、

Jupyter Notebookでは、R言語Rubyといった他の言語のカーネル

インストールすることで、他言語も使うことができます。

以下導入方法です。

Rカーネルの導入

パッケージインストール

R StudioなどRコンソールで

以下コマンドを実行して

Rカーネルをインストールします。

install.packages('devtools')
devtools::install_github('IRkernel/IRkernel')
IRkernel::installspec()

RのNotebook作成

Jupyter Notebookを起動します。

Notebookを新規作成するとき、

メニューにRが出てきますので、

クリックすれば、

R言語が書けるNotebookが作成されます。

記述例

以下のように、通常のRを記述すれば、実行結果が表示されます。

以上、JupyterNotebookでRを使う方法でした。

RによるRFM分析

概要

RによるRFM分析をご紹介します。

RFM分析 とは、顧客を以下の3つの側面から分析する手法です。

  • R(Recency, 最近購買しているかどうか, 購買日付)
  • F(Frequency, 頻繁に購買しているかどうか, 購買頻度)
  • M(Monetary, 高額に購買しているかどうか, 購買金額)

まず、最近、頻繁に、高額商品を購入していれば、優良顧客と言えます。

逆に、最近購入していない、または、頻繁に購入していない、または、高額に購入していない、

のいずれか、またはその組み合わせの場合は、優良顧客とは言えず、

改善していく施策を打つ必要があります。

準備

データ

まず、以下のような売上データを準備します。

# 顧客ID,売上金額,日付
custID,sales,date
1289,2599,2012/1/1
1925,110,2012/1/1
1498,75,2012/1/1

実装

まず、売上データを読み込みます。

rfmdata <- read.csv("~/Downloads/idposdata.csv", header = T)
str(rfmdata)

'data.frame':  10000 obs. of  3 variables:
 $ custID: int  1289 1925 1498 1535 1886 1773 1422 1219 1215 1500 ...
 $ sales : int  2599 110 75 99 2485 3828 191 3901 266 58 ...
 $ date  : Factor w/ 360 levels "2012/1/1","2012/1/10",..: 1 1 1 1 1 1 1 1 1 1 ...
 

summary(rfmdata)

     custID         sales                date     
 Min.   :1000   Min.   :   1.0   2012/4/16 :  42  
 1st Qu.:1251   1st Qu.:  66.0   2012/11/17:  41  
 Median :1498   Median : 197.0   2012/7/15 :  41  
 Mean   :1498   Mean   : 610.8   2012/10/17:  40  
 3rd Qu.:1749   3rd Qu.: 587.0   2012/8/12 :  40  
 Max.   :1999   Max.   :9998.0   2012/8/17 :  40  
                                 (Other)   :9756

まず、Recencyを算出するため、datediff列に新しく基準日と購買日の差分日数を格納します。

rfmdata$datediff<- round(as.numeric(difftime("2013-01-01",rfmdata$date,units="days")))
str(rfmdata)

'data.frame':  10000 obs. of  4 variables:
 $ custID  : int  1289 1925 1498 1535 1886 1773 1422 1219 1215 1500 ...
 $ sales   : int  2599 110 75 99 2485 3828 191 3901 266 58 ...
 $ date    : Factor w/ 360 levels "2012/1/1","2012/1/10",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ datediff: num  366 366 366 366 366 366 366 366 366 366 ...

cf. もし基準日を"2013-01-01"ではなく、実行日を基準にする場合は、Sys.Date() に置き換えます。

Sys.Date()
[1] "2019-02-17"

差分日数を算出したら、Recencyを計算します。

Recency <- aggregate(rfmdata$datediff,list(rfmdata$custID),min)
names(Recency) <- c("custID","Recency")
str(Recency)

'data.frame':  1000 obs. of  2 variables:
 $ custID : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Recency: num  13 8 74 26 33 20 9 33 9 16 ...

次に、顧客IDをキーにして、購買頻度Frequencyを集計します。

Frequency <- aggregate(rfmdata$sales,list(rfmdata$custID),length)
names(Frequency) <- c("custID","Frequency")
str(Frequency)

'data.frame':  1000 obs. of  2 variables:
 $ custID   : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Frequency: int  9 11 6 8 10 14 15 5 15 14 ...

そして、購買金額Monetaryも算出します。

Monetary <- aggregate(rfmdata$sales,list(rfmdata$custID),sum)
names(Monetary) <- c("custID","Monetary")
str(Monetary)

'data.frame':  1000 obs. of  2 variables:
 $ custID  : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Monetary: int  1177 6364 6995 2909 11470 9235 6312 1814 4930 10560 ...

最後に、各変数を結合します。

temp <- merge(Frequency,Monetary,"custID")
customerRFM <- merge(temp,Recency,"custID")
str(customerRFM)

'data.frame':  1000 obs. of  4 variables:
 $ custID   : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Frequency: int  9 11 6 8 10 14 15 5 15 14 ...
 $ Monetary : int  1177 6364 6995 2909 11470 9235 6312 1814 4930 10560 ...
 $ Recency  : num  13 8 74 26 33 20 9 33 9 16 ...

各RFMデータが集計されましたので、それぞれランクづけを5段階にしてみます。

customerRFM$rankR <- cut(customerRFM$Recency,quantile(customerRFM$Recency,(0:5)/5,na.rm=TRUE),label=FALSE,include.lowest=TRUE)
customerRFM$rankF <- cut(customerRFM$Frequency,quantile(customerRFM$Frequency,(0:5)/5,na.rm=TRUE),label=FALSE,include.lowest=TRUE)
customerRFM$rankM <- cut(customerRFM$Monetary,quantile(customerRFM$Monetary,(0:5)/5,na.rm=TRUE),label=FALSE,include.lowest=TRUE)

なお、Recencyだけは、小さい方が良いので、反転しておきます。

customerRFM$rankR <- 6-customerRFM$rankR

ランクづけしたデータを参照します。

str(customerRFM)
'data.frame':  1000 obs. of  7 variables:
 $ custID   : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Frequency: int  9 11 6 8 10 14 15 5 15 14 ...
 $ Monetary : int  1177 6364 6995 2909 11470 9235 6312 1814 4930 10560 ...
 $ Recency  : num  13 8 74 26 33 20 9 33 9 16 ...
 $ rankR    : num  5 5 1 3 3 4 5 3 5 4 ...
 $ rankF    : int  2 3 1 2 3 5 5 1 5 5 ...
 $ rankM    : int  1 4 4 2 5 5 4 1 3 5 ...

ランクづけしたデータを表形式で参照します。

// 5列目rankR, 6列目rankFの組み合わせ
table(customerRFM[,5:6])

     rankF
rankR  1  2  3  4  5
    1 78 59 34 21  7
    2 47 57 41 28 25
    3 35 42 57 32 26
    4 31 55 41 38 43
    5 24 41 55 44 39

// 6列目rankF, 7列目rankMの組み合わせ
table(customerRFM[,6:7])
     rankM
rankF  1  2  3  4  5
    1 99 46 29 25 16
    2 64 66 56 42 26
    3 28 58 42 57 43
    4  8 24 44 43 44
    5  1  6 29 33 71

// 各rankMと、他のrankR/rankFとの組み合わせ
table(customerRFM[,5:7])
, , rankM = 1

     rankF
rankR  1  2  3  4  5
    1 43 11  3  0  0
    2 23 17  6  3  0
    3 17 13  8  1  0
    4 10 12  3  3  0
    5  6 11  8  1  1

, , rankM = 2

     rankF
rankR  1  2  3  4  5
    1 15 17  8  2  0
    2  9 17  6  4  2
    3  8 12 13  6  0
    4  4 13 10  3  1
    5 10  7 21  9  3

, , rankM = 3

     rankF
rankR  1  2  3  4  5
    1  8 15  7  7  2
    2  4 12  8  6  8
    3  4  7 10 11  5
    4 10 11  7  8  7
    5  3 11 10 12  7

, , rankM = 4

     rankF
rankR  1  2  3  4  5
    1  8 10  7  6  1
    2  5  6 12  8  2
    3  4  8 17  5  9
    4  5 13 13 14 11
    5  3  5  8 10 10

, , rankM = 5

     rankF
rankR  1  2  3  4  5
    1  4  6  9  6  4
    2  6  5  9  7 13
    3  2  2  9  9 12
    4  2  6  8 10 24
    5  2  7  8 12 18

最後に、高いランクだけファイルに出力する場合は以下のようにきじゅつします

subset(customerRFM, customerRFM$rankM==5 & customerRFM$rankF==5 & customerRFM$rankR==5)
RFMgoodcustomers <- subset(customerRFM, customerRFM$rankM==5 & customerRFM$rankF==5 & customerRFM$rankR==5)
write.csv(RFMgoodcustomers, "~/Downloads/RFMgoodcustomers.csv", quote=F, row.names=F)

以上、RによるRFM分析でした。

Rによるデシル分析

概要

Rによるデシル分析についてご紹介します。

デシル分析とは、売上など特定の数値で顧客データを並び替えたあとに、10等分する分析です。

上位に集中する特徴を捉えて、次の施策を考案するために活用します。

準備

データ

まず、1カスタマー1データで売上データを準備します。

customerId,sales_amount
1289,2599
1925,110
1498,75
...

実装

さっそくデシル分析をしていきます。

まず、上記で準備した売上データを読み込みます。

sales_data = read.csv("~/Downloads/decile.csv", header = T)

つぎに、str()でデータ構造を確認します。

str(sales_data)

'data.frame':  10000 obs. of  2 variables:
 $ customerId  : int  1289 1925 1498 1535 1886 1773 1422 1219 1215 1500 ...
 $ sales_amount: int  2599 110 75 99 2485 3828 191 3901 266 58 ...

また、summary()で基礎統計量も確認します。

summary(sales_data)

   customerId    sales_amount   
 Min.   :1000   Min.   :   1.0  
 1st Qu.:1251   1st Qu.:  66.0  
 Median :1498   Median : 197.0  
 Mean   :1498   Mean   : 610.8  
 3rd Qu.:1749   3rd Qu.: 587.0  
 Max.   :1999   Max.   :9998.0

そして、売上のヒストグラムを表示します。

hist(sales_data$sales)

基礎データの概略を把握した後、さっそく顧客IDごとの売上集計をして、結果を確認します。

sales_data_sum <- aggregate(sales_data$sales_amount,list(sales_data$customerId),sum)
str(sales_data_sum)
'data.frame':  1000 obs. of  2 variables:
 $ Group.1: int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ x      : int  1177 6364 6995 2909 11470 9235 6312 1814 4930 10560 ...

列名を付け直して、わかりやすくします。

names(sales_data_sum) <- c("customerId","Monetary")
str(sales_data_sum)
'data.frame':  1000 obs. of  2 variables:
 $ customerId: int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Monetary  : int  1177 6364 6995 2909 11470 9235 6312 1814 4930 10560 ...

ヒストグラムで表示確認します。

hist(sales_data_sum$Monetary)

さて、つぎは、

顧客IDごとに売上集計がとれたので、顧客を10等分してランク名を付けます。

quantile()で指定分位数に分割し、cut()で量的変数を質的変数に変換します。

各顧客IDごとに、decile_rankが付与されています。

sales_data_sum$decile_rank <- cut(sales_data_sum$Monetary,quantile(sales_data_sum$Monetary,(0:10)/10,na.rm=TRUE),label=FALSE,include.lowest=TRUE)

str(sales_data_sum)
'data.frame':  1000 obs. of  3 variables:
 $ customerId : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Monetary   : int  1177 6364 6995 2909 11470 9235 6312 1814 4930 10560 ...
 $ decile_rank: int  1 7 7 3 9 9 7 2 5 9 ...

cf. (0:10)/10 は、0.1刻みで0から1まで生成する記法です。

res = (0:10)/10
> res
 [1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

ランクづけができたら、それぞれのランクごとに集計します。

decile <- aggregate(sales_data_sum$Monetary,list(sales_data_sum$decile_rank),sum) 
names(decile) <- c("decile_rank","Monetary")
str(decile)
'data.frame':  10 obs. of  2 variables:
 $ decile_rank: int  1 2 3 4 5 6 7 8 9 10
 $ Monetary   : int  114950 211066 288829 373596 467780 566464 687162 830994 1031740 1535566

デシルランクごとに売上比率を算出します。

total <- sum(decile$Monetary)
percent <- decile$Monetary / total * 100
decile2 <- cbind(decile, percent)
decile3 <- decile2[order(-decile2$decile_rank),]
str(decile3)

'data.frame':  10 obs. of  3 variables:
 $ decile_rank: int  10 9 8 7 6 5 4 3 2 1
 $ Monetary   : int  1535566 1031740 830994 687162 566464 467780 373596 288829 211066 114950
 $ percent    : num  25.14 16.89 13.6 11.25 9.27 ...

もし、上位金額のデータだけを活用したい場合は、以下のように抽出し、CSVファイルに出力して活用できます。

subset(sales_data_sum, sales_data_sum$decile_rank==10)
decile_top_customers <- subset(sales_data_sum, sales_data_sum$decile_rank==10)
write.csv(decile_top_customers, "~/Downloads/decile_top_customers.csv", quote=F, row.names=F)

以上、Rによるデシル分析をご紹介しました。

Rによる共分散・相関係数・ファイ係数の算出

概要

Rによる共分散・相関係数・ファイ係数の算出方法をご紹介します。

関数

2つの異なるデータ群の共分散を cov() で算出できます。

ただし、単位に依存するので、例えば、身長がm単位なのかcm単位なのかで共分散の値が異なります。

cov() : 共分散

共分散: 偏差の積の平均
デメリット: 単位に依存する
> h1 <- c(1.5, 1.6, 1.7, 1.8, 1.9) # 身長(m)
> h1
[1] 1.5 1.6 1.7 1.8 1.9
> 
> h2 <- h1*100 # 身長(cm)
> h2
[1] 150 160 170 180 190
> 
> w <- c(50, 60, 70, 80, 90) # 体重(kg)
> w
[1] 50 60 70 80 90
> 
> cov(h1, w)
[1] 2.5
> 
> cov(h2, w)
[1] 250

cor() : 相関係数

x, y の共分散を、それぞれのx, y の標準偏差の積で割った値が相関係数です。

メリットとして、単位に依存しないので、任意のデータ群の相関が統一的に算出できます。

また、

100%正の相関のときは1

100%負の相関のときは-1

無相関のときは0

となります。

相関係数r : cov(x,y) / ( sd(x) * sd(y) )
メリット: 単位に依存しない
-1 <= r <= 1
> h1 <- c(1.5, 1.6, 1.7, 1.8, 1.9) # 身長(m)
> h1
[1] 1.5 1.6 1.7 1.8 1.9
> 
> h2 <- h1*100 # 身長(cm)
> h2
[1] 150 160 170 180 190
> 
> w <- c(50, 60, 70, 80, 90) # 体重(kg)
> w
[1] 50 60 70 80 90
> 
> cor(h1, w)
[1] 1
> 
> cor(h2, w)
[1] 1

> z <- c(90, 80, 70, 60, 50)
> z
[1] 90 80 70 60 50
> cor(z,w)
[1] -1

> p <- c(100, 2, 400, 8, 5)
> cor(p, w)
[1] -0.1700732

table(x, y) クロス集計表

複数のデータ群を集計したクロス集計表は、table()を使って整理します。

> x <- c("a", "b", "a", "a", "b", "a", "b", "a", "a", "a", "a", "a", "b", "a", "a", "b", "a")
> table(x)
x
 a  b 
12  5 
> 
> y <- c("b", "b", "a", "b", "a", "a", "a", "b", "a", "a", "b", "a", "b", "b", "a", "a", "a")
> table(y)
y
 a  b 
10  7 
> 
> table(x, y)
   y
x   a b
  a 7 5
  b 3 2

ファイ係数

カテゴリカルデータの相関係数を算出したい場合は、以下のようにダミー変数に変換した値の相関係数をとります。

これをファイ係数と呼びます。

ファイ係数: 1 or 0 の値からなる変数(2値変数)の相関係数

ifelse(Condition, True, False)

IF分岐させたい場合は、ifelse()を使います。

> x <- c("a", "b", "a", "a", "b", "a", "b", "a", "a", "a", "a", "a", "b", "a", "a", "b", "a")
> x
 [1] "a" "b" "a" "a" "b" "a" "b" "a" "a" "a" "a" "a" "b" "a" "a" "b"
[17] "a"
> x_bi <- ifelse(x=="a", 1, 0)
> x_bi
 [1] 1 0 1 1 0 1 0 1 1 1 1 1 0 1 1 0 1
> y <- c("b", "b", "a", "b", "a", "a", "a", "b", "a", "a", "b", "a", "b", "b", "a", "a", "a")
> y_bi <- ifelse(y=="a", 1, 0)
> y_bi
 [1] 0 0 1 0 1 1 1 0 1 1 0 1 0 0 1 1 1
> cor(x_bi, y_bi)
[1] -0.01543033
> cor(x_bi, x_bi)
[1] 1
> cor(x_bi, ifelse(x_bi==1, 0, 1))
[1] -1

以上です。

Rによる分散・標準偏差・Z得点・偏差値の算出

概要

Rによる分散・標準偏差・Z得点・偏差値の算出方法をご紹介します。

関数

var(): 不偏分散

不偏分散を算出するには、var()を使います。

x <- c(10, 13, 8, 15, 8)
x
# [1] 10 13  8 15  8

# 不偏分散
var(x)
# [1] 9.7

sd(): 標準偏差

標準偏差を算出するには、sd() を使います。 もしくは、sqrt(var(x)) で分散の平方根をとります。

x <- c(10, 13, 8, 15, 8)
x
# [1] 10 13  8 15  8

# 標準偏差(不偏分散)
> sd(x)
[1] 3.114482

# OR

> sqrt(var(x))
[1] 3.114482

varp() // 標本分散 独自関数

不偏分散ではなく、標本分散を算出したい場合は、以下のような独自関数を作成します。

自由度を n ではなく n-1 に置き換える必要があります。

# 標本分散を算出する関数
varp <- function(x) {
  ret <- var(x) * ( length(x) - 1 ) / length(x)
  ret
}

x <- c(10, 13, 8, 15, 8)
x
# [1] 10 13  8 15  8

# 標本分散
varp(x)
# [1] 7.76

# 標準偏差(標本分散)
> sqrt(varp(x))
[1] 2.785678

score(): 標準化によるZ得点

Z得点(z値、z-score、z-value)とは、

平均が0

標準偏差が1

になるように変換した得点のことを指し、

(値 - 平均) / 標準偏差 で算出できます。

また、偏差値とは、

平均が50

標準偏差が10

になるように変換した値であり、

z得点 * 10 + 50 で算出できます。

z得点 = (値 - 平均) / 標準偏差
平均(z得点) = 0
標準偏差(z得点) = 1

偏差値 = z得点 * 10 + 50
平均(偏差値) = 50
標準偏差(偏差値) = 10

scale() を使えば、標準化できます。

> num <- sample(20)
> num
 [1]  6  8 17 18 19  2 11  1 20  5 15  3 16 12  9 14  7 13  4 10
> z_score <- scale(num)
> z_score
             [,1]
 [1,] -0.76063883
 [2,] -0.42257713
 [3,]  1.09870053
 [4,]  1.26773138
 [5,]  1.43676223
 [6,] -1.43676223
 [7,]  0.08451543
 [8,] -1.60579308
 [9,]  1.60579308
[10,] -0.92966968
[11,]  0.76063883
[12,] -1.26773138
[13,]  0.92966968
[14,]  0.25354628
[15,] -0.25354628
[16,]  0.59160798
[17,] -0.59160798
[18,]  0.42257713
[19,] -1.09870053
[20,] -0.08451543
attr(,"scaled:center")
[1] 10.5
attr(,"scaled:scale")
[1] 5.91608

> mean(z_score)
[1] 0

> sd(z_score)
[1] 1

# 偏差値 = z得点 * 10 + 50
# 平均(偏差値) = 50
# 標準偏差(偏差値) = 10
> standard_score = z_score * 10 + 50
> standard_score
          [,1]
 [1,] 42.39361
 [2,] 45.77423
 [3,] 60.98701
 [4,] 62.67731
 [5,] 64.36762
 [6,] 35.63238
 [7,] 50.84515
 [8,] 33.94207
 [9,] 66.05793
[10,] 40.70330
[11,] 57.60639
[12,] 37.32269
[13,] 59.29670
[14,] 52.53546
[15,] 47.46454
[16,] 55.91608
[17,] 44.08392
[18,] 54.22577
[19,] 39.01299
[20,] 49.15485
attr(,"scaled:center")
[1] 10.5
attr(,"scaled:scale")
[1] 5.91608

> mean(standard_score)
[1] 50
> sd(standard_score)
[1] 10

以上です。

Rによるバスケット分析まとめ

概要

Rによるバスケット分析方法をまとめました。

Rのバスケット分析パッケージを使えば、

すぐに詳細なバスケット分析ができます。

書籍『リテールデータ分析入門』のデータを使って、実際に分析してみます。

準備

データの準備

まず、商品カテゴリ(例:おにぎり、お茶など)一覧を定義します。

Cate1,Cate2,Cate3,...
おにぎり,お茶,弁当,...

以後、

つぎに、実際の購買データを準備します。

レシート番号,購買商品
1,おにぎり&お茶
2,お茶&弁当
3,弁当
4,...

このままでは、Rで処理しづらいので、以下のように、 各カテゴリが

購入されていれば 1

購入されていなければ 0

とダミー変数に変換しておきます。

ReceiptNo,Cate1,Cate2,Cate3,Cate4,Cate5,Cate6,Cate7,Cate8,Cate9,Cate10
1,0,0,1,1,0,0,1,0,0,0
2,1,0,0,0,0,0,1,0,0,0
3,0,1,0,0,1,0,1,0,0,0
...

cf.ダミー変数への変換方法は、別途記事にします。

Rパッケージの準備

Rパッケージのarulesをインストールします。 https://www.rdocumentation.org/packages/arules/versions/1.6-2

install.packages("arules")

バスケット分析の実施

パッケージを読み込みます。

library("arules")

上記で準備しておいたダミー変数のcsvファイルを読み込みます。

data_set <- read.csv("~/Downloads/Receipt1000.csv", header = T, row.names = 1)
// 引数の説明:
// header 有り: header = T
// header 無し: header = F
// 1列目を行名として使う場合: row.names = 1
// 1列目を行名として使う場合: row.names削除

行列形式に変換します

data_set_matrix = as(data_set, "matrix")

バスケット分析を実行しルールを生成します

rules <- apriori(data_set_matrix, parameter = list(supp = 0.06, conf = 0.2, maxlen = 2))
// 引数の説明:
// supp: support値下限を指定
// conf: confidence値下限を指定
// maxlen: 条件部個数の最大値を指定

ルールの概要を確認します

summary(rules)
set of 36 rules

rule length distribution (lhs + rhs):sizes
 1  2 
 7 29 

   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   2.000   2.000   1.806   2.000   2.000 

summary of quality measures:
    support          confidence          lift           count       
 Min.   :0.06300   Min.   :0.2030   Min.   :0.760   Min.   : 63.00  
 1st Qu.:0.08075   1st Qu.:0.2593   1st Qu.:1.000   1st Qu.: 80.75  
 Median :0.09900   Median :0.3676   Median :1.083   Median : 99.00  
 Mean   :0.13458   Mean   :0.3610   Mean   :1.131   Mean   :134.58  
 3rd Qu.:0.14850   3rd Qu.:0.4316   3rd Qu.:1.231   3rd Qu.:148.50  
 Max.   :0.43800   Max.   :0.6201   Max.   :1.742   Max.   :438.00  

mining info:
         data ntransactions support confidence
rules          1000    0.06        0.2

生成されたルール先頭行を表示します

// 引数の説明:
// by: ソート項目を指定(supp=support, conf=confidence, lift)

// 表の説明:
// lhs: 条件部 空の場合は、各カテゴリの購買確率
// rhs: 結論部

> inspect(head(rules, by = "supp"))
    lhs    rhs     support confidence lift count
[1] {}  => {Cate1} 0.438   0.438      1    438  
[2] {}  => {Cate2} 0.391   0.391      1    391  
[3] {}  => {Cate3} 0.263   0.263      1    263  
[4] {}  => {Cate4} 0.229   0.229      1    229  
[5] {}  => {Cate8} 0.212   0.212      1    212  
[6] {}  => {Cate7} 0.205   0.205      1    205  

> inspect(head(rules, by = "conf"))
    lhs         rhs     support confidence lift     count
[1] {Cate4}  => {Cate1} 0.142   0.6200873  1.415725 142  
[2] {Cate10} => {Cate1} 0.080   0.5925926  1.352951  80  
[3] {Cate9}  => {Cate1} 0.103   0.5786517  1.321123 103  
[4] {Cate3}  => {Cate1} 0.140   0.5323194  1.215341 140  
[5] {Cate5}  => {Cate1} 0.099   0.4876847  1.113435  99  
[6] {Cate6}  => {Cate1} 0.085   0.4696133  1.072176  85

> inspect(head(rules, by = "lift"))
    lhs         rhs     support confidence lift     count
[1] {Cate9}  => {Cate4} 0.071   0.3988764  1.741818  71  
[2] {Cate4}  => {Cate9} 0.071   0.3100437  1.741818  71  
[3] {Cate4}  => {Cate1} 0.142   0.6200873  1.415725 142  
[4] {Cate1}  => {Cate4} 0.142   0.3242009  1.415725 142  
[5] {Cate10} => {Cate1} 0.080   0.5925926  1.352951  80  
[6] {Cate9}  => {Cate1} 0.103   0.5786517  1.321123 103 

ルール全体を表示します

inspect(sort(rules, by="supp"))
// 引数の説明:
// by: ソート項目を指定(supp=support, conf=confidence, lift)
     lhs         rhs     support confidence lift      count
[1]  {}       => {Cate1} 0.438   0.4380000  1.0000000 438  
[2]  {}       => {Cate2} 0.391   0.3910000  1.0000000 391  
[3]  {}       => {Cate3} 0.263   0.2630000  1.0000000 263  
[4]  {}       => {Cate4} 0.229   0.2290000  1.0000000 229  
[5]  {}       => {Cate8} 0.212   0.2120000  1.0000000 212  
[6]  {}       => {Cate7} 0.205   0.2050000  1.0000000 205  
[7]  {}       => {Cate5} 0.203   0.2030000  1.0000000 203  
[8]  {Cate2}  => {Cate1} 0.168   0.4296675  0.9809761 168  
[9]  {Cate1}  => {Cate2} 0.168   0.3835616  0.9809761 168  
[10] {Cate4}  => {Cate1} 0.142   0.6200873  1.4157245 142  
[11] {Cate1}  => {Cate4} 0.142   0.3242009  1.4157245 142  
[12] {Cate3}  => {Cate1} 0.140   0.5323194  1.2153411 140  
[13] {Cate1}  => {Cate3} 0.140   0.3196347  1.2153411 140  
[14] {Cate3}  => {Cate2} 0.115   0.4372624  1.1183180 115  
[15] {Cate2}  => {Cate3} 0.115   0.2941176  1.1183180 115  
[16] {Cate9}  => {Cate1} 0.103   0.5786517  1.3211226 103  
[17] {Cate1}  => {Cate9} 0.103   0.2351598  1.3211226 103  
[18] {Cate5}  => {Cate1} 0.099   0.4876847  1.1134355  99  
[19] {Cate1}  => {Cate5} 0.099   0.2260274  1.1134355  99  
[20] {Cate4}  => {Cate2} 0.097   0.4235808  1.0833268  97  
[21] {Cate2}  => {Cate4} 0.097   0.2480818  1.0833268  97  
[22] {Cate7}  => {Cate1} 0.094   0.4585366  1.0468872  94  
[23] {Cate1}  => {Cate7} 0.094   0.2146119  1.0468872  94  
[24] {Cate5}  => {Cate2} 0.087   0.4285714  1.0960906  87  
[25] {Cate2}  => {Cate5} 0.087   0.2225064  1.0960906  87  
[26] {Cate6}  => {Cate1} 0.085   0.4696133  1.0721764  85  
[27] {Cate8}  => {Cate1} 0.081   0.3820755  0.8723184  81  
[28] {Cate10} => {Cate1} 0.080   0.5925926  1.3529511  80  
[29] {Cate7}  => {Cate2} 0.077   0.3756098  0.9606388  77  
[30] {Cate4}  => {Cate3} 0.077   0.3362445  1.2784964  77  
[31] {Cate3}  => {Cate4} 0.077   0.2927757  1.2784964  77  
[32] {Cate9}  => {Cate4} 0.071   0.3988764  1.7418184  71  
[33] {Cate4}  => {Cate9} 0.071   0.3100437  1.7418184  71  
[34] {Cate6}  => {Cate2} 0.068   0.3756906  0.9608455  68  
[35] {Cate9}  => {Cate2} 0.064   0.3595506  0.9195667  64  
[36] {Cate8}  => {Cate2} 0.063   0.2971698  0.7600251  63

以上でRによるバスケット分析ができました。

応用

カテゴリの粒度

カテゴリの粒度は、データによって異なります。

カテゴリを集約しすぎると、施策へ反映しづらくなり、

カテゴリを分割しすぎると、分析結果の解釈が難しくなります。

出現頻度に応じて、集約したり分割することで調整します。

カテゴリの追加

カテゴリに商品だけでなく、

時間情報・曜日など追加したり、

購入者属性を追加することで、

異なる種類のカテゴリ同士のルールを生成できます。

同時購買を生み出す消費者心理

バスケット分析の結果で、同時購買を生み出す消費者心理を推測することは可能ですが、本当に有効な打ち手かどうか検討する必要があります。

一般的に、複数の商品の組み合わせによって得られる便益は、以下のように分類できます:

  • 商品A単体の便益
  • 商品Aだけがもつ固有便益
  • 商品B単体の便益
  • 商品Bだけがもつ固有便益
  • 商品A,Bの共通便益: 重複する便益
  • 商品A,Bの併用便益: 組み合わせることで新規に生まれる便益

さらに、共通便益と併用便益の有無で、以下のように分類できます:

  • 共通便益なし & 併用便益なし (例)おむつとビール、など殆どの組み合わせ
  • 共通便益なし & 併用便益あり (例)おにぎりとお茶、カミソリ本体と刃
  • 共通便益あり & 併用便益なし (例)お茶と牛乳、アメとガム
  • 共通便益あり & 併用便益あり (例)なし

一般的に併用便益の魅力を消費者が理解できれば、同時購買が発生しやすく、

共通便益がある場合、同時購買が発生しにくいです。

バスケット分析を通して、消費者に併用便益の魅力を伝える施策を打つ必要があります。

レコメンデーションに用いられる3つの指標まとめ

概要

購買データから、同時に購入される商品の傾向を分析することを、

マーケットバスケット分析(ショッピングバスケット分析とも呼ばれる。以下バスケット分析)

と呼びます。アソシエーション分析の一種です。

バスケット分析では、

「商品Aを買うときは、商品Bを買う」といった同時購買のルールを見出すことが求められます。

以下では、

「商品Aを買う」(条件部)とき、「商品Bを買う」(結論部)

というルールを

A=>B

という記号で表現します。

「商品Aを買うときは、商品Bを買う」というパターンが発見されたら、

レコメンデーションで使うことができます。

ここでは、まず、

レコメンデーションに用いられる3つの指標をまとめたいと思います。

3つの重要な指標

3つの重要な指標について説明します。

  • Support(支持度): 同時確率
  • Confidence(信頼度): 条件付き確率
  • Lift(リフト): 改善率

Support(支持度)

A=>BのSupport(支持度) とは、

すべてのトランザクションに占める商品A,Bが同時に購入されたトランザクションの割合を指します。

A=>BのSupport(支持度) : AとBの同時購買回数 / 全トランザクション数

と定義します。

具体的には、

全購買件数が5件あり、

商品Aと商品Bを購入した回数が2回であれば、

A=>BのSupport(支持度) : 2 / 5 = 0.4

となります。

Support(支持度)の高いルールは良いルールと言えます。

なお、

A=>BのSupport(支持度)

B=>AのSupport(支持度)

は、等しくなります。

Confidence(信頼度)

A=>BのConfidence(信頼度)とは、

商品Aを買ったトランザクションに占める、

Bが同時に購入されたトランザクションの割合、

つまり条件付き確率を指します。

A=>BのConfidence(信頼度) : AとBの同時購買回数 / Aの購買回数

B=>AのConfidence(信頼度) : AとBの同時購買回数 / Bの購買回数

と定義します。

具体的には、

商品Aを購入したトランザクションが3件、

商品Bを購入したトランザクションが2件、

商品Aと商品Bを同時購入したトランザクションが2件であれば、

A=>BのConfidence(信頼度) : 2 / 3

B=>AのConfidence(信頼度) : 2 / 2

となります。

Confidence(信頼度)の高いルールは良いルールと言えます。

なお、

A=>BのConfidence(信頼度)

B=>AのConfidence(信頼度)

は、基本的に異なります。

つまり、方向性によっては、レコメンデーションの意味をなさないときがありますので、注意が必要です。

例)髭剃り本体を買った人に替え刃をレコメンドできるが、替え刃を買った人に髭剃り本体をリコメンドしても意味がない(当然もっているはず)

Lift(リフト)

A=>BのLift(リフト)とは、

A=>BのSupport(支持度)を、

AとBが独立と仮定したときの購買確率で割った値を指します。

Aの購買確率 : Aの購買回数 / 全トランザクション数

Bの購買確率 : Bの購買回数 / 全トランザクション数

A=>BのLift(リフト) : A=>BのSupport(支持度) / Aの購買確率 * Bの購買確率

別の定義で表現すれば、

A=>BのConfidence(信頼度)を、

Bの購買確率で割った値を指します。

A=>BのLift(リフト) : A=>BのConfidence(信頼度) / Bの購買確率

要するに、

Aの購入によってどれぐらい確率が持ち上げ(Lift)られているか?

という指標です。

A=>BのLift(リフト)値1 を越えているかが、有効なルールの判断基準と言えます。

具体的には、

トランザクションが5件あり、

商品Aを購入したトランザクションが3件、

商品Bを購入したトランザクションが2件であれば、

Aの購入確率 : 3 / 5

Bの購入確率 : 2 / 5

であり、

商品Aと商品Bを同時購入したトランザクションが2件であれば、

A=>BのSupport(支持度) : 2 / 5

A=>BのConfidence(信頼度) : 2 / 3

であり、

A=>BのLift(リフト) : A=>BのSupport(支持度) / Aの購買確率 * Bの購買確率 = (2 / 5) / (3 / 5) * (2 / 5) = 5 / 3 > 1

または、

A=>BのLift(リフト) : A=>BのConfidence(信頼度) / Bの購買確率 = (2 / 3) / (2 / 5) = 5 / 3 > 1

であり、

この例では、

A=>BのLift(リフト)値1 を超えているので、

同時購入されやすいと言えます。

逆に、

A=>BのLift(リフト)値1 以下ならば、

同時購入されにくいと言えます。

以上、

3つの重要な指標について説明しました。

  • Support(支持度): 同時確率
  • Confidence(信頼度): 条件付き確率
  • Lift(リフト): 改善率

『リテールデータ分析入門』書誌情報

顧客の購買行動を測定したデータをリテールデータと呼びますが、

大規模なリテールデータに特化したRによる分析ノウハウが記載されている書籍です。

書誌情報

リテールデータ分析入門

上田隆穂/編著 田島博和/編著 奥瀬喜之/編著 斉藤嘉一/編著
出版社名    中央経済社
出版年月    2014年6月
ISBNコード   978-4-502-09630-3 
(4-502-09630-X)
税込価格    3,024円
頁数・縦    236P 21cm

目次概要

第1章 リテールデータを活用するにはどう考えればよいのか
第2章 データを俯瞰する
第3章 顧客とのリレーションシップをつくる
第4章 同時に購買されやすい商品を知る
第5章 重要な顧客を特定する
第6章 消費者の立場から価格を設定する
第7章 ブランド・ロイヤルティとストア・ロイヤルティを構築する
第8章 購買数量を把握する
第9章 価格とブランド・ロイヤルティがブランド選択に及ぼす影響を把握する

目次詳細

 まえがき
第1章 リテールデータを活用するにはどう考えればよいのか
1   リテール・サイエンスの進展
2   分析にあたっての本質的な課題
3   データ分析から仮説づくりのきっかけをどう生み出すか
4   たとえばPOSデータ分析レベルはどのように整理できるか?
5   新しいデータ分析としての定性データのテキスト・マイニング化(定量化)による分析
6   リテールデータの活用の方向性
第2章 データを俯瞰する
1   ID付きPOSデータとは何か
2   データを詳細に分析する前にしておくこと
3   データの特徴を可視化する
4   数値からデータの特徴をつかむ
5   データクリーニング
第3章 顧客とのリレーションシップをつくる
1   リレーションシップ・マーケティング
2   ID付きPOSデータに基づくリレーションシップ・マーケティングの実践
3   “リレーションシップ”と消費者心理
4   まとめ
第4章 同時に購買されやすい商品を知る
1   レシートデータ
2   ショッピングバスケット分析の考え方
3   Rによるショッピングバスケット分析
4   ショッピングバスケット分析の応用
5   同時購買を引き出す仕組みづくりに向けて
第5章 重要な顧客を特定する
1   デシル分析
2   RFM分析
3   デシル分析とRFM分析の発展
第6章 消費者の立場から価格を設定する
1   価格設定における消費者心理の重要性
2   消費者の価格反応―価格弾力性
3   消費者の値ごろ感―参照価格概念
4   消費者心理を踏まえた価格設定
5   まとめ
第7章 ブランド・ロイヤルティとストア・ロイヤルティを構築する
1   ブランドとは何か
2   ブランド・ロイヤルティをどのように捉えるのか
3   ストア・ロイヤルティをどのように捉えるのか
4   真のブランド・ロイヤルティとストア・ロイヤルティの構築に向けて
第8章 購買数量を把握する
1   価格の変化による購買数量の把握
2   価格弾力性と値引きの影響
3   複数のマーケティング変数の影響を把握する
第9章 価格とブランド・ロイヤルティがブランド選択に及ぼす影響を把握する
1   ブランド選択をモデル化する
2   2項ロジットモデルとロジスティック回帰分析
3   Rによる2項ロジットモデルの推定
4   多項ロジットモデル―選択肢が3つ以上のロジットモデル
5   まとめ
 
あとがき
索引