語言 / Language

台灣綠色和平 — 月捐/混合捐者 TFR 名單分群與捐款傾向Greenpeace Taiwan — TFR List Segmentation & Donation Propensity for Regular / Mixed Giving Donors

對象
募款決策者/業務負責人(先看第 1「一分鐘看懂」與第 2「需要決策的事項」即可)。
Audience
Fundraising decision-makers / business owners (read §1 BLUF and §2 Decisions first).
分析母體
台灣、捐款狀態(Giving Status)= Active/Reactivated/New/At Risk、捐款類型(Giving Type)= 月捐(Regular)或混合捐(Mixed)、價值層(Value Tier)= 一般/中額潛力/中額 —— 共 51,306 人。單筆捐(Single)3,268 人因性質不同,另列第 8 節。
Cohort
Taiwan; Giving Status = Active / Reactivated / New / At Risk; Giving Type = Regular or Mixed Giving Donor; Value Tier = General / Middle Donor Prospect / Middle Donor — 51,306 people. Single Giving Donors (3,268) differ in nature and are analysed separately in §8.
資料區間
通話結果=近 5 年(2021-06-17 → 2026-06-17);活動經歷=近 10 年(2016 → 2026)。擷取於 2026-06-17。
Windows
Call outcomes = last 5 years (2021-06-17 → 2026-06-17); campaign history = last 10 years (2016 → 2026). Extracted 2026-06-17.
資料來源
Salesforce 正式環境 00D2v000001f7liEAACase 物件(TFR 通話紀錄);LTV 取自 BigQuery donor_deveopment_performance。分類定義:Obsidian wiki donation-flow.md
Sources
Salesforce production 00D2v000001f7liEAA, Case object (TFR call records); LTV from BigQuery donor_deveopment_performance. Classification defs: Obsidian wiki donation-flow.md.
口徑
「Decline(婉拒)」採通話結果 TFR_Call_Outcome__c = No(四分類乾淨桶,與業務確認)。
Definition
"Decline" = call outcome TFR_Call_Outcome__c = No (clean four-bucket, confirmed with the BO).
已驗證 Verified 直接由資料計算的真實數字computed directly from source data 估算 Est. 基於假設的推算(假設已標明)inferred, assumption stated — not a measured value 混合 Mixed 同段落含實測與估算mixes measured + estimated
目錄Contents
  1. 一分鐘看懂(執行摘要)+ 母體界定BLUF (executive summary) + cohort definition
  2. 需要決策的事項(建議行動)Decisions needed (recommended actions)
  3. 營運 × 募款 雙視角洞察Operations × Fundraising dual-lens insights
  4. 名單分群:低接通 vs 可接通List segmentation: low-connect vs reachable
  5. 接觸現況 + 時段接通率Contact status + reachability by time of day
  6. 假設驗證Hypothesis validation
  7. 捐款傾向、金額與終身價值Propensity, amounts & lifetime value
  8. 單筆捐者單獨分析Single Giving Donors — separate analysis
  9. 資料盲點與限制Blind spots & limitations
  10. 資料驗證 · 方法論 · 來源Validation · methodology · sources

1. 一分鐘看懂(執行摘要)1. BLUF (one-minute executive summary)

一句話:對月捐/混合捐者而言,電話通路整體並未失效(接通率 54.5%),所以不該用「打不通比例」整批清名單 —— 「未接通」幾乎都是時段/可及性問題(號碼無效僅 0.4%),不是空號。真正能零誤殺、立即停撥的只有一小群「打很多次、從沒接通」的人;更大的機會在「久未聯繫名單」與「把量能往高價值層/高接受度活動傾斜」。

Bottom line: for Regular/Mixed donors the phone channel is not broadly dead (54.5% connect rate), so do not purge lists by "Not-Reached %" — "Not Reached" is almost entirely a timing/availability issue (invalid numbers only 0.4%), not dead lines. The only zero-false-removal group safe to stop immediately is the small set called many times yet never connected. The bigger upside is the "long-neglected" list and tilting capacity toward higher-value tiers and higher-acceptance campaigns.

  • 低接通名單(近 5 年未接通 > 70%)約 4,936–7,316 人(撥打 ≥5 通者的 14.1%),但此切點敏感、且接通難易與捐款行為脫鉤(最難接通的反而是 Active 狀態者)——不能直接清除。Low-connect list (Not-Reached > 70% over 5y): ~4,936–7,316 people (14.1% of those with ≥5 calls) — but the threshold is sensitive and reachability is decoupled from giving (the hardest to reach are Active donors) → do not purge wholesale.
  • 可立即停撥的硬子集:1,127 人近 5 年被打 ≥5 通卻一次都沒接通,合計 7,965 通空撥(零誤殺)。Zero-risk stop set: 1,127 people called ≥5 times with 0 connections — 7,965 wasted dials.
  • 假設一(>70% 打不通=電話失效):對整體 ❌ 推翻;只對特定 ~15% 子集成立。Hypothesis 1 (Not-Reached>70% = channel dead): ❌ refuted for the whole cohort; true only for a ~15% subset.
  • 假設二(活動類型影響結果):✅ 成立。接通後 Special Appeal 答應率 39.9% > Upgrade 29.2%(須用「接通後」基礎比)。Hypothesis 2 (campaign type matters): ✅ confirmed. Acceptance-when-reached: Special Appeal 39.9% > Upgrade 29.2% (use the reached-basis, not all-calls).
  • 傾向訊號清楚:接通後答應率 一般 46.1% → 中額潛力 55.4% → 中額 67.6%,且越高價值越好接通。Clear propensity gradient: acceptance-when-reached rises General 46.1% → Middle Prospect 55.4% → Middle Donor 67.6%, and higher tiers are easier to reach.
  • 金額面的反轉:Upgrade 雖單通答應率較低,但帶來「每月複利」的定期加碼 —— 年化約 +NT$21.5M,遠大於 Special Appeal 一次性 NT$6.56M;一次成功升級 LTV ≈ 第一年 NT$1,580 且持續。答應率高 ≠ 價值高。Value reversal: Upgrade has a lower single-call yes-rate but adds compounding monthly recurring revenue — ~+NT$21.5M annualized, far above Special Appeal's one-off NT$6.56M; one successful upgrade ≈ NT$1,580 first-year LTV and persists. Higher yes-rate ≠ higher value.
  • 免費的接通率槓桿:光看時段就有 17 個百分點落差(上午 9 點 51% → 傍晚 6 點 68%),而最會接通的傍晚 17–19 點目前撥打量最低 —— 把量能從上午移到傍晚+中午、優先週三至週五,不增加人力即可拉高接通率。Free reachability lever: time of day swings the connect rate 17 points (09:00 51% → 18:00 68%), yet the best window (17–19h) has the lowest call volume — shifting dials from morning to evening + noon, favouring Wed–Fri, raises connect rate with no extra headcount.
  • 久未聯繫名單:2,276 人近 5 年完全沒被電訪過 —— 但其中約 522 人(22.9%)已被勿擾設定排除,須先過濾。Long-neglected list: 2,276 people never called in 5y — but ~522 (22.9%) carry a do-not-contact flag and must stay excluded.
  • 單筆捐者(3,268 人)另列第 8 節:捐款傾向明顯較低(接通後答應率僅 28.0%)、過半從未被電訪,且其「Active 狀態」非源自定期定額,不宜與月捐者混為一談。Single Giving Donors (3,268) — see §8: markedly lower propensity (28.0% acceptance-when-reached), over half never called, and their "Active" status is not from an active recurring donation — do not blend with monthly donors.

分析母體界定(51,306 人是什麼)+ 為何把單筆捐分開Cohort definition (what the 51,306 are) + why Single donors are separated

主母體 51,306 人=依 GPEA 官方三欄捐款分類篩出(定義見第 10 節)。三欄含義:

The 51,306 main cohort is filtered by GPEA's official three-column classification (defs in §10):

分類欄位Classification field分組Group人數Count
捐款類型Giving Type月捐 RegularRegular35,814
混合捐 MixedMixed15,492
價值層Value Tier一般捐者General Donor36,592
中額潛力Middle Prospect12,758
中額捐者Middle Donor1,956
捐款狀態Giving StatusActive34,669
Reactivated9,698
At Risk4,982
New1,957

為何把單筆捐(3,268 人)分開? 經查證,這群人 100% 沒有生效中的定期定額(3,267/3,268)。他們之所以也標為 Active/New/Reactivated,是因為這些狀態對單筆捐者改以「單次捐款的時近度」判定(New 1,431、Active 1,340、Reactivated 497;無 At Risk)—— 與月捐者「定期定額仍在扣款」的 Active 是兩種不同的『活躍』。混在一起會把「最近捐過一次的人」誤當成「活躍月捐者」,故另列第 8 節。

Why separate the 3,268 Single donors? Verified: this group has essentially no active recurring donation (3,267/3,268). They are labelled Active/New/Reactivated because for single-gift donors those statuses are driven by single-gift recency (New 1,431, Active 1,340, Reactivated 497; no At Risk) — a different kind of "active" than a monthly donor's live recurring payment. Blending them would misread "someone who gave once recently" as an "active monthly donor", so they are reported separately in §8.

51,306
分析母體(月捐+混合捐)Cohort (Regular + Mixed) 已驗證
293,632
近 5 年電訪通數Calls, last 5y 已驗證
54.5%
接通率Connect rate (Yes+No+Call Back) 已驗證
1,127
空撥對象(≥5 通 0 接通)Wasted-dial set (≥5 calls, 0 connects) 已驗證
2,276
近 5 年從未被電訪Never called in 5y 已驗證
39.9%
Special Appeal 接通後答應率Special Appeal acceptance-when-reached 已驗證

2. 需要決策的事項(建議行動)2. Decisions needed (recommended actions)

依「先給答案、再給證據」原則前置。標 Placed first per BLUF. Items marked 估算 Est. 者含假設、非實測。 rest on an assumption, not a measurement.

#建議Recommendation優先Pri.
1先停撥 1,127 位空撥對象(≥5 通、0 接通,7,965 通)並轉多管道。唯一零誤殺、可立即執行。低接通名單整體不可直接清除 —— 須先做閾值敏感度+時段接通分析。Stop the 1,127 wasted-dial contacts (≥5 calls, 0 connects, 7,965 dials) and move to other channels. The only zero-risk, immediate action. Do not purge the whole low-connect list — run threshold-sensitivity + time-of-day analysis first.高 High
2把撥打時段移到傍晚+中午、優先週三至週五:傍晚 17–19 點接通率 59–68%(上午 9–10 點僅 51–53%)卻撥打量最低。不增加人力即可拉高接通率 —— 先 A/B 驗證再全面調整。Shift dialling to evening + noon, favour Wed–Fri: 17–19h connects at 59–68% (vs 51–53% at 09–10h) yet has the lowest volume. Lifts connect rate with no extra headcount — A/B test before full rollout.高 High
3處理 2,276 位「5 年未致電」名單:先扣除已被勿擾排除的約 522 人(22.9%),其餘約 1,750 位無勿擾旗標者列為優先觸及測試池 —— 零疲勞、零空號包袱的新機會。Work the 2,276 "never-called-in-5y" list: remove the ~522 (22.9%) with a do-not-contact flag; treat the remaining ~1,750 as a priority test pool — fresh, no fatigue or dead-number baggage.高 High
4量能向中額/中額潛力層+黃金時段傾斜:中額層接通後答應率 67.6%(一般層 1.47 倍 估算)、最好接通,且金額更大 —— 每次升級月加碼 $452、單筆均 $3,338,約一般層 2.5–3.4 倍。停撥/降頻規則須分層套用。Tilt capacity toward Middle / Middle-Prospect tiers + prime hours: Middle acceptance-when-reached 67.6% (1.47× General Est.), easiest to reach, and bigger amounts — monthly upgrade uplift $452, one-off avg $3,338, ~2.5–3.4× General. Apply stop/throttle rules by tier.高 High
5Active 狀態但電話難觸及者改走多管道(簡訊/Email/LINE),不是從名單移除 —— 接通難易只代表電話不通,不代表捐款不健康。Active-status but hard-to-reach donors → switch channels (SMS/Email/LINE), do not remove — low reachability means the phone isn't working, not that giving is unhealthy.中 Med
6升級邀請設每月配額上限+建立準實驗對照追蹤退捐率 —— 升級拒絕量大(約 4 萬通 估算),有關係侵蝕風險但缺對照無法定量。Cap monthly upgrade-ask quota + build a quasi-experimental control to track churn — upgrade declines are large (~40k calls Est.), a relationship-erosion risk that can't be quantified without a control.中 Med
7單筆捐者另策對待:3,268 人捐款傾向明顯較低(見第 8 節),電訪優先級應較低,或改以低成本管道(Email/自動化)邀請轉為月捐。Treat Single donors separately: 3,268 people, markedly lower propensity (§8) — lower phone priority, or convert-to-monthly via low-cost channels (Email/automation).中 Med
8建立傾向分數:以「價值層級 × 活動類型 × 個人接通/答應史」排序名單,取代平均撥打。Build a propensity score: rank lists by value tier × campaign type × personal connect/accept history, instead of uniform dialling.中 Med

3. 營運 × 募款 雙視角洞察3. Operations × Fundraising dual-lens insights

以兩種立場檢視同一份已驗證數據 —— 電訪營運(座席工時/名單衛生/撥打效率)與 募款策略(捐者價值/活動組合/傾向)—— 互相挑戰後留下的共識洞察。所有數字皆引用已驗證資料,推算一律標 估算 並寫明假設。立場取捨未一致處列於末段。(本節以月捐/混合捐主母體為準;單筆捐見第 8 節。)

The same verified data examined from two stances — Operations (agent hours / list hygiene / dial efficiency) and Fundraising strategy (donor value / campaign mix / propensity) — challenged against each other; the consensus insights remain. All figures cite verified data; any projection is marked Est. with its assumption. Unresolved trade-offs are listed at the end. (Main Regular/Mixed cohort; Single donors in §8.)

① 未接通是「接觸問題」不是空號① "Not Reached" is a contact problem, not a dead number 已驗證 Verified 營運×募款 Ops×FR

近五年 293,632 通中,未接通佔 38.8%(114,018 通),遠高於拒絕的 19.8%(58,179 通);接通率僅 54.5%。但號碼無效只佔 0.4%,兩者量級差近百倍。絕大多數未接通是時段/可及性問題,不是該清的空號。

Of 293,632 calls in 5y, Not Reached is 38.8% (114,018), far above Decline at 19.8% (58,179); connect rate is 54.5%. But invalid numbers are only 0.4% — nearly 100× smaller. The vast majority of "Not Reached" is a timing/availability issue, not a dead number to purge.

行動:禁止用「未接通比例」當清名單規則;先做時段/星期輪撥分析,把座席工時導向接得到人的時段。Action: Don't purge by Not-Reached %; run a time-of-day/day-of-week rotation analysis and steer agent hours to reachable windows.

② 唯一可零誤殺立即停撥的,是 1,127 人空撥子集② Only the 1,127 wasted-dial subset is safe to stop with zero false removals 估算 Est. 營運×募款 Ops×FR

以「未接通 >70%」定義的低接通名單有 7,316 人(已撥打的 14.9%),但這是自設切點、門檻一動數字就變。真正命中率確定為零的硬子集,是 1,127 位撥 ≥5 通、0 次接通者,累積 7,965 通空撥 —— 僅佔總撥打的 2.72%估算)。

The low-connect list (Not-Reached >70%) is 7,316 people (14.9% of those dialled), but that's a self-set threshold — move it and the count moves. The truly zero-hit hard subset is 1,127 people called ≥5 times with 0 connects — 7,965 wasted dials, just 2.72% of all dials (Est.).

行動:先對這 1,127 位設停撥/轉管道;低接通名單整體須先做閾值敏感度分析。Action: Set stop/divert rules for these 1,127 first; run threshold-sensitivity before touching the broader list.

③ 接通難易與捐款行為脫鉤 —— 最難接通的反而是 Active 狀態者③ Reachability is decoupled from giving — the hardest to reach are Active donors 已驗證 Verified 營運×募款 Ops×FR

依捐款狀態,低接通比例 Active 15.7%、Reactivated 10.6%、At Risk 9.9% —— 最該擔心流失的 At Risk 反而最容易接通,仍穩定捐的 Active 反而最難接通。證明「>70% 未接通」純是電話可達性指標,與捐款健康脫鉤。

By status, the low-connect share is Active 15.7%, Reactivated 10.6%, At Risk 9.9% — the at-risk donors are the easiest to reach, while steadily-giving Active donors are the hardest. This proves "Not-Reached >70%" is purely a reachability metric, decoupled from giving health.

行動:停撥規則必須交叉比對捐款狀態;對 Active 但難觸及者改走多管道。Action: Cross-check status before any stop rule; switch Active-but-unreachable donors to other channels.

④ 中價值層既好接通又高轉換,難觸及比例僅一般層四成④ Middle tier is both easier to reach and higher-converting — its low-connect rate is ~40% of General's 估算 Est. 營運×募款 Ops×FR

接通後答應率隨層上升:一般 46.1%、中額潛力 55.4%、中額 67.6%(中額約一般層 1.47 倍 估算)。難觸及比例:一般 15.1%、中額潛力 12.4%、中額 6.2%(中額僅一般層 41% 估算)。但無各層成交絕對量,只能定方向。

Acceptance-when-reached rises by tier: General 46.1%, Middle Prospect 55.4%, Middle 67.6% (Middle ~1.47× General Est.). Low-connect share: General 15.1%, Middle Prospect 12.4%, Middle 6.2% (Middle only 41% of General Est.). No per-tier absolute conversion counts, so direction only.

行動:黃金時段與資深專員優先給中價值層;停撥/降頻分層套用。Action: Give prime hours and senior agents to the Middle tiers; apply stop/throttle by tier.

⑤ 升級 vs 特別專案須用「接通後」基礎比;單通同意高 ≠ 終身價值高⑤ Compare Upgrade vs Special Appeal on a reached-basis; high single-call yes ≠ high lifetime value 混合 Mixed 營運×募款 Ops×FR

兩活動接通率不同(升級 49.9% vs 特別專案 46.7%),直接比全體同意率(14.6% vs 18.6%)會把接通難易度誤算進成效。固定在接通者基礎:特別專案接通後同意率 39.9%、升級 29.2%,差 10.7 個百分點。但這只是單通命中率:升級的代理存續年限均值 2.44 年(中位 3)約為特別專案 1.27 年(中位 1)近兩倍(代理指標)。金額面見第 7 節。

Connect rates differ (Upgrade 49.9% vs Special Appeal 46.7%), so comparing all-call yes-rates (14.6% vs 18.6%) bakes reachability into the result. On the reached-basis: Special Appeal accepts 39.9% vs Upgrade 29.2% — a 10.7-pt gap. But that's single-call hit rate only: the proxy "years experienced" averages Upgrade 2.44 (median 3) vs Special Appeal 1.27 (median 1) (Proxy). Money comparison in §7.

行動:比較只用接通後同意率;不得僅憑單通同意率把資源從升級移向特別專案。Action: Compare on acceptance-when-reached only; don't shift resource from Upgrade to Special Appeal on single-call yes alone.

⑥ 升級致電拒絕量是關係風險點,但缺留存對照無法定量⑥ Upgrade-ask declines are a relationship-risk point, but unquantifiable without a retention control 估算 Est. 營運×募款 Ops×FR

升級全體拒絕率 28.5% 高於特別專案 22.4%,且升級佔總致電約七成(143,024 通)。換算拒絕量約 40,762 通(估算),是其同意量近兩倍。反覆對同一捐款人發升級被拒,有侵蝕關係風險;但無退捐/流失欄位、也無未致電對照組。

Upgrade's all-call decline rate (28.5%) exceeds Special Appeal's (22.4%), and Upgrade is ~70% of all calls (143,024). That's ~40,762 declines (Est.), nearly 2× its accepts. Repeatedly asking the same donor to upgrade and being refused risks eroding the relationship — but there's no churn field and no never-called control.

行動:對低意願層設升級每月配額上限;建立準實驗對照追蹤退捐率後再決定加碼。Action: Cap monthly upgrade-ask quota for low-propensity tiers; build a quasi-experimental control on churn before scaling.

⑦ 撥打頻次右偏:平均 6 通掩蓋 1,823 人被撥 ≥11 通的疲勞長尾⑦ Right-skewed call frequency: a mean of 6 hides 1,823 people dialled ≥11 times (fatigue tail) 估算 Est. 營運 Ops

每位撥打中位與平均皆 6 通,但最大值 82、11–20 通有 1,806 人、≥21 通 17 人,合計 1,823 人被撥 ≥11 通(約被撥者 3.7% 估算)。疲勞與超撥風險集中此尾。但無按次數帶的同意產出,無法證明高頻撥打邊際同意已歸零。

Median and mean dials per donor are both 6, but the max is 82; 1,806 people got 11–20 calls and 17 got ≥21 — 1,823 dialled ≥11 times (~3.7% of those called Est.). Fatigue/over-dial risk concentrates in this tail. No accept-by-frequency-band data exists to prove marginal acceptance has hit zero.

行動:對長尾單獨稽核並設撥打次數上限(非全面降頻);補「次數帶 × 同意數」交叉表。Action: Audit the tail and set a per-donor dial cap (not a blanket throttle); build a calls-band × accepts crosstab.

⑧ 久未聯繫名單 2,276 人 —— 約四分之一已被勿擾排除,其餘才是可啟用機會⑧ The 2,276 never-called — ~a quarter are deliberately suppressed; the rest are the real opportunity 已驗證 Verified 營運×募款 Ops×FR

主母體 51,306 人中,2,276 人五年內完全沒被撥打過。但這不是乾淨的純機會名單:其中 522 人(22.9%)帶勿擾旗標(DoNotCall 483/拒絕聯絡 19/拒絕募款邀請 510;遠高於有撥打組的 6.8% 基準),須維持不撥。扣除後約 1,750 人是真正未觸及、可啟用對象。

Of the 51,306 cohort, 2,276 were never dialled in 5y. But it's not a clean opportunity list: 522 (22.9%) carry a suppression flag (DoNotCall 483 / Do-Not-Contact 19 / Fundraising Opt-Out 510 — far above the 6.8% baseline among those called) and must stay excluded. The remaining ~1,750 are genuinely untouched and activatable.

行動:先以勿擾旗標過濾,再把約 1,750 人列為優先觸及測試池;不可假設 2,276 全可啟用。Action: Filter by suppression flag first, then treat the ~1,750 as a priority test pool; don't assume all 2,276 are usable.
兩種立場未完全一致的取捨(誠實揭露)Unresolved trade-offs between the two stances (disclosed honestly)
  • 優先級:營運立場傾向先「停掉空撥、設次數上限」;募款立場認為「價值分層傾斜+啟用久未聯繫名單」才是最大槓桿,效率回收量級(2.72%)太小不該驅動主要策略。兩者同意並行,但「何者排第一」未完全一致。Priority: Operations would first "stop wasted dials, set a dial cap"; Fundraising argues "tilt to value tiers + activate the neglected list" is the bigger lever and the efficiency recovery (2.72%) is too small to drive strategy. Both agree to do both; they did not fully converge on what comes first.
  • 升級頻次是否主動調降:募款立場傾向先收緊(拒絕量大、關係風險);營運立場認為在沒有退捐/流失對照組前證據不足。兩者同意需補準實驗對照。Whether to proactively reduce upgrade frequency: Fundraising leans to tightening (large decline volume, relationship risk); Operations says the evidence is insufficient without a churn control. Both agree a quasi-experiment is needed first.

4. 名單分群:低接通 vs 可接通4. List segmentation: low-connect vs reachable 已驗證 Verified

本節數字由 Salesforce 匯出資料直接計算(月捐/混合捐母體)。低接通名單(對應 ticket 的 Cold List)= 近 5 年「Not Reached」佔比 > 70%;其餘為可接通名單(Warm List)。同列「全體被打過」與「撥打 ≥5 通(較穩健)」兩口徑。Computed directly from the Salesforce export (Regular/Mixed cohort). Low-connect list (the ticket's "Cold List") = Not-Reached share > 70% over 5y; the rest is the reachable "Warm List". Shown for "all dialled" and "≥5 calls (more robust)".

口徑Basis低接通Low-connect可接通Reachable合計Total低接通占比Low-connect %
全體近 5 年被電訪者All dialled in 5y7,31641,71449,03014.9%
撥打 ≥5 通者(較穩健)≥5 calls (robust)4,93630,16735,10314.1%
近 5 年從未被電訪Never dialled in 5y2,276(另成一類「久未聯繫」)2,276 (separate "long-neglected" class)4.4%

兩口徑都落在 ~14–15%:約每 7 位被電訪者有 1 位電話接通率過低。但此切點敏感、且接通難易與捐款脫鉤,不可直接清除(見第 3 節①②③)。Both bases land at ~14–15%: roughly 1 in 7 dialled donors has a too-low connect rate. But the threshold is sensitive and reachability is decoupled from giving — don't purge (see §3 ①②③).

低接通名單分布 — 依價值層級(≥5 通口徑)Low-connect by value tier (≥5-calls basis)

一般捐者General
15.1%
中額潛力Mid Prospect
12.4%
中額捐者Middle
6.2%

低接通以「一般捐者」最多(3,776 人 / 15.1%);越高價值越打得通(中額僅 6.2%)。Low-connect is concentrated in General donors (3,776 / 15.1%); higher value = easier to reach (Middle only 6.2%).

低接通名單分布 — 依捐款狀態(≥5 通口徑)Low-connect by Giving Status (≥5-calls basis)

Giving Status低接通Low-connect該狀態 ≥5 通人數≥5-calls in status低接通占比Low-connect %
Active3,79724,15915.7%
Reactivated8638,16710.6%
At Risk2732,7629.9%
New315

最難接通的是 Active、最容易接通的是 At Risk —— 印證「接通難易 ≠ 捐款健康」。Hardest to reach = Active; easiest = At Risk — confirming reachability ≠ giving health.

可直接使用的名單檔:exports/20260617-1812-tw-tfr-per-donor-list-v2.csv(每位捐款人一列,含 giving_type 可區分月捐/混合/單筆),欄位 list_segment(Cold=低接通/Warm=可接通/Never Called)、not_reached_pct、各結果次數、redundant_flag(空撥)、suppressed(勿擾)。可在 Excel/Looker 篩 not_reached_pct > 70redundant_flag = Y

Ready-to-use list file: exports/20260617-1812-tw-tfr-per-donor-list-v2.csv (one row per donor, with giving_type to separate Regular/Mixed/Single), columns list_segment (Cold/Warm/Never Called), not_reached_pct, per-outcome counts, redundant_flag (wasted-dial), suppressed (do-not-contact). Filter not_reached_pct > 70 or redundant_flag = Y in Excel/Looker.

5. 接觸現況回顧 + 時段接通率5. Contact status + reachability by time of day 已驗證 Verified

每位捐款人近 5 年被撥打次數分布Calls per donor over 5y

1–2 通1–2 calls
6,128
3–5 通3–5 calls
13,919
6–10 通6–10 calls
27,160
11–20 通11–20 calls
1,806
21 通以上21+ calls
17

中位 6 通/平均 6 通/最多 82 通。大宗落在 6–10 通;過度撥打(≥11 通)僅 1,823 人。另一端 6,128 人只被打 1–2 通+2,276 人完全沒被打 —— 「久未聯繫」比「過度撥打」更大。Median 6 / mean 6 / max 82. Bulk at 6–10; over-dialling (≥11) is only 1,823 people. At the other end, 6,128 got just 1–2 calls + 2,276 never called — "neglect" is a bigger problem than "over-dialling".

近 5 年整體通話結果分布(293,632 通)Overall call-outcome mix, 5y (293,632 calls)

Yes 27.0% No 19.8% Call Back 7.7% Not Reached 38.8% Other 6.7%
通話結果Outcome通數Calls%意義Meaning
Yes79,17327.0%當下接受 askaccepted the ask
No58,17919.8%有接通但拒絕(本次 Decline 口徑)reached but declined (our "Decline")
Call Back22,6217.7%有接通、請改期reached, asked to call back
Not Reached114,01838.8%沒人接/忙線/語音信箱(非空號)no answer / busy / voicemail (not a dead line)
Invalid / Other19,6416.7%號碼無效僅 0.4%(1,094 通)invalid numbers only 0.4% (1,094)

接通率(Yes+No+Call Back)= 54.5%。電話可達性相當健康 —— 假設一對整體不成立的關鍵證據。Connect rate (Yes+No+Call Back) = 54.5%. Reachability is healthy — the key evidence that Hypothesis 1 fails for the whole cohort.

什麼時段/星期打得通 —— 把「未接通」變成可行動的排班When calls connect — turning "Not Reached" into a scheduling lever 已驗證 Verified

用實際撥打時間 Last_Call_Date__c(轉台灣時間)計算各時段接通率。注意:不可用 CreatedDate —— 經查它混入批次匯入(00:00/08:00 有人工尖峰),非真實撥打時間。下圖為撥打量 ≥2,000 的時段。Connect rate by hour from actual call time Last_Call_Date__c (Taiwan time). Note: CreatedDate is unusable — it is contaminated by batch imports (artificial 00:00/08:00 spikes), not real call time. Hours with ≥2,000 dials shown.

09:00(最差)09:00 (worst)
51.1%
10:00
52.5%
11:00
54.7%
12:00(午休)12:00 (lunch)
62.2%
14:00
54.9%
15:00
53.8%
16:00
56.0%
17:00
59.4%
18:00(最佳)18:00 (best)
68.4%
19:00
63.8%
星期Weekday接通率Connect %撥打量Volume
週三 / 週四Wed / Thu57.4% / 57.3%54,741 / 54,343
週五Fri56.3%52,887
週一 / 週二Mon / Tue53.7% / 54.1%56,935 / 59,312
週六(量少)Sat (low vol)61.2%5,115
週日(最差)Sun (worst)52.6%3,579

關鍵:光靠時段就有 17 個百分點的接通率落差(09:00 的 51% → 18:00 的 68%),而最會接通的傍晚 17–19 點目前撥打量最低(約 1 萬 vs 中午時段 3.5 萬)。把更多量能從上午 9–10 點移到傍晚 17–19 點+中午 12 點、優先週三至週五,可在不增加人力下拉高接通率。限制:傍晚/週六量少可能有選擇偏誤,且須遵守電訪時段法規 —— 建議小規模 A/B 驗證後再全面調整。

Key: time of day alone swings the connect rate 17 points (51% at 09:00 → 68% at 18:00), yet the best window (17–19h) currently has the lowest dial volume (~10k vs ~35k at midday). Moving capacity from 09–10h to 17–19h + 12:00, favouring Wed–Fri, raises the connect rate with no extra headcount. Caveats: low evening/Saturday volume may carry selection bias, and calling-hour regulations apply — A/B test before a full shift.

6. 假設驗證6. Hypothesis validation 已驗證 Verified

假設一:電話通路是否已失效?Hypothesis 1: is the phone channel dead?

假設原文:近 5 年 >70% 標記「Not Reached」的捐者,代表電話通路已失效,應移出 TFR、改數位/線下。
驗證結論:對「全體」 ❌ 推翻;對特定子集 ✅ 成立(但仍不可僅憑此切點清名單,見第 3 節①②)。

The hypothesis: donors with >70% "Not Reached" over 5y mean the phone channel is dead and should be removed from TFR and targeted via digital/offline.
Verdict: ❌ refuted for the whole cohort; ✅ true only for a specific subset (but still don't purge by this threshold alone — see §3 ①②).

假設二:Upgrade vs Special Appeal 的結果差異Hypothesis 2: Upgrade vs Special Appeal outcome differences

驗證結論:✅ 成立。兩類接通率接近(~47–50%),但接通後答應率差很多 —— Special Appeal 39.9% vs Upgrade 29.2%(1.37 倍)。活動類型可作預測特徵。務必用「接通後」基礎比,不可用全體率。

Verdict: ✅ confirmed. Connect rates are close (~47–50%), but acceptance-when-reached differs a lot — Special Appeal 39.9% vs Upgrade 29.2% (1.37×). Campaign type is a usable predictive feature. Always compare on the reached-basis, not all-calls.

指標MetricUpgradeSpecial Appeal
近 5 年通數Calls (5y)143,02459,334
Yes(占全部)Yes (of all)14.6%18.6%
No(占全部)No (of all)28.5%22.4%
Not Reached46.2%46.7%
接通率Connect rate49.9%46.7%
接通後答應率Acceptance-when-reached29.2%39.9%

「Upgrade」含 Annual Upgrade/New Donor Upgrade/Middle Donor Upgrade/Middle Donor Conversion(依 ticket 定義)。解讀:把人升級到更高月捐的阻力(婉拒 28.5%)比請他為特定議題加碼更大。但答應率高 ≠ 價值高(見第 7 節)。"Upgrade" covers Annual / New Donor / Middle Donor Upgrade / Middle Donor Conversion (per the ticket). Reading: resistance to raising a monthly gift (28.5% declines) is higher than asking for a one-off cause gift. But higher yes-rate ≠ higher value (see §7).

7. 捐款傾向、金額與終身價值7. Propensity, amounts & lifetime value 已驗證 Verified

以「接通後答應率(Yes ÷ 接通數)」當捐款傾向代理指標。兩面向都呈現可用於排序名單的梯度:

Using acceptance-when-reached (Yes ÷ reached) as a propensity proxy. Both cuts show a gradient usable for list ranking:

依價值層級(所有電訪類型)By value tier (all campaign types)

一般捐者General
46.1%
中額潛力Mid Prospect
55.4%
中額捐者Middle
67.6%

中額捐者接通後答應率 67.6%,是一般捐者 46.1% 的 1.47 倍 估算。電訪量能應優先配給中額/中額潛力層。Middle-tier acceptance-when-reached is 67.6%, 1.47× General's 46.1% Est. Prioritise call capacity to Middle / Middle-Prospect tiers.

依「實際募得金額」看 —— 答應之後真的進帳多少By actual amounts raised — what a "Yes" really brings in 混合 Mixed

兩種「Yes」對應兩種金額:Upgrade 的「Yes」=把原本的定期定額加碼,價值是每月增加金額(Change Amount,存於 Recurring Donation Item);Special Appeal 的「Yes」=新增一筆單次捐款。金額為新台幣。Two kinds of "Yes" map to two kinds of money: an Upgrade "Yes" raises the existing recurring gift — value = the monthly increase (Change Amount, on the Recurring Donation Item); a Special Appeal "Yes" creates a new one-off gift. Amounts in TWD.

覆蓋率限制:79,179 通「Yes」中,Upgrade 有 47% 連得到加碼紀錄、Special Appeal 有 32% 連得到單次捐款紀錄,其餘無金額連結。金額是「有連到紀錄」子集的下限,不是 TFR 總募得金額;「口頭答應」與「系統入帳」之間有缺口。

Coverage limit: of 79,179 "Yes" calls, 47% of Upgrades link to an uplift record and 32% of Special Appeals link to a one-off; the rest have no money link. Figures are a lower bound on the linked subset, not total TFR revenue — there is a gap between a verbal "Yes" and a recorded gift.

活動類型Campaign typeYes 通數Yes calls價值型態與金額(已連結子集)Value type & amount (linked subset)
Upgrade20,856定期加碼:9,793 筆(47%)、每月平均 +$183、月加碼合計 +$1.79M → 年化約 +$21.5M 新增定期收入Recurring uplift: 9,793 (47%), avg +$183/mo, +$1.79M/mo total → ~+$21.5M annualized new recurring revenue
Special Appeal11,064一次性現金:3,545 筆(32%)、平均 $1,851、合計 $6.56MOne-off cash: 3,545 (32%), avg $1,851, $6.56M total
其他(回流/維護)Other (reactivation/care)47,259幾乎無加碼或新單筆 —— 其「Yes」多為維持/續扣既有定期,非新增金額almost no uplift or new one-off — these "Yes" mostly sustain existing recurring gifts, not new money
價值層Value tierUpgrade 每月加碼均額Avg monthly upliftSpecial Appeal 單筆均額Avg one-off gift
一般捐者General$133$1,345
中額潛力Mid Prospect$221$1,983
中額捐者Middle$452$3,338

金額面的關鍵反轉:Upgrade 雖然接通後答應率較低(29.2% vs 39.9%),但帶來「每月複利」的定期加碼 —— 年化約 +$21.5M,遠大於 Special Appeal 的一次性 $6.56M。所以「答應率高」不等於「價值高」。Upgrade = 長期定期成長引擎;Special Appeal = 即時現金。

The value reversal: Upgrade has a lower acceptance-when-reached (29.2% vs 39.9%), but it delivers compounding monthly recurring uplift — ~+$21.5M annualized, far above Special Appeal's one-off $6.56M. So "higher yes-rate" does not mean "higher value". Upgrade = long-term recurring growth engine; Special Appeal = immediate cash.

金額也證實層級梯度更陡:中額捐者每次升級月加碼 $452,是一般捐者 $133 的 3.4 倍 估算;單筆均 $3,338,是一般層 2.5 倍。Amounts confirm a steeper tier gradient: Middle's monthly upgrade uplift $452 is 3.4× General's $133 Est.; one-off avg $3,338 is 2.5× General's.

終身價值(LTV)—— 一次成功的升級到底值多少Lifetime value — what one successful upgrade is worth 混合 Mixed

資料來源改用 BigQuery 倉儲表 donor_deveopment_performance(已含 TFR 結果 × RD 加碼金額 × 逐月留存 × 退捐)—— LTV 所需資料倉儲早已具備,不需重建。Source: BigQuery warehouse table donor_deveopment_performance (already has TFR outcome × RD uplift × monthly retention × cancellation) — the LTV data already exists in the warehouse; no rebuild needed.

升級後留存(第 N 個月仍在扣款)Post-upgrade retention (still paying at month N)M3M6M9M12
仍在扣款比例% still paying 已驗證94.4%93.2%90.0%86.9%

一次成功升級的價值 ≈ 第一年 NT$1,580(並延續到第 2 年以後)。算法:每月加碼約 $156(倉儲口徑;直接拉 Salesforce 為 $183,同量級)× 第一年實際扣款約 10.1 個月 = ~NT$1,580 第一年增量 估算。因 87% 升級者滿 12 個月仍在扣款,這筆加碼會持續進帳 —— 多年累積是第一年的數倍。對比 Special Appeal 一次性:平均每筆 NT$2,138、每位捐者約 1.23 筆 = ~NT$2,630/人(一次性、不複利)。

One successful upgrade ≈ NT$1,580 in year 1 (and continues into year 2+). Method: monthly uplift ~$156 (warehouse; Salesforce direct gives $183, same order) × ~10.1 actual paid months in year 1 = ~NT$1,580 first-year increment Est. Because 87% of upgraders are still paying at month 12, the uplift keeps accruing — the multi-year total is several times the first year. Versus a Special Appeal one-off: avg NT$2,138/gift × ~1.23 gifts/donor = ~NT$2,630/donor (one-off, no compounding).

結論:升級的「每月複利」價值約在第 2 年追過特別專案的一次性金額;3 年期一次升級的累積增量(粗估 NT$4,000+)明顯高於一筆特別專案。限制:第一年增量穩健,跨年 LTV 含留存外推假設(估算);倉儲電話留存欄位歷有資料缺口,已挑非缺口子集計算。Conclusion: the upgrade's compounding value overtakes the special-appeal one-off by ~year 2; over 3 years one upgrade (rough NT$4,000+ cumulative) clearly beats one special-appeal gift. Caveat: the first-year increment is robust; multi-year LTV carries a retention-extrapolation assumption (Est.); the warehouse telephone-retention field has historical gaps — a non-gap subset was used.

活動經歷(Donor Lifespan,近 10 年)Donor lifespan (campaigns experienced, 10y) 代理 Proxy

維度Dimension平均Mean中位Median
經歷過幾個「年度的 Upgrade 活動」Upgrade campaign-years experienced2.443
經歷過幾個「年度的 Special Appeal 活動」Special Appeal campaign-years experienced1.271

以「有被該類活動電訪的不同年份數」作為活動波次代理(多數活動名稱不帶年份/波次,故用年份計,非真實波數)。Uses "distinct years with a call of that campaign type" as a wave proxy (most campaign names lack year/wave granularity, so years are used — not true wave counts).

8. 單筆捐者(Single Giving Donor)單獨分析8. Single Giving Donors — separate analysis 已驗證 Verified

為何單獨看:這 3,268 位歷來只有單次捐款、且 100% 沒有生效中定期定額的捐款者,其 Active/New/Reactivated 狀態是依「單次捐款時近度」判定,與月捐者「定期定額仍在扣款」是兩種不同的『活躍』,混在一起會誤導。

Why separate: these 3,268 donors gave only single gifts historically and have essentially no active recurring donation; their Active/New/Reactivated status is set by single-gift recency, a different "active" than a monthly donor's live recurring payment — blending would mislead.

狀態成因(為何沒有月捐卻是 Active)Status causation (why "Active" without a monthly gift)

Giving Status人數Count對單筆捐者的意義Meaning for single donors
New1,431近期首次捐款的新單筆捐者(最多)recent first-time single donor (largest)
Active1,340近期有單次捐款、判為活躍recent single gift → flagged active
Reactivated497沉睡後又單次捐款回流lapsed then gave a single gift again
At Risk0—(屬定期定額降額情境,單筆不適用)— (a recurring-downgrade state; N/A to single)

有定期定額者僅 1/3,268。換言之,這群的「活躍」完全來自單次捐款的時近度,不是月捐。Only 1/3,268 has a recurring donation. Their "active" is entirely single-gift recency, not monthly giving.

單筆捐者的 TFR 接觸與成效(vs 月捐/混合捐)Single donors' TFR contact & outcomes (vs Regular/Mixed)

指標Metric單筆捐者Single月捐/混合(對照)Regular/Mixed (ref)
人數People3,26851,306
近 5 年曾被電訪比例% ever called in 5y47%96%
近 5 年從未被電訪Never called in 5y1,723 (53%)2,276 (4%)
近 5 年通數Calls (5y)3,222293,632
接通率Connect rate48.9%54.5%
接通後答應率Acceptance-when-reached28.0%49.5%
低接通占比(已撥打者)Low-connect % (of dialled)32.6%14.9%

建議:單筆捐者不宜與月捐者放同一電訪策略 —— 電訪優先級應較低;要轉化為月捐,用低成本管道(Email/自動化/一次性轉月捐 ask)測試更划算,把高成本真人電訪量能留給月捐/混合捐主母體與中額層。

Recommendation: don't put Single donors on the same phone strategy as monthly donors — lower phone priority; to convert them to monthly, test low-cost channels (Email/automation/one-tap convert-to-monthly), reserving costly live calls for the Regular/Mixed cohort and the Middle tier.

9. 資料盲點與限制9. Blind spots & limitations

10. 資料驗證 · 方法論 · 來源10. Validation · methodology · sources

分析母體定義(GPEA 官方三欄分類)Cohort definition (GPEA official three-column classification)
  • 主母體篩選(Salesforce Contact)Market__c=TaiwanGiving_Status__c∈{Active,Reactivated,New,At Risk}Giving_Type__c∈{Regular, Mixed Giving Donor}Value_Tier__c∈{General, Middle Donor Prospect, Middle Donor}。單筆捐=同條件但 Giving_Type__c='Single Giving Donor'Main cohort filter (Salesforce Contact): Market__c=TaiwanGiving_Status__c∈{Active,Reactivated,New,At Risk}Giving_Type__c∈{Regular, Mixed Giving Donor}Value_Tier__c∈{General, Middle Donor Prospect, Middle Donor}. Single = same but Giving_Type__c='Single Giving Donor'.
  • 官方定義來源:GPEA 2025 Classification,整理於 Obsidian wiki donation-flow.md。Giving Type=依捐款方式的歷史紀錄;Giving Status=生命週期(月捐/混合捐四種要求 RD 生效中,單筆捐依單次時近度);Value Tier=依金額分級。Definition source: GPEA 2025 Classification, in Obsidian wiki donation-flow.md. Giving Type = by historical giving mode; Giving Status = lifecycle (the four Regular/Mixed states require an active RD; for Single it's single-gift recency); Value Tier = by gift size.
資料來源與口徑定義Sources & definitions
  • TFR 通話物件=Salesforce Case(單一 Master record type),每通電話一筆。TFR call object = Salesforce Case (single Master record type), one row per call.
  • 通話結果TFR_Call_Outcome__c(Yes/No/Call Back/Not Reached/Invalid Data/Other)。活動類型Campaign_Name__c 字串前綴(99.85%;標準 Campaign lookup 多空)。金額:Upgrade 用 Recurring_Item__c.Change_Amount__c、單筆用 Donation__c.Amount__cLTV/留存=BigQuery donor_deveopment_performanceOutcome = TFR_Call_Outcome__c. Campaign type = Campaign_Name__c prefix (99.85% populated; standard Campaign lookup mostly empty). Amounts: Upgrade via Recurring_Item__c.Change_Amount__c, one-off via Donation__c.Amount__c. LTV/retention = BigQuery donor_deveopment_performance.
  • 視窗:通話結果近 5 年(≥2021-06-17);活動經歷近 10 年(≥2016-01-01)。低接通=Not Reached > 70%。接通=Yes+No+Call Back。時段Last_Call_Date__c(非 CreatedDate)。Windows: outcomes last 5y (≥2021-06-17); campaign history last 10y (≥2016-01-01). Low-connect = Not Reached > 70%. Reached = Yes+No+Call Back. Time-of-day uses Last_Call_Date__c (not CreatedDate).
數字來源分級(已驗證 / 估算 / 代理)Provenance grading (Verified / Est. / Proxy)
  • 已驗證 Verified第 1 節母體拆解、第 4–8 節原始計數與百分比、第 3 節①③⑧、KPI 卡、時段接通率、留存曲線:直接由資料計算。§1 cohort breakdown, §4–8 raw counts/percentages, §3 ①③⑧, KPI cards, time-of-day connect rates, retention curve: computed directly.
  • 估算 Est.文中標明者(2.72%、1.47/1.48 倍、3.7%、40,762 通、LTV NT$1,580):由已驗證數字推得,假設已寫明。items so marked (2.72%, 1.47/1.48×, 3.7%, 40,762 calls, LTV NT$1,580): derived from verified numbers with stated assumptions.
  • 代理 ProxyDonor Lifespan「活動年數」:以致電年份近似波次。Donor Lifespan "campaign-years": approximates waves by distinct call-years.
數字可重現性(如何重跑)Reproducibility (how to re-run)
  • 原始匯出:exports/20260617-1810-cohort-cases-10y-v2.csv…-cohort-contacts-v2.csv(含勿擾旗標);金額 …-1850-tfr-yes-amounts-v2.csv;時段 …-1910-tfr-callkitime.csvExports: exports/20260617-1810-cohort-cases-10y-v2.csv, …-cohort-contacts-v2.csv (with suppression flags); amounts …-1850-tfr-yes-amounts-v2.csv; time-of-day …-1910-tfr-callkitime.csv.
  • 彙總腳本 exports/20260617-1812-tfr-analysis-v2.py(主母體=Regular+Mixed 過濾;單筆捐=Single 過濾);LTV 查 BigQuery donor_deveopment_performanceAggregation script exports/20260617-1812-tfr-analysis-v2.py (main = Regular+Mixed filter; Single = Single filter); LTV via BigQuery donor_deveopment_performance.

產生:2026-06-17 · 資料擷取自 Salesforce 正式環境 00D2v000001f7liEAA · 分類定義 Obsidian wiki donation-flow.md · 對應 Asana 任務「TW Active RG Donor TFR Cold / Warm List Analysis & Donation Propensity」(MCW-7491)。Generated 2026-06-17 · data from Salesforce production 00D2v000001f7liEAA · classification defs Obsidian wiki donation-flow.md · Asana task "TW Active RG Donor TFR Cold / Warm List Analysis & Donation Propensity" (MCW-7491).


本頁為彙總分析(不含個資/聯絡方式/捐款金額),僅供內部募款決策參考。Aggregate analysis (no PII / contact details / individual donation amounts), for internal fundraising decisions only.

技術重現附錄(給工程師/AI)Technical reproducibility appendix (for engineers / AI)

目的:看完這個分頁,下一個人(或 AI)就能把「報告」分頁上的每一個數字從頭重算一次。下方依序給:環境與存取 → 來源物件/欄位 → 三步重現流程 → 每個數字的來源對照表 → 完整查詢與程式。所有 SQL/SOQL/指令可直接複製執行。

Goal: after reading this tab, the next person (or AI) can recompute every number on the Report tab from scratch. Below, in order: environment & access → source objects/fields → 3-step pipeline → number-by-number lineage table → full queries & code. All SQL/SOQL/commands are copy-runnable.

A. 環境與存取A. Environment & access

B. 來源物件與關鍵欄位B. Source objects & key fields

物件Object關鍵欄位Key fields
Case
(TFR 通話,每通一筆)(TFR call, 1/row)
TFR_Call_Outcome__c (Yes/No/Call Back/Not Reached/Invalid Data/Other) · Campaign_Name__c (Upgrade/Special Appeal 分類來源) · ContactId · CreatedDate · Last_Call_Date__c (真實撥打時間) · Result_Recurring_Item__cRecurring_Item__c.Change_Amount__c (升級加碼) · Result_One_off_Donation__cDonation__c.Amount__c (單筆)
ContactGiving_Status__c · Giving_Type__c · Value_Tier__c · Market__c · DoNotCall · Do_Not_Contact__c · Fundraising_Appeals_Opt_Out__c · Has_Recurring_Donation__c · Active_Recurring_Donation_Count__c
BigQuery
donor_deveopment_performance
category (Upgrade/Special Appeal/…) · type (Upgrade/Oneoff/Regular) · amount (升級=加碼額) · original_amount · mon_1..mon_12 (逐月是否仍扣款) · since_anchor · cancel_date · market='Taiwan'

C. 三步重現流程C. 3-step reproduction pipeline

定義:5 年窗 = CreatedDate ≥ 2021-06-17;10 年窗 = ≥ 2016-01-01。接通 = Yes+No+Call Back。低接通 = Not Reached 佔比 > 70%。主母體 = Regular+Mixed;單筆捐 = Single。Definitions: 5y window = CreatedDate ≥ 2021-06-17; 10y = ≥ 2016-01-01. Reached = Yes+No+Call Back. Low-connect = Not-Reached share > 70%. Main cohort = Regular+Mixed; Single = Single.

第 1 步 — 從 Salesforce 匯出(Bulk API)Step 1 — export from Salesforce (Bulk API)

# cohort contacts (+ suppression flags) → 20260617-1810-cohort-contacts-v2.csv
sf data export bulk --target-org gpea-prod -r csv --output-file cohort-contacts-v2.csv -q "
 SELECT Id, Giving_Status__c, Giving_Type__c, Value_Tier__c,
        DoNotCall, Do_Not_Contact__c, Fundraising_Appeals_Opt_Out__c
 FROM Contact
 WHERE Market__c='a0J2u000001NjoNEAS'
   AND Giving_Status__c IN ('Active','Reactivated','New','At Risk')
   AND Giving_Type__c IN ('Regular Giving Donor','Mixed Giving Donor','Single Giving Donor')
   AND Value_Tier__c IN ('General Donor','Middle Donor Prospect','Middle Donor')"

# 10y TFR cases → 20260617-1810-cohort-cases-10y-v2.csv
sf data export bulk --target-org gpea-prod -r csv --output-file cohort-cases-10y-v2.csv -q "
 SELECT ContactId, TFR_Call_Outcome__c, Campaign_Name__c, CreatedDate
 FROM Case
 WHERE TFR_Call_Outcome__c!=null AND CreatedDate>=2016-01-01T00:00:00Z
   AND Contact.Market__c='a0J2u000001NjoNEAS'
   AND Contact.Giving_Status__c IN ('Active','Reactivated','New','At Risk')
   AND Contact.Giving_Type__c IN ('Regular Giving Donor','Mixed Giving Donor','Single Giving Donor')
   AND Contact.Value_Tier__c IN ('General Donor','Middle Donor Prospect','Middle Donor')"

第 2 步 — 跑彙總腳本Step 2 — run the aggregation script

# Run in the same folder as the Step-1 CSVs. Single-donor view (§8) = re-run
# with Giving_Type filtered to Single in Step 1, or filter cohort dict to that type.
python3 tfr-analysis.py   # the full script (no local files needed):

#!/usr/bin/env python3
"""
TW donor TFR Cold/Warm analysis v2 — cohort now includes Single Giving Donor (SG/Oneoff)
alongside Regular and Mixed (per stakeholder 2026-06-17). One pass: stats JSON +
per-donor list CSV + suppression breakdown. Definitions: Decline=No; Reached=Yes+No+CallBack;
Cold=Not-Reached share of last-5y calls>70%; outcome window last 5y; lifespan 10y.
"""
import csv, json, collections, statistics

BASE = "./"   # run in the same folder as the two Step-1 CSVs
CONTACTS = BASE + "cohort-contacts-v2.csv"   # from Step 1
CASES = BASE + "cohort-cases-10y-v2.csv"  # from Step 1
OUT = BASE + "stats.json"
PERDONOR = BASE + "per-donor-list.csv"
FIVE_Y = "2021-06-17"
OUTCOMES = ["Yes", "No", "Call Back", "Not Reached", "Invalid Data", "Other"]


def classify(name):
    n = name.lower()
    if "special appeal" in n: return "Special Appeal"
    if "annual upgrade" in n or "new donor upgrade" in n or "middle donor upgrade" in n or "md upgrade" in n or "middle donor conversion" in n:
        return "Upgrade"
    return "Other"


cohort = {}
with open(CONTACTS) as f:
    for r in csv.DictReader(f):
        cohort[r["Id"][:15]] = r
print(f"cohort: {len(cohort):,}")


def newd():
    return {"oc": collections.Counter(), "up": collections.Counter(), "sa": collections.Counter(),
            "upy": set(), "say": set(), "last": ""}


D = collections.defaultdict(newd)
with open(CASES) as f:
    for r in csv.DictReader(f):
        cid = r["ContactId"][:15]
        if cid not in cohort: continue
        date = r["CreatedDate"][:10]; yr = date[:4]
        subj = classify(r["Campaign_Name__c"].strip())
        d = D[cid]
        if subj == "Upgrade": d["upy"].add(yr)
        elif subj == "Special Appeal": d["say"].add(yr)
        if date > d["last"]: d["last"] = date
        if date >= FIVE_Y:
            oc = r["TFR_Call_Outcome__c"].strip() or "Other"
            if oc not in OUTCOMES: oc = "Other"
            d["oc"][oc] += 1
            if subj == "Upgrade": d["up"][oc] += 1
            elif subj == "Special Appeal": d["sa"][oc] += 1

d5 = {c: d for c, d in D.items() if sum(d["oc"].values()) > 0}
S = {"generated": "2026-06-17", "cohort_total": len(cohort),
     "donors_called_10y": len(D), "donors_called_5y": len(d5),
     "never_called_5y": len(cohort) - len(d5)}

# breakdowns
def brk(field):
    c = collections.Counter(v[field] for v in cohort.values())
    return dict(c)
S["by_giving_type"] = brk("Giving_Type__c")
S["by_value_tier"] = brk("Value_Tier__c")
S["by_giving_status"] = brk("Giving_Status__c")

# overall outcome
ov = collections.Counter()
for d in d5.values(): ov.update(d["oc"])
tot = sum(ov.values())
S["total_calls_5y"] = tot
S["outcome_n"] = {k: ov.get(k, 0) for k in OUTCOMES}
S["outcome_pct"] = {k: round(100*ov.get(k, 0)/tot, 1) for k in OUTCOMES}
S["reach_rate_pct"] = round(100*(ov["Yes"]+ov["No"]+ov["Call Back"])/tot, 1)

# calls per donor
def band(n): return "1-2" if n<=2 else "3-5" if n<=5 else "6-10" if n<=10 else "11-20" if n<=20 else "21+"
cd = collections.Counter()
cpd = []
for d in d5.values():
    n = sum(d["oc"].values()); cpd.append(n); cd[band(n)] += 1
S["calls_per_donor_band"] = {b: cd.get(b, 0) for b in ["1-2","3-5","6-10","11-20","21+"]}
S["calls_per_donor"] = {"median": statistics.median(cpd), "mean": round(statistics.mean(cpd),1), "max": max(cpd)}

# cold list
def nr(d): n=sum(d["oc"].values()); return d["oc"]["Not Reached"]/n if n else 0
def reached(d): return d["oc"]["Yes"]+d["oc"]["No"]+d["oc"]["Call Back"]
cold_all = [c for c,d in d5.items() if nr(d)>0.70]
ge5 = {c:d for c,d in d5.items() if sum(d["oc"].values())>=5}
cold_ge5 = [c for c,d in ge5.items() if nr(d)>0.70]
S["cold"] = {"all": {"cold": len(cold_all), "total": len(d5), "pct": round(100*len(cold_all)/len(d5),1)},
             "ge5": {"cold": len(cold_ge5), "total": len(ge5), "pct": round(100*len(cold_ge5)/len(ge5),1)}}
cset = set(cold_ge5)
bt=collections.Counter(); btt=collections.Counter(); bs=collections.Counter(); bst=collections.Counter()
for c,d in ge5.items():
    t=cohort[c]["Value_Tier__c"]; s=cohort[c]["Giving_Status__c"]; btt[t]+=1; bst[s]+=1
    if c in cset: bt[t]+=1; bs[s]+=1
S["cold_by_tier"]={t:{"cold":bt[t],"total":btt[t],"pct":round(100*bt[t]/btt[t],1)} for t in btt}
S["cold_by_status"]={s:{"cold":bs[s],"total":bst[s],"pct":round(100*bs[s]/bst[s],1)} for s in bst}
wasted=[(c,sum(d["oc"].values())) for c,d in d5.items() if sum(d["oc"].values())>=5 and reached(d)==0]
S["redundant"]={"donors":len(wasted),"calls":sum(n for _,n in wasted)}

# upgrade vs appeal
up=collections.Counter(); sa=collections.Counter()
for d in d5.values(): up.update(d["up"]); sa.update(d["sa"])
def blk(c):
    t=sum(c.values()); r=c["Yes"]+c["No"]+c["Call Back"]
    return {"calls":t,"pct":{k:round(100*c.get(k,0)/t,1) for k in OUTCOMES},
            "reached_pct":round(100*r/t,1) if t else 0,
            "yes_of_reached_pct":round(100*c["Yes"]/r,1) if r else 0}
S["upgrade_vs_appeal"]={"upgrade":blk(up),"special_appeal":blk(sa)}

# propensity by tier
prop={}
for tier in ["General Donor","Middle Donor Prospect","Middle Donor"]:
    y=n=cb=0
    for c,d in d5.items():
        if cohort[c]["Value_Tier__c"]==tier:
            y+=d["oc"]["Yes"]; n+=d["oc"]["No"]; cb+=d["oc"]["Call Back"]
    r=y+n+cb; prop[tier]={"reached":r,"yes":y,"yes_pct":round(100*y/r,1) if r else 0}
S["propensity_by_tier"]=prop

# lifespan
upl=[len(d["upy"]) for d in D.values()]; sal=[len(d["say"]) for d in D.values()]
S["lifespan"]={"upgrade_mean":round(statistics.mean(upl),2),"upgrade_median":statistics.median(upl),
               "sa_mean":round(statistics.mean(sal),2),"sa_median":statistics.median(sal)}

# suppression on never-called
def T(v): return str(v).lower()=="true"
never=[c for c in cohort if c not in d5]
def supc(ids,field): return sum(1 for i in ids if T(cohort[i].get(field,"")))
nev_any=sum(1 for i in never if T(cohort[i]["DoNotCall"]) or T(cohort[i]["Do_Not_Contact__c"]) or T(cohort[i]["Fundraising_Appeals_Opt_Out__c"]))
called_any=sum(1 for i in d5 if T(cohort[i]["DoNotCall"]) or T(cohort[i]["Do_Not_Contact__c"]) or T(cohort[i]["Fundraising_Appeals_Opt_Out__c"]))
S["suppression_never_called"]={"n":len(never),"DoNotCall":supc(never,"DoNotCall"),
    "Do_Not_Contact":supc(never,"Do_Not_Contact__c"),"FR_OptOut":supc(never,"Fundraising_Appeals_Opt_Out__c"),
    "any":nev_any,"any_pct":round(100*nev_any/len(never),1),"activatable":len(never)-nev_any}
S["suppression_called_baseline_pct"]=round(100*called_any/len(d5),1)

json.dump(S, open(OUT,"w"), ensure_ascii=False, indent=1)

# per-donor CSV
with open(PERDONOR,"w",newline="") as f:
    w=csv.writer(f)
    w.writerow(["contact_id","giving_status","giving_type","value_tier","calls_5y","yes","decline_no","call_back","not_reached","not_reached_pct","reached_pct","list_segment","redundant_flag","upgrade_calls_5y","special_appeal_calls_5y","upgrade_years_10y","special_appeal_years_10y","last_call_date","suppressed"])
    for cid,info in cohort.items():
        d=D.get(cid); supp="Y" if (T(info["DoNotCall"]) or T(info["Do_Not_Contact__c"]) or T(info["Fundraising_Appeals_Opt_Out__c"])) else ""
        if not d or sum(d["oc"].values())==0:
            w.writerow([cid,info["Giving_Status__c"],info["Giving_Type__c"],info["Value_Tier__c"],0,0,0,0,0,"","","Never Called (5y)","",0,0,len(d["upy"]) if d else 0,len(d["say"]) if d else 0,d["last"] if d else "",supp]); continue
        c5=sum(d["oc"].values()); nrn=d["oc"]["Not Reached"]; rc=reached(d)
        seg="Cold" if nrn/c5>0.70 else "Warm"
        w.writerow([cid,info["Giving_Status__c"],info["Giving_Type__c"],info["Value_Tier__c"],c5,d["oc"]["Yes"],d["oc"]["No"],d["oc"]["Call Back"],nrn,round(100*nrn/c5,1),round(100*rc/c5,1),seg,"Y" if (c5>=5 and rc==0) else "",sum(d["up"].values()),sum(d["sa"].values()),len(d["upy"]),len(d["say"]),d["last"],supp])

# print
print(json.dumps(S, ensure_ascii=False, indent=1))

第 3 步 — 金額、LTV、時段(額外查詢)Step 3 — amounts, LTV, time-of-day (extra queries)

# §7 金額:升級加碼 + 單筆 → 20260617-1850-tfr-yes-amounts-v2.csv
sf data export bulk --target-org gpea-prod -r csv -q "
 SELECT Contact.Value_Tier__c, Campaign_Name__c,
        Result_Recurring_Item__r.Change_Amount__c,
        Result_One_off_Donation__r.Amount__c
 FROM Case WHERE TFR_Call_Outcome__c='Yes' AND CreatedDate>=2021-06-17T00:00:00Z
   AND Contact.Market__c='a0J2u000001NjoNEAS'
   AND Contact.Giving_Type__c IN ('Regular Giving Donor','Mixed Giving Donor') ...同上cohort"
# 升級價值=Change_Amount(加碼額,非全額);單筆價值=Donation.Amount。各別 groupby 活動/層級。

# §7 LTV / 留存(BigQuery)
bq query --use_legacy_sql=false "
 SELECT AVG(mon_3) m3, AVG(mon_6) m6, AVG(mon_9) m9, AVG(mon_12) m12,
        AVG(amount) avg_uplift
 FROM \`gpea-data.report_table.donor_deveopment_performance\`
 WHERE market='Taiwan' AND category='Upgrade' AND type='Upgrade'"
# 第一年實扣月數 = SUM(AVG(mon_2..mon_12)) where since_anchor>=12 → 10.12;LTV ≈ uplift×月數。

# §5 時段:用 Last_Call_Date__c(非 CreatedDate,後者混批次匯入)→ 20260617-1910-tfr-callkitime.csv
sf data export bulk --target-org gpea-prod -r csv -q "
 SELECT Last_Call_Date__c, TFR_Call_Outcome__c
 FROM Case WHERE TFR_Call_Outcome__c!=null AND Last_Call_Date__c!=null
   AND CreatedDate>=2021-06-17T00:00:00Z AND ...同上cohort(Regular+Mixed)"
# Python: Last_Call_Date__c[:19] 解析 +8 小時(台灣);接通率 = reached/total 按小時與星期 groupby。

第 3 步的兩段彙總程式(無本機檔案,貼上即可跑)Step-3 aggregation snippets (no local files needed)

# §7 金額:升級加碼(Change_Amount) + 單筆(Donation) → 依活動 / 依層級
import csv, collections
def classify(n):
    n=n.lower()
    if "special appeal" in n: return "Special Appeal"
    if any(s in n for s in ["annual upgrade","new donor upgrade","middle donor upgrade","md upgrade","middle donor conversion"]): return "Upgrade"
    return "Other"
def f(x):
    try: return float(x)
    except: return None
rows=list(csv.DictReader(open("tfr-yes-amounts.csv")))   # from Step-3 SOQL export
agg=collections.defaultdict(lambda:{"yes":0,"chg_n":0,"chg_sum":0.0,"oo_n":0,"oo_sum":0.0})
for r in rows:
    key=classify(r["Campaign_Name__c"])            # for by-tier: key=r["Contact.Value_Tier__c"]
    g=agg[key]; g["yes"]+=1
    chg=f(r["Result_Recurring_Item__r.Change_Amount__c"])   # upgrade uplift (the delta)
    oo =f(r["Result_One_off_Donation__r.Amount__c"])        # special-appeal one-off
    if chg is not None: g["chg_n"]+=1; g["chg_sum"]+=chg
    if oo  is not None: g["oo_n"]+=1;  g["oo_sum"]+=oo
# Upgrade 月加碼均 = chg_sum/chg_n (= +183); 月總 = chg_sum; 年化 = chg_sum*12 (~21.5M)
# Special Appeal 單筆均 = oo_sum/oo_n (= 1,851); 總 = oo_sum (= 6.56M)

# §5 時段接通率:用真實撥打時間 Last_Call_Date__c(非 CreatedDate)
from datetime import datetime, timedelta
REACHED={"Yes","No","Call Back"}
byh=collections.defaultdict(lambda:[0,0]); bywd=collections.defaultdict(lambda:[0,0])  # [reached,total]
for r in csv.DictReader(open("tfr-callkitime.csv")):    # from Step-3 SOQL export
    try: dt=datetime.strptime(r["Last_Call_Date__c"][:19],"%Y-%m-%dT%H:%M:%S")+timedelta(hours=8)
    except: continue
    rc=1 if r["TFR_Call_Outcome__c"].strip() in REACHED else 0
    byh[dt.hour][0]+=rc; byh[dt.hour][1]+=1
    bywd[dt.weekday()][0]+=rc; bywd[dt.weekday()][1]+=1
# 每小時接通率 = byh[h][0]/byh[h][1](09:00=51.1% ... 18:00=68.4%);星期同理用 bywd

D. 每個數字的來源對照表D. Number-by-number lineage

「來源」欄:SOQL=Salesforce 查詢;script=第 2 步腳本欄位;bq=BigQuery;calc=由前述數字算得(估算)。"Source": SOQL = Salesforce query; script = field from the Step-2 script; bq = BigQuery; calc = derived from the above (estimate).

章節§數字Number怎麼拿到的How obtained
151,306SOQL COUNT(Id) on Contact, cohort filter (Regular+Mixed)
135,814 / 15,492SOQL GROUP BY Giving_Type__c
136,592 / 12,758 / 1,956SOQL GROUP BY Value_Tier__c
134,669 / 9,698 / 4,982 / 1,957SOQL GROUP BY Giving_Status__c
1·5293,632script: count cases with CreatedDate ≥ 2021-06-17 (Regular+Mixed)
1·5Yes 27.0% (79,173) / No 19.8% / Call Back 7.7% / Not Reached 38.8% / Invalid 0.4%script overall_outcome (count by TFR_Call_Outcome__c, ÷ total)
1·554.5% connectcalc: (Yes+No+Call Back) ÷ total
1·47,316 (14.9%) / 4,936 (14.1%)script cold: per-donor Not-Reached share > 0.70 (all-called / ≥5-calls)
1·61,127 / 7,965script redundant: ≥5 calls AND reached=0; wasted = sum of their calls
1·82,276 never calledcalc: cohort − donors with ≥1 call in 5y
1·3522 (22.9%); baseline 6.8%merge suppression CSV with never-called set; any of DoNotCall/Do_Not_Contact/Fundraising_Appeals_Opt_Out
3·746.1% / 55.4% / 67.6%script propensity_by_tier: Yes ÷ (Yes+No+Call Back) per Value_Tier
4cold by tier 15.1 / 12.4 / 6.2%script cold_by_tier (≥5-calls basis)
4cold by status 15.7 / 10.6 / 9.9%script cold_by_status
5calls/donor 6,128 / 13,919 / 27,160 / 1,806 / 17; median 6; max 82script calls_per_donor_band
6Upgrade 143,024 / Special Appeal 59,334script: count by classify(Campaign_Name__c)
6Upgrade 29.2% / Special Appeal 39.9% (accept-when-reached)script upgrade_vs_appeal: Yes ÷ reached per subject
7+$183/mo · +$1.79M/mo · ~+$21.5M annualized (Upgrade)amounts CSV: AVG/SUM of Result_Recurring_Item__r.Change_Amount__c for Upgrade-Yes; ×12
7$1,851 · $6.56M (Special Appeal)amounts CSV: AVG/SUM of Result_One_off_Donation__r.Amount__c
7by-tier $133/$221/$452 · $1,345/$1,983/$3,338amounts CSV grouped by Value_Tier__c
7retention 94.4 / 93.2 / 90.0 / 86.9%bq: AVG(mon_3/6/9/12), category='Upgrade' type='Upgrade', market='Taiwan'
7uplift $156 · 10.1 months · LTV ≈ NT$1,580bq AVG(amount); SUM AVG(mon_2..12) where since_anchor≥12; calc product
7Special Appeal $2,138 × 1.23 = ~$2,630bq: category='Special Appeal' type='Oneoff'; gifts ÷ distinct donors
7lifespan 2.44 / 1.27script: distinct call-years per donor per subject, then AVG
5hour reach 51.1%→68.4%; weekday 52.6–61.2%callkitime CSV: parse Last_Call_Date__c +8h; reached ÷ total by hour / weekday
8SG 3,268; New 1,431 / Active 1,340 / Reactivated 497 / At Risk 0SOQL Single filter; GROUP BY Giving_Status__c
8SG no-RD 3,267/3,268SOQL GROUP BY Has_Recurring_Donation__c (Single)
8SG 47% called · 28.0% accept · 32.6% low-connectsame script logic, cohort filtered to Giving_Type__c='Single Giving Donor'
2.72% / 1.47× / 1.48× / 3.4× / 3.7% / 40,762 估算 Est.calc: ratios/products of the verified numbers above (formula stated inline in the Report tab)

E. 重要陷阱(重算時務必注意)E. Pitfalls (must-know when recomputing)

完整可執行腳本與原始 CSV 皆在 deliverable 的 exports/;本附錄之指令為精簡版(cohort 過濾條件以「…同上」省略,請套用第 1 步的完整 WHERE)。Full runnable scripts and raw CSVs are in the deliverable's exports/; commands here are abbreviated (cohort filters shown as "…same as above" — apply the full WHERE from Step 1).