paper2skills Playbook

CDA — 隐私保护因果渠道归因:无用户数据的多触点归因

Skill-CDA-Privacy-Causal-Attribution · 15-营销投放分析

causalexperimentforecastingoptimization广告与投放风控与合规WF-B 广告优化WF-D 选品扫描
实现难度⭐⭐⭐☆☆
业务优先级⭐⭐⭐⭐⭐
业务视角
适用角色CMO / 营销负责人 · 广告优化师 · CEO
适用平台Amazon + TikTok + Meta + KOL 四渠道 · Prime Day / Black Friday 预算前置
什么情况下用同时跑 Amazon 广告/TikTok/网红投放/邮件,不知道整体预算怎么分配最高效;网红投放花了大钱但不知道带来多少真实 GMV
成功是什么样的建立全渠道营销归因模型(MMM),每个渠道真实 ROI 可量化,大促前做预算优化模拟
业务痛点
多渠道预算分配靠感觉网红带货效果不知道怎么量化渠道之间互相抢功劳数据打架整体营销 ROI 算不清楚

1. 解决的问题

传统多触点归因(Multi-Touch Attribution, MTA)依赖用户级点击路径数据:追踪每个用户从广告曝光→点击→转化的完整旅程,才能判断各渠道贡献。

2. 核心算法逻辑

传统 MTA 在隐私时代的失效

3. 业务应用场景

业务背景: 母婴 DTC 品牌在欧洲市场同时投放 Google、Meta、TikTok 三渠道。GDPR 合规要求下,无法使用用户级 cookie 追踪,传统 MTA 完全失效。

产出: - Google 直接归因权重:42%(ROAS 计算基准) - Meta 归因权重:35%(含 TikTok 引流的间接效应) - TikTok 归因权重:23%(较 Last-Click 低估已修正)

业务背景: 618 大促期间 Google/Meta/TikTok 三渠道同时加大投入,传统 Last-Click 把所有转化归给最后点击渠道(通常是品牌词搜索),严重低估 TikTok 的种草效应。

4. 输入数据要求

请查看原始代码模板获取输入规格。

5. 输出结果

请查看原始代码模板获取输出规格。

6. 业务价值 / ROI

未自动抽取;请查看原始 Skill 卡片。

7. 代码模板

代码块数量:3 · 路径:未检测到

"""
CDA — 因果驱动归因(隐私保护多渠道归因)
论文:Causal-driven attribution (CDA): Estimating channel influence without user-level data
arXiv:2512.21211 | 2024年12月
"""

from __future__ import annotations
from dataclasses import dataclass, field
from typing import Optional
import math
import statistics
from collections import defaultdict


# ──────────────────────────────────────────────
# 数据类
# ──────────────────────────────────────────────

@dataclass
class ChannelTimeSeries:
    """单渠道时序数据"""
    channel_name: str
    daily_impressions: list[float]  # 每日曝光量
    daily_conversions: list[float]  # 每日转化量(汇总,非用户级)

    def __post_init__(self):
        assert len(self.daily_impressions) == len(self.daily_conversions), \
            "曝光量和转化量时序长度必须一致"

    @property
    def n_days(self) -> int:
        return len(self.daily_impressions)

    def impression_rate(self) -> list[float]:
        """归一化曝光率"""
        max_imp = max(self.daily_impressions) or 1.0
        return [x / max_imp for x in self.daily_impressions]


@dataclass
class CausalDAG:
    """因果有向无环图"""
    channels: list[str]
    edges: list[tuple[str, str, int]]  # (from_channel, to_channel, lag_days)
    edge_weights: dict[tuple[str, str], float] = field(default_factory=dict)

    def get_parents(self, channel: str) -> list[tuple[str, int]]:
        """获取某渠道的所有父节点(直接因果来源)"""
        return [(src, lag) for src, dst, lag in self.edges if dst == channel]

    def get_children(self, channel: str) -> list[tuple[str, int]]:
        """获取某渠道的所有子节点(直接因果影响)"""
        return [(dst, lag) for src, dst, lag in self.edges if src == channel]


# ──────────────────────────────────────────────
# 简化版 PCMCI 因果发现
# ──────────────────────────────────────────────

class PCMCICausalDiscovery:

8. 论文来源

  • 2512.21211