価格設定実験プロトコル

価格設定におけるA/Bテストプロトコル


ホーム » 価格最適化 » このページ

測定無しに価格最適化はなく、価格最適化には実験と測定が必須です。実際、何度も繰り返しますが、価格設定とは「価格変更」であり、その変更に影響されずに新価格に対するマーケットの反応についての綿密な知識を与えることを目的としています。価格戦略は、既存の戦略をこまかに調整するために大半の変更が「帰納的」に分析された知識主導型プロセスであるべきです。このページでは、Lokadサポートで行われる価格設定のためのA/Bテストプロトコルの概要を説明します。議論は手持ちのツールをうまく利用していますが、プロトコル自体がLokad特定のものでなく他のツールでも、各シートが慎重に保守されていればたとえエクセルでも、複製可能なことは述べておきます。


1. 参照文書を作成する

もしあなたが価格設定実験を1回実行できるのであれば、すぐに多数の価格設定実験を行っているでしょう。このすべての情報を収集するのに構造化された方法がなければ、あなたが得るのは面倒な結果であり、得られるはずの見込みある考察もほとんど失うことになります。したがって、すべての価格設定実験は、この実験に関連するすべての情報を集める“参照文書”の作成から始めることを勧めます。この文書はMicrosoftワードファイルでもいいですが、最近であれば、Googleサイトやhackpadのようなウェブネイティブ連携ツール、または オンラインで利用できるコンテンツマネージメントシステムを使うことを強くお勧めしています。

文書を作成したら、実験に”覚えやすい名前”を付けることにも労力を割いてください。実際に、実験とは、時間をかけて人々が入れ替わっていく中で、実験について簡単にコミュニケーションができる場合、そしてその結論が価格設定チームとその後継者に覚えてもらえる場合にのみ、効果的となります。 退屈は知識の敵です。もし、名前が記憶に残らないほどにつまらなかったら、この情報は熱心な従業員からでさえも棄却されるでしょう。

2. 専用プロジェクトを作成する

Lokad内で価格設定実験専用のプロジェクトを作成してください。明確にするためにも、実験名を覚えやすい名前にした後で、このプロジェクトの名前を決めてください。さらに、オンラインで利用可能な 参照文書に直接リンクするlabelタイルも利用してください。その結果として、最初のエンビジョンスクリプトは以下のようになります。

// Pricing Experiment: Sevilla
// Start: 2014-07-09 End: 2014-08-08
// Author: Joannes Vermorel
show label "http://example.org/my-reference-document Ref. Document"

このスクリプトでは、labelタイルは、価格実験そのものを詳述した参照文書へのハイパーリンクとしてダッシュボードに表示されます。

この実験はまさに価格実験の異なる段階を経て複数の目的を果たすことになります。 以下のように使用されます。

  1. アイテムのサンプルコントロールグループを作成する
  2. それらのグループを持続させる
  3. 改定価格戦略を規定する
  4. 価格の適正な展開を確認する
  5. 2つの対照群を可視化しまとめる

すべての手順は、このプロジェクトのスクリプトのセクションを通して実行されます。

3. 試験仮説を定義する

価格戦略を試験するたびに、その結果が当初の予測に沿っているかどうかは別にして、その結果を異なる視点から解釈することに魅力を感じます。心理バイアスがナラティブファラシーに陥っているからです。その心理バイアスはタレブによって以下のように見事に説明されています。

ナラティブファラシー は、説明を織り込むことなく、又は等価的に、論理リンクやそれらの関連性の矢印を強制しながら事実の結果を見る我々の制限された能力に応じている。説明は事実と一緒に紐付けられる。より覚えやすいように作り、理解しやすいようにする。この傾向が誤った方向にいくと、我々の理解の感銘を増長することになる。 —ナシム・ニコラス・タレブ、黒鳥理論

価格設定に関しては特に深刻な問題です。なぜなら市場にとっての“制御された環境”など存在しないからです。どれだけ実験プロトコルに注意が払われても、競合他社の取り組みに始まる多くの要因が我々の制御不能なところに取り返しのつかないほど残っています。

したがって、 実験の始めに、試験される仮説を定義づけることが非常に重要となります。実験の過程において改訂されたその場限りの仮説ではなく、我々は定義づけられた仮説の立証または反証をしているということを明確にするためです。前述した参照文書の冒頭にこの仮説を書いてください。よい仮説は次のようになります。販売量の暴落は需要の減少に説明されるものではなく、積極的な価格を出す競合他社の可視性の減少にある。故に、我々が当該製品のマージンを積極的に引き下げることで販売量も呼応して強化されるはずだ。

4. 陽性対照群と陰性対照群

仮説を書いたら、次は実験を設計していきます。異なる複数の顧客グループに対して、同じ製品に2つの異なる価格を示すことは、概して不可能であり非現実的–時には違法-です。それ故に、小売においてより実用的なアプローチとは、わずか2つの等価的な製品グループを全体のカタログの中から選ぶことにあります。 2つのサンプルはそれぞれ以下のように名付けます。
  • 新価格戦略を適用する陽性対照群
  • 現行の価格戦略を変更しない陰性対照群

    陽性対照群と陰性対照群の結果を比較することで、当初の仮説が正しかったかどうかの評価ができます。エンビジョンのハッシュ関数を使えば、2つの対照群のランダム選択も非常に簡単に行えます。


    seed := "hello world"
    R = rankd(hash(concat(Id, seed)))
    where R <= 1000
    // positive group here
    where R > 1000 and R <= 2000
    // negative group here

    上記のスクリプトにおいて、2行目ですべての項目をランダムに並び替える処理をしています。このハッシュ関数は、入力値として渡された文字列に疑似乱数を生成します。これは同じ文字列が毎回同じハッシュ値を得られるため、正ランダム値ではありません。故に、異なるサンプルを生成するために、一度テキストをseedとして変更する必要があります。そして、3行目にて1,000項目を陽性対照群として抽出します。5行目では、同様にして 1,000項目を陰性対照群として保持します。

    このロジックは“両方がある条件に適合する”2つのグループを定義付けたいシーンでも簡単に対応できます。例えば、Fabrikamブランドのアイテムセットを含む対照群を確立したいと仮定しましょう。この場合は以下のように処理します。


    where Brand == "Fabrikam" // scoping only the brand 'Fabrikam'
    seed := "hello world"
    R = rankd(hash(concat(Id, seed)))
    where R <= 50
    // positive group here
    where R > 50 and R <= 100
    // negative group here

    ===5. 対照群の永続化===
    対照群の選択において複雑な条件が使われていると、同じサンプリングロジックでも、経時的に同じ項目リストを返さなくなるというリスクがあります。例えば、新しい項目が導入されると、前章で紹介したように、サンプリングロジックに影響を与えます。故に、経時的に意図せず変化できるよう対照群を永続させることを奨励しているのです。


    seed := "hello world"
    R = rankd(hash(concat(Id, seed)))
    Control = ""
    where R <= 1000
    Control = "pos" // positive group
    where R > 1000 and R <= 2000
    Control = "neg" // negative group

    // exporting the group
    startDate := "2014-07-09"
    endDate := "2014-08-09"
    where Control != ""
    show table "sample" export:"/exp/g-sevilla.tsv" with \
    Id, startDate as "Date", endDate, Control, Price

    上記のスクリプトでは、グループが確立されるとその結果を`g-sevilla.tsv`という名前のファイルに保存します。次の章にて、接頭辞`g` (または任意の代替文字)がサンプルの重複回避にどれだけ便利であるかを説明します。`sevilla`は単なる 覚えやすい実験名の例です。このファイルは、Lokadにて後にリロード可能なイベントストリームファイルとして既にフォーマットされています。このロジックを実行し、既存のファイルの上書きを防ぐ為、ファイル出力行をコメントアウトします。そして、安全策として、このファイルをダウンロードし、参照文書に添付してコピーを保存してください。

    その後、別の実行中の実験のグループ定義付けと重なるのを避ける為、保存したファイルをリロードし、この情報を使用して関連する項目を除外できます。 以下のようになります。


    read "/exp/g-*" as Experiments // loading all experiments so far

    today := Date(2014,7,9)
    IsPartOfGroup = false
    where Experiments.EndDate < today // we exclude all ongoing experiments
    // look for the existence of a matching group
    IsPartOfGroup = exists(Experiments.Date)

    where not IsPartOfGroup
    seed := "hello world"
    R = rankd(hash(concat(Id, seed)))
    Control = ""
    where R <= 1000
    // positive group here
    where R > 1000 and R <= 2000
    // negative group here

    ===6. 新価格設定スクリプトを定義する===
    前章において、複数の価格戦略について取り扱ってきました。実験プロトコルはこの時点において、改訂価格スクリプトを実行する段階にきました。価格設定ロジックの詳細は本章の範囲を超越していますが、価格は一般的にシンプルなtable タイルで算出されることを知っておく必要があります。


    read "/exp/g-sevilla.tsv" as Scope

    // Original logic to generate the control groups is commented out.
    // We reload the control groups directly from the persisted copy.
    Control = last(Scope.Control) or ""

    where Control != "" // excluding items not part of the scope
    // snipped here: actual pricing logic
    show table "sample" export:"/exp/p-sevilla.tsv" with Id, today() as Date, Price

    このスクリプトでは、まず存続している対照群のコピーを読み込むことから始めています。そして、範囲をそれらの対照群だけに狭めて再定義します。最後に、新価格のファイル出力を生成します。

    実際問題として、改訂価格を永続させることは、対照群を存続させることに比べるとそこまでシンプルではありません。実際、実験の期間中も、価格はそれぞれの根幹となる戦略に沿って変化し続けています。

    ===7. 価格の公開と観察===
    スクリプトが改定価格を生成する段階になると、それらの価格は多様な販路にて公開されるべきです。可能な限り、自動価格公開プロセスに力を入れる必要があります。とりわけ、Lokadはプロジェクトの自動トリガーに対応したREST APIを提案します。スクリプトの実行が完了すると、出力ファイルはLokadからFTPやFTPSに回収され本番システムにインポートされます。

    価格改訂後の販売観察に確信を持つ為にも、販路に出される価格は実際にエンビジョンで算出された価格であることを確実にすることは重要です。実際に公開された価格と価格戦略で算出された価格間の不一致につながるような誤った価格や不公平な価格の導入が行われていないかを日常的に観察します。


    公開された歴代の価格はLokadに入力値としてフィードバックされることが理想ではあります。データループがある場合、公開価格とLokad算出価格の比較が可能になり、それらの値が揃っていることを確実にできます。下記スクリプトでは、実験とその元来算出されたときの価格で範囲を絞り込んで検索した、歴代の価格を読み込んでいます。


    // ... snipped items and other data files ...
    read "/prices.tsv" as Prices // from the production systems
    read "/exp/g-sevilla.tsv" as Scope
    read "/exp/p-sevilla.tsv" as ExpPrices

    // The original logic to generate control groups is commented out.
    // We reload the control groups directly from the persisted copy.
    Control = last(Scope.Control) or ""

    where Control != "" // excluding items not part of the scope
    // relevant date for the publish date of the prices
    when date <= date(2014,8,1)
    where last(Prices.Price) != last(ExpPrices.Price)
    show table "Item count with price mismatch" with count(Id)

    このスクリプトは、本番システムから検索された価格と価格設定スクリプトが元来算出した価格が合致していない件数を出力します。

    このようにして、数日か数週間後、価格設定実験の適用可能な時間枠に応じて、2つのグループの販売の進化について集められた観察結果を比較し、当初の仮説が正しかったかどうかを評価します。最終的な実験を集積することは、事業者が経時的にますます効果的な価格戦略を設計していくための資産なのです。


FORMATTER ERROR (Transcluded inexistent page or this same page)