Triggering 의 Open Questions — Triggering Unit · Time-series Plotting

Whole Session vs Triggered Activity · Decreasing Treatment Effect 의 false trend

Kohavi (2020) Ch.20.10 의 미해결 문제를 깊게 다룬다. Triggering unit 의 결정 (triggered activity 만 vs whole session vs whole day vs experiment start), computational vs statistical 의 trade- off, time-series plotting 의 false trend (decreasing Treatment effect 의 inevitable 패턴), Chen, Liu and Xu (2019) 의 권고 패턴을 정리한다.

Experimentation
A/B Test
저자

Kwangmin Kim

공개

2026년 05월 08일

1 정의

정의: Triggering 의 2 가지 Open Question

Kohavi (2020) Ch.20.10 가 명시한 미해결 문제. 산업 표준의 정답이 없는 영역.

1.0.0.1 Question 1 — Triggering Unit
사용자 가 trigger 된 후, 어느 activity 부터 분석에 포함?

Option 1: trigger 시점 이후의 activity 만
Option 2: 전체 session
Option 3: 전체 day
Option 4: 실험 시작부터의 모든 activity
1.0.0.2 Question 2 — Plotting Metrics over Time
Triggered metric 의 time-series plot:
  - 가짜 trend 의 자연 발생
  - Decreasing Treatment effect over time pattern
  - 단일-day 와 cross-day 의 mismatch

원문 인용 (Ch.20.10): “The following are issues that we face where we have no clear answer. Awareness is important, even when we have pros and cons and not the ‘answer.’”

핵심 통찰: 두 question 모두 trade-off 가 명확. Option 별 장단. Awareness 가 가장 중요. 표준 은 없지만 잘못된 선택의 결과를 알면 informed decision 가능.

2 개념 및 원리

2.1 Question 1 — Triggering Unit

저자 명시 (Ch.20.10).

2.1.1 4 가지 Option

2.1.1.1 Option 1 — Trigger 후 의 activity 만
Approach:
  사용자 가 trigger 시점 t_T
  Analysis: t > t_T 의 activity 만

Reasoning:
  Trigger 전 의 data 는 Treatment 영향 안 받음
  → Pure Treatment effect 측정

Implementation:
  사용자 별 trigger timestamp 추적
  분석 시 t > t_T 만 filter
2.1.1.2 함정 — Partial Sessions

저자 강조: “those triggered sessions are now partial, and their metrics are abnormal (e.g., the metric clicks prior to checkout are zero).”

시나리오:
  사용자 Alice 의 trigger:
    Session 시작 t=0
    Trigger event t=120s (checkout 시작)
    Session 끝 t=300s

  Trigger 후 activity:
    t=120~300s 만
    Total session: 180s 의 후반

  Metric 의 abnormality:
    Pre-checkout clicks: 0 (사실 trigger 후 의 사용자 가 이미 search 다 했음)
    Page views: 매우 적음
    Browse time: 0

  분석:
    Per-user metric 이 abnormal
    Triggered user 의 baseline 이 wrong
    Variance 큼
2.1.1.3 Statistical Power 의 손실
Partial session 의 effect:
  - Pre-trigger activity 무시
  - Per-user 의 활동 부족
  - Metric 의 mean 이 small
  - Variance 큼

  Sensitivity:
    Smaller effect detect 어려움
    Sample size 더 큰 필요
2.1.1.4 Option 2 — 전체 Session
Approach:
  사용자 가 trigger 한 session 의 모든 activity
  Trigger 전후 모두 포함

Reasoning:
  Session 의 일관성
  사용자 의 전체 행동 측정
  Pre-trigger 도 trigger 의 motivation 의 일부

함정:
  Pre-trigger activity 가 Treatment effect 와 무관
  Variance ↑ (irrelevant noise 추가)
2.1.1.5 Option 3 — 전체 Day
Approach:
  사용자 가 trigger 한 day 의 모든 activity
  Multi-session 포함

Reasoning:
  Day 의 자연 단위
  사용자 의 daily behavior 의 capture
  Cross-session effect (예: trigger 후 다시 visit)

함정:
  Day 의 boundary 가 arbitrary (timezone)
  사용자 의 trigger 와 day boundary 의 mismatch
  분석 의 noise
2.1.1.6 Option 4 — 실험 시작부터의 모든 activity

저자 권고 (computational simplicity): “Computationally, it is easier to take the user, including data from the beginning of the experiment, if they trigger at any point; however, this causes a small loss of statistical power.”

Approach:
  사용자 가 실험 기간 동안 한 번이라도 trigger 시:
    그 사용자 의 모든 activity (실험 시작부터) 분석에 포함

Reasoning:
  Computationally simple:
    - Per-user trigger flag 만 필요
    - Activity 전체 sum
    - 단순 query

  Lifetime tracking 의 mental model:
    - Trigger 한 사용자 = "affected"
    - 전체 사용자 행동 분석

함정:
  Pre-trigger activity 는 Treatment 영향 안 받음
  포함 시 variance ↑ (small statistical power loss)

2.1.2 4 Option 의 비교

Statistical Power:
  Option 1 (trigger 후 만): potentially highest (cleanest), but partial sessions 의 noise
  Option 2 (전체 session): medium
  Option 3 (전체 day): lower
  Option 4 (실험 시작부터): lowest (가장 많은 irrelevant)

Computational Cost:
  Option 1: 높음 (per-event filtering)
  Option 2: 중간 (session boundary detect)
  Option 3: 중간 (day aggregation)
  Option 4: 낮음 (single trigger flag)

Implementation 복잡성:
  Option 1: 매우 복잡
  Option 2~3: 중간
  Option 4: 매우 단순

2.1.3 산업 표준

대부분 platform: Option 4
  Why:
    - Computational efficiency 우선
    - Sensitivity loss 가 marginal (대부분 case)
    - Implementation 단순

예외:
  - Per-event critical metric (page latency)
  - 매우 niche feature (trigger 후 effect 만 critical)
  - 이 경우 Option 1 또는 2 사용
2.1.3.1 Option 4 의 sensitivity loss 의 정량
사용자 Alice 의 모든 activity:
  Pre-trigger: 100 events (Treatment 영향 0)
  Post-trigger: 50 events (Treatment 영향 가능)

Total events: 150
Treatment effect 의 dilution:
  Effect 가 50/150 = 33% 의 events 에 집중
  → Effect dilute factor 1/3

Sample variance:
  Pre-trigger 의 variability 가 sample 에 추가
  But Treatment effect 자체는 50 events 에만

Net effect:
  Sensitivity 손실 ~30% (Option 1 대비)
  But computational savings 큼
  Trade-off 합리적
직관 — 4 Option 의 mental model
2.1.3.2 Time axis 의 분해
사용자 Alice 의 timeline:
  t_0 = 실험 시작
  t_1 = Alice 의 first visit
  t_2 = Alice 의 trigger event (예: checkout 시작)
  t_3 = Alice 의 trigger session 끝
  t_4 = Alice 의 trigger day 끝
  t_5 = 실험 끝

각 option 의 분석 범위:
  Option 1: t_2 ~ t_5 (trigger 후 만)
  Option 2: session boundary ~ session boundary 의 trigger session
  Option 3: day boundary ~ day boundary 의 trigger day + 모든 future activity
  Option 4: t_1 ~ t_5 (Alice 의 모든 activity)
2.1.3.3 Trade-off 의 graph
Option 1 ←─ pure ─────────────── inclusive ─→ Option 4
              (Statistical purity)        (Computational simplicity)

Trade-off:
  Pure 쪽: sensitivity 의 theoretical maximum, but partial sessions
  Inclusive 쪽: 단순, but small dilution
2.1.3.4 실무 선택 framework
선택 기준:
  Option 1:
    - Per-event critical metric
    - Partial session OK
    - Niche analysis

  Option 2:
    - Session-level metric (session duration, etc.)
    - Whole session 의 의미

  Option 3:
    - Daily metric (DAU, daily revenue)
    - Day boundary 의 명확성

  Option 4:
    - Computational efficiency 우선
    - User-level metric (LTV, retention)
    - Lifetime tracking
    - 산업 표준

이 framework 이 결정의 input.

2.1.4 Implementation Detail — Sticky Flag

Standard implementation:
  사용자 의 trigger flag (sticky):
    Initial: False
    First trigger event: True
    이후: True 유지

  분석:
    Trigger flag = True 사용자
    그 사용자 의 모든 events 분석

  Storage:
    Per-user 의 single boolean
    Compact

  Query:
    SELECT user.* FROM events
    WHERE user_id IN (SELECT user_id FROM triggers)

이 sticky flag 가 Option 4 의 운영 표준.

2.2 Question 2 — Plotting Metrics over Time

저자 명시 (Ch.20.10).

2.2.1 일반 plotting 의 함정

저자 인용 (Kohavi et al. 2012, Chen, Liu and Xu 2019): “Plotting a metric over time with increasing numbers of users usually leads to false trends.”

2.2.1.1 시나리오 — Cumulative
Standard (잘못된) plot:
  X-axis: day
  Y-axis: cumulative metric (실험 시작부터 누적)

  Day 1: metric 의 day 1 만
  Day 2: metric 의 day 1 + 2
  Day 3: metric 의 day 1 + 2 + 3
  ...

False trend:
  Cumulative metric 의 평균이 점진 변화
  - 사용자 수 증가
  - Heavy user 의 추가
  - Fresh data 의 비중 변화
  → "Trend" 처럼 보이지만 noise

해결:
  Daily plot:
    X-axis: day
    Y-axis: that day 의 metric

  Each day 의 사용자 가 visit 한 metric 만
  Cumulative 무시
2.2.1.2 Daily plot 의 issue

저자 강조: “It’s best to look at graphs over time where each day shows the user who visited that day.”

Daily plot:
  Day 1 의 metric: Day 1 의 사용자 만
  Day 2 의 metric: Day 2 의 사용자 만
  ...

  각 day 가 independent
  Trend 의 noise 회피

2.2.2 Triggered Metric 의 추가 함정

저자 강조: “When users are triggered, we have the same problem: 100% of users on the first day were triggered, but a smaller portion of users on day 2 were triggered, as some triggered on day 1 but just visited on day 2.”

2.2.2.1 시나리오
Day 1:
  100 users visit
  10 users trigger (10% trigger rate)
  Triggered analysis: 10 users 의 metric

Day 2:
  100 users visit
  20 users 가 트리거 ever (Day 1 의 10 + Day 2 의 새 10)
  But: Day 1 의 trigger 한 사용자 의 일부만 Day 2 visit (예: 5)
  Triggered visit on Day 2:
    10 (Day 2 의 새 trigger) + 5 (Day 1 의 returning) = 15

  Triggered analysis 의 day 2 metric:
    15 users (mixed: new triggers + returning)
    Average effect? unclear
2.2.2.2 False Trend 의 메커니즘
Triggered effect 의 시간 변화:
  Day 1: 100% 사용자 가 fresh trigger
    - First exposure 의 novelty effect
    - Effect: +10%

  Day 2: mix
    - 67% returning triggered (Day 1 의 trigger 한 사용자)
    - 33% new triggered
    - Returning 의 attenuated effect (novelty 사라짐)
    - Effect: +7%

  Day 3:
    - 더 많은 returning
    - Average effect: +5%

  Day 7:
    - 거의 모든 사용자 가 returning
    - Average effect: +3% (long-term effect 만)

False trend:
  "Treatment effect 가 decreasing over time"
  → 사용자 가 wear-out? 또는 단지 sample composition 변화?

이 trend 가 일반적이지만 false interpretation.

2.2.3 Chen, Liu, Xu (2019) 의 권고

저자 인용: “Perhaps it’s better to plot each day with the users who visited that day and triggered on that day.”

2.2.3.1 Approach
Plot:
  X-axis: day
  Y-axis: that day's triggered users 의 metric

  Each day 의 사용자:
    - That day visit
    - That day triggered (다른 day 가 아닌)

각 day 의 fresh sample 만
Cross-day contamination 제거
2.2.3.2 한계

저자 강조: “The key issue is that the overall Treatment effect must include all days, so the single-day and overall (or cross-day) numbers do not match.”

Plot 의 limit:
  Day 별 fresh trigger 만 사용
  But overall analysis 는 모든 sample 포함:
    - Cross-day returning user
    - Lifetime tracking

Mismatch:
  Daily plot 의 effect: +X%
  Overall analysis 의 effect: +Y%
  X ≠ Y (different sample composition)

Reason:
  Daily plot: pure first-exposure effect
  Overall: lifetime average effect (novelty + wear-out)

이 mismatch 가 reporting 의 confusion 원인.

2.2.3.3 Best Practice
Recommended:
  - Daily plot (fresh trigger 만): trend visualization
  - Overall analysis: launch decision

  Both 표시:
    Daily: "trend pattern"
    Overall: "summary number"

  Decision:
    Overall metric 으로 launch 결정
    Daily plot 으로 trend 의 understanding

이 dual presentation 이 산업 표준.

가정 — Daily Trend 의 misinterpretation

가정: 분석가 가 daily plot 의 decreasing trend 를 보고 “Treatment effect wearing out” 결론.

2.2.3.4 시나리오
Daily plot:
  Day 1: +10% lift
  Day 7: +5% lift
  Day 14: +3% lift
  Day 21: +2% lift

분석가 conclusion:
  "Treatment effect wearing out over time"
  "Long-term lift = 2%"

Decision:
  "Long-term effect 가 launch ROI 에 부족"
  Reject

Mistake:
  Daily plot 의 sample composition 의 자연 변화
  Treatment 의 진정 wear-out 가 아닌 (또는 일부 만)
  Cross-day returning user 의 attenuated effect
2.2.3.5 Real Investigation
Daily plot 의 분해:
  Day 7 의 triggered = 첫 trigger 사용자 + Day 1 trigger 사용자 의 returning

  첫 trigger (Day 7):
    Effect: +10% (novelty intact)

  Day 1 trigger 의 returning:
    Effect: +3% (attenuated)

  Mix 의 average:
    Day 7 의 fresh: 30%
    Day 7 의 returning: 70%
    Average: 0.30 × 10% + 0.70 × 3% = 5.1%

→ Daily 의 +5% 는 mix 의 결과
→ True wear-out: +10% (첫 exposure) → +3% (long-term)
2.2.3.6 Correct Decision
Two effects 의 분리:
  Initial (novelty): +10%
  Long-term: +3%

Decision:
  Long-term +3% 가 sustainability 측정의 truth
  Holdout 의 결과 (Ch.15) 와 일치

만약 +3% 가 launch ROI 에 충분:
  Launch decision

만약 +3% 가 부족:
  Investigate novelty effect
    - Real value or trick?
    - Generalize possible?
  또는 reject

이 분석이 진정 mature platform 의 decision quality.

2.3 Multi-iteration Triggered 분석

2.3.0.1 시나리오
실험: 새 feature
Trigger: feature 사용

Iteration 1:
  Trigger rate: 5%
  Triggered effect: +20%
  Diluted: +1%

Iteration 2 (improved version):
  Trigger rate: 8%
  Triggered effect: +15%
  Diluted: +1.2%

Iteration 3 (final):
  Trigger rate: 12%
  Triggered effect: +12%
  Diluted: +1.4%
2.3.0.2 Pattern
Each iteration:
  Triggered effect 의 ↓ (effect 의 attenuation)
  Trigger rate 의 ↑ (더 많은 사용자 노출)
  Diluted impact 의 일부 ↑

Decision:
  Final iteration 의 +1.4% 가 launch ROI?
  Generalization 의 path 보임
  Future iteration 가능?

이 multi-iteration 의 pattern 이 generalization 의 typical path.

3 왜 필요한가

2 Open Question 의 awareness 부재 시.

  • Triggering unit 의 잘못된 선택: Sensitivity 손실 또는 computational cost
  • Time-series misinterpretation: False trend 의 잘못된 결정

활성 시.

  • 합리적 trigger unit: Trade-off 의 명시
  • Time-series 의 정확한 해석: Daily vs overall 의 분리

이 awareness 가 advanced triggering 의 mature 단계.

4 응용 사례 — Chen, Liu, Xu (2019) 의 LinkedIn 사례

LinkedIn 의 메타분석 (Chen, Liu, Xu 2019):

Cross-experiment pattern:
  Day 1: 강한 effect
  Day 7: 약한 effect
  Day 30: 매우 약한 effect

이 pattern 이 universal:
  Sample composition 의 변화
  Returning user 의 attenuated effect

Recommendation:
  Daily plot 의 trend 를 wear-out 으로 해석 X
  Sample composition 의 이해 필요

Best practice:
  Each day 의 fresh trigger 만 plot
  Overall lifetime metric 으로 decision

이 메타분석 의 lesson 이 산업 표준.

5 Ch.20 시리즈 마무리

6 편 완료:

  1. F20-0 — Triggering 의 본질, 5 사례, trustworthy, pitfalls 의 지도
  2. F20-1 — Examples 1~3 (Partial, Conditional, Coverage Increase)
  3. F20-2 — Examples 4~5 (Coverage Change, ML Counterfactual)
  4. F20-3 — Numerical example, optimal/conservative, diluted impact
  5. F20-4 — Trustworthy (SRM, complement), 3 pitfalls
  6. F20-5 — Open questions (triggering unit, time-series)

다음: Ch.21 (SRM, 4 편).

6 코드 예시 — Triggered Metric 의 Time-series Plot

False trend vs corrected plot.

import numpy as np
import pandas as pd
from datetime import timedelta

rng = np.random.default_rng(42)

# 시뮬레이션 setup
n_days = 21
n_users = 5000

# 사용자 의 daily visit 패턴
daily_visit_prob = rng.uniform(0.1, 0.8, n_users)  # heterogeneous

# Trigger probability per visit (10% chance per visit)
trigger_per_visit = 0.10

# Treatment assignment
treatment = rng.choice([0, 1], n_users, p=[0.5, 0.5])

# Treatment effect 의 lifecycle
# - Initial (first day after trigger): +10%
# - Long-term (after day 7): +3%

# 각 user 의 trigger day 추적
user_trigger_day = np.full(n_users, -1, dtype=int)

# Daily activity 시뮬레이션
daily_data = []

for day in range(n_days):
    for user in range(n_users):
        if rng.uniform(0, 1) < daily_visit_prob[user]:
            # Visit
            if user_trigger_day[user] == -1:
                # 아직 trigger 안 함, trigger 시도
                if rng.uniform(0, 1) < trigger_per_visit:
                    user_trigger_day[user] = day

            triggered = user_trigger_day[user] != -1
            days_since_trigger = day - user_trigger_day[user] if triggered else 0

            # Treatment effect (depending on time since trigger)
            if treatment[user] == 1 and triggered:
                if days_since_trigger <= 1:
                    effect_factor = 1.10  # +10% initial
                elif days_since_trigger <= 7:
                    effect_factor = 1.06  # +6% middle
                else:
                    effect_factor = 1.03  # +3% long-term
            else:
                effect_factor = 1.0

            base_metric = rng.normal(50, 15)
            metric = base_metric * effect_factor

            daily_data.append({
                "day": day,
                "user": user,
                "treatment": treatment[user],
                "metric": metric,
                "triggered": triggered,
                "first_trigger_day": user_trigger_day[user] if triggered else -1,
            })

df = pd.DataFrame(daily_data)

# === Plot 1: Naive cumulative ===
print("=== Naive Cumulative Plot (잘못) ===")
print(f"{'Day':>4} {'Cumulative T mean':>20} {'Cumulative C mean':>20} {'Cum lift':>10}")
for day in range(0, n_days, 3):
    cum_data = df[df["day"] <= day]
    triggered = cum_data[cum_data["triggered"]]
    t_mean = triggered[triggered["treatment"] == 1]["metric"].mean()
    c_mean = triggered[triggered["treatment"] == 0]["metric"].mean()
    if not np.isnan(t_mean) and not np.isnan(c_mean):
        lift = (t_mean - c_mean) / c_mean * 100
        print(f"{day:>4} {t_mean:>20.2f} {c_mean:>20.2f} {lift:>9.2f}%")

# === Plot 2: Daily (mixed sample) ===
print("\n=== Daily Plot (mix of fresh + returning triggered) ===")
print(f"{'Day':>4} {'Daily T mean':>15} {'Daily C mean':>15} {'Daily lift':>12}")
for day in range(0, n_days, 3):
    day_data = df[df["day"] == day]
    triggered = day_data[day_data["triggered"]]
    t_mean = triggered[triggered["treatment"] == 1]["metric"].mean()
    c_mean = triggered[triggered["treatment"] == 0]["metric"].mean()
    if not np.isnan(t_mean) and not np.isnan(c_mean):
        lift = (t_mean - c_mean) / c_mean * 100
        print(f"{day:>4} {t_mean:>15.2f} {c_mean:>15.2f} {lift:>11.2f}%")

# === Plot 3: Daily fresh trigger only ===
print("\n=== Daily Plot (only fresh trigger 사용자) ===")
print(f"{'Day':>4} {'Fresh T mean':>15} {'Fresh C mean':>15} {'Fresh lift':>12}")
for day in range(0, n_days, 3):
    day_data = df[df["day"] == day]
    fresh_trigger = day_data[day_data["first_trigger_day"] == day]
    t_mean = fresh_trigger[fresh_trigger["treatment"] == 1]["metric"].mean()
    c_mean = fresh_trigger[fresh_trigger["treatment"] == 0]["metric"].mean()
    if not np.isnan(t_mean) and not np.isnan(c_mean):
        lift = (t_mean - c_mean) / c_mean * 100
        print(f"{day:>4} {t_mean:>15.2f} {c_mean:>15.2f} {lift:>11.2f}%")

# Overall analysis
print("\n=== Overall Lifetime Analysis ===")
all_triggered = df[df["triggered"]]
t_overall = all_triggered[all_triggered["treatment"] == 1]["metric"].mean()
c_overall = all_triggered[all_triggered["treatment"] == 0]["metric"].mean()
overall_lift = (t_overall - c_overall) / c_overall * 100
print(f"T overall: {t_overall:.2f}, C overall: {c_overall:.2f}")
print(f"Overall lift: {overall_lift:.2f}%")
직관 — 시뮬레이션 의 메시지

이 코드의 메시지:

6.0.0.1 Naive Cumulative
Day 별 cumulative lift:
  Day 0: ~+10% (only first day)
  Day 7: ~+7% (mix)
  Day 14: ~+5%
  Day 21: ~+4%

Pattern: decreasing
6.0.0.2 Daily Plot (mix)
Day 별 daily lift:
  Day 0: ~+10%
  Day 7: ~+6% (mix of fresh + returning)
  Day 14: ~+5%
  Day 21: ~+4%

Pattern: similar decreasing
But each day independent
6.0.0.3 Daily Fresh Trigger
Day 별 fresh trigger lift:
  Day 0: ~+10% (all fresh)
  Day 7: ~+10% (still fresh on day 7)
  Day 14: ~+10%
  Day 21: ~+10%

Pattern: stable!
True initial effect
6.0.0.4 통합 메시지
Cumulative 또는 daily mix:
  False trend (decreasing)
  Sample composition 의 변화

Fresh trigger only:
  True initial effect (stable)
  Pure first-exposure 측정

Overall lifetime:
  Mix 의 lifetime average
  Launch decision 의 input

→ 3 metric 모두 다른 question 에 답
→ Awareness 의 가치

7 관련 주제

선행

Ch.20 시리즈 마무리 — 6 편 완료. 다음 Ch.21 (SRM).

관련 챕터

다른 카테고리 연결

Subscribe

Enjoy this blog? Get notified of new posts by email: