#13│通販企業版パランティア―オントロジーという地図


パランティアの最も重要な概念は「オントロジー(Ontology)」だ。

オントロジーとはもともと哲学用語で「存在の本質を問う学問」だが、データの文脈では「現実世界の概念をデータとして定義し、その関係性を記述する仕組み」を指す。

パランティア Foundryのオントロジーは「部品・工場・注文というオブジェクトが、意味的な関係性で結ばれ、アクションをトリガーする仕組み」だと説明されている。

難しく聞こえる。しかし本質は単純だ。

「顧客ID:12345」というデータを「現在ウィンバック施策の対象となっている、LTV 3万円の休眠60日顧客」という意味と結びつける仕組み

これがオントロジーだ。


なぜデータは「意味を持たない」のか

Treasure Dataに格納されたデータは、何の意味も持たない。

orders テーブルの customer_id = 12345amount = 8900order_date = 2024-09-15 というレコードは、単なる数字と日付の羅列だ。

それが「意味を持つ」のは、こう解釈されたときだ

「この顧客は3ヶ月前に8,900円の買い物をした。それ以来購買がなく、過去1年間で累計4.2万円のLTVがある。類似顧客の平均購買間隔は45日であり、現在90日が経過している。離脱リスクは中程度で、次のウィンバックメールに対して40%の確率で反応する」

データを「意味ある情報」に変換するのが、オントロジー設計の仕事だ。

パランティアはこれをシステムとして構造化した。

しかし「パランティアがなければオントロジーを実現できない」というわけではない。Treasure Dataというデータプラットフォームと、SQLという言語があれば、オントロジー的な設計は実現できる。


「顧客オントロジー」の設計—3つの層

通販企業における顧客オントロジーは3つの層で構成される。

層①:属性層(Attributes Layer)
顧客が持つ静的・準静的な属性
→ 年齢層・性別・地域・会員ランク・初回購買年
層②:行動層(Behavior Layer)
顧客が示す動的な行動パターン
→ 最終購買日・購買頻度・LTV・閲覧カテゴリ・メール開封率
層③:状態層(State Layer)
顧客が今いるビジネス上の「状態」
→ RFMセグメント・LTVティア・チャーンリスクスコア・CJOのステージ

この3層を組み合わせることで「この顧客は誰で、何をしていて、今どんな状態か」が一つのレコードとして表現できる。

-- 「顧客オントロジーテーブル」の構築
-- 3つの層を統合した顧客マスターテーブル

CREATE TABLE customer_ontology AS
WITH
-- ─── 層①:属性層 ───
attribute_layer AS (
  SELECT
    customer_id,
    gender,
    age_group,
    prefecture,
    registration_date,
    DATE_DIFF('day', registration_date, CURRENT_DATE) AS tenure_days
  FROM customer_master
),

-- ─── 層②:行動層 ───
behavior_layer AS (
  SELECT
    customer_id,
    MAX(order_date)                                    AS last_order_date,
    COUNT(order_id)                                    AS total_orders,
    SUM(amount)                                        AS total_ltv,
    AVG(amount)                                        AS avg_order_value,
    DATE_DIFF('day', MAX(order_date), CURRENT_DATE)    AS recency_days,
    -- 最も多く購買したカテゴリ(好みカテゴリ)
    MAX_BY(product_category, order_count)              AS primary_category
  FROM (
    SELECT
      o.customer_id,
      o.order_id,
      o.order_date,
      o.amount,
      oi.product_category,
      COUNT(oi.product_id) OVER (
        PARTITION BY o.customer_id, oi.product_category
      )                                                AS order_count
    FROM orders o
    JOIN order_items oi ON o.order_id = oi.order_id
    WHERE o.status = 'completed'
  ) t
  GROUP BY customer_id
),

-- ─── 層③:状態層 ───
state_layer AS (
  SELECT
    customer_id,
    -- RFMスコア
    NTILE(5) OVER (ORDER BY recency_days DESC)         AS r_score,
    NTILE(5) OVER (ORDER BY total_orders ASC)          AS f_score,
    NTILE(5) OVER (ORDER BY total_ltv ASC)             AS m_score,
    -- LTVティア
    CASE
      WHEN total_ltv >= 100000 THEN 'VIP'
      WHEN total_ltv >= 30000  THEN '優良'
      WHEN total_ltv >= 10000  THEN '標準'
      ELSE                          '育成中'
    END                                                AS ltv_tier,
    -- チャーンリスク状態
    CASE
      WHEN recency_days >= 180 THEN '深刻休眠'
      WHEN recency_days >= 90  THEN '要注意休眠'
      WHEN recency_days >= 60  THEN '軽度休眠'
      ELSE                          '活性顧客'
    END                                                AS churn_status,
    recency_days,
    total_orders,
    total_ltv
  FROM behavior_layer
)

SELECT
  a.customer_id,
  -- 層①:属性
  a.gender,
  a.age_group,
  a.prefecture,
  a.tenure_days,
  -- 層②:行動
  b.last_order_date,
  b.total_orders,
  b.total_ltv,
  b.avg_order_value,
  b.recency_days,
  b.primary_category,
  -- 層③:状態
  s.r_score,
  s.f_score,
  s.m_score,
  (s.r_score + s.f_score + s.m_score)                AS rfm_total_score,
  s.ltv_tier,
  s.churn_status,
  -- メタデータ
  CURRENT_TIMESTAMP                                   AS updated_at,
  'customer_ontology_v1.0'                            AS schema_version
FROM attribute_layer a
LEFT JOIN behavior_layer b USING (customer_id)
LEFT JOIN state_layer s USING (customer_id)

「関係性オントロジー」—顧客×商品×チャネルをつなぐ

パランティアのオントロジーの真価は「オブジェクト同士の関係性を定義すること」にある。

「顧客」と「商品」と「チャネル」の関係性を定義すると、「この顧客はこの商品をこのチャネルで買いやすい」という行動可能なインテリジェンスが生まれる。

-- 顧客×商品×チャネルの関係性テーブル

SELECT
  o.customer_id,
  oi.product_category,
  o.acquisition_channel,  -- 流入チャネル(メール/SNS広告/自然検索等)
  COUNT(o.order_id)                                   AS purchase_count,
  SUM(oi.amount)                                      AS category_ltv,
  -- このチャネル×カテゴリの組み合わせが全購買に占める割合
  ROUND(
    SUM(oi.amount) * 100.0
    / SUM(SUM(oi.amount)) OVER (PARTITION BY o.customer_id)
  , 1)                                                AS share_of_wallet_pct,
  -- 直近この組み合わせで購買した日
  MAX(o.order_date)                                   AS last_purchase_date
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
WHERE
  o.status = 'completed'
  AND o.order_date >= DATE_ADD('day', -365, CURRENT_DATE)
GROUP BY
  o.customer_id,
  oi.product_category,
  o.acquisition_channel
ORDER BY
  o.customer_id,
  share_of_wallet_pct DESC

この「関係性テーブル」から「顧客Aはメールチャネル経由で、スキンケアカテゴリを購買する傾向が最も強い(シェア・オブ・ウォレット40%)」という情報が読み取れる。

これが「次の施策をどのチャネルで、何のカテゴリで打つか」という意思決定の根拠になる。


digdagで「生きたオントロジー」を維持する

オントロジーテーブルは作るだけでは意味がない。顧客の状態は毎日変わるため、毎日更新する必要がある。

# digdag ワークフロー:顧客オントロジーの日次更新

timezone: Asia/Tokyo

schedule:
  daily>: "02:00:00"

+step1_rebuild_customer_ontology:
  td>: queries/ontology/customer_ontology.sql
  database: production
  # 毎日全件再構築(CREATE OR REPLACE TABLE)
  _retry: 3

+step2_rebuild_relationship_ontology:
  td>: queries/ontology/relationship_ontology.sql
  database: production
  _retry: 3

+step3_sync_to_cjo:
  # CJOの外部セグメントとして参照されるセグメントを更新
  td>: queries/ontology/cjo_segment_refresh.sql
  database: production

_error:
  http>: https://hooks.slack.com/services/XXX/YYY/ZZZ
  method: POST
  content_type: application/json
  content: '{"text": "⚠️ オントロジー更新バッチが失敗しました"}'

次回予告

#14│通販企業版パランティア―通販企業の「作戦室」を作る

パランティアが軍の司令部に提供する「作戦室」

全ての情報が一画面に集約され、意思決定が即座に下せる環境。これを通販企業のマーケティング・MD・経営に実装する。


MarTech Farmをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む