diff --git a/apps/docs/src/components/IncomeDistribution.tsx b/apps/docs/src/components/IncomeDistribution.tsx index 611b3d0..cd2cc64 100644 --- a/apps/docs/src/components/IncomeDistribution.tsx +++ b/apps/docs/src/components/IncomeDistribution.tsx @@ -20,6 +20,9 @@ export default function IncomeDistribution({ data, onSelect }: { }), [data, dateWindow, year, annualize]); const groups = useMemo(() => groupIncome(sample.points, splitModels).sort((a, b) => a.category.localeCompare(b.category) || a.provider.localeCompare(b.provider) || a.model.localeCompare(b.model)), [sample.points, splitModels]); + const categoryGroups = [...new Set(groups.map((group) => group.category))].map((category) => ({ + category, rows: groups.filter((group) => group.category === category), + })); const categories = new Map(data.categories.map((category) => [category.id, category.name])); const metric = annualize ? "MRR×12参考年化" : "平台MRR"; const values = sample.points.map((point) => point.value); @@ -41,6 +44,7 @@ export default function IncomeDistribution({ data, onSelect }: {
+

{splitModels ? "分组:业务类别 × 支付平台 × 收费与交付模式。同一类别下的多行是不同统计组,各自计算均值和中位数。" : "分组:业务类别 × 支付平台。同一平台内的收费模式已合并,不同支付平台仍分别统计。"}

资料截至 {data.asOf},纳入 {sample.points.length} 个产品,另有 {sample.excluded.length} 个 MRR 观测未纳入。{annualize ? "乘12只改变显示单位,不证明全年收入、续约或ARR;CV与相对敏感性不变。" : "MRR不等于近30日收款;平台数据连接也不等于财务审计。"}{dateWindow === "all" && " 当前包含不同时间的历史观测,不能称为同期对照。"}

{sample.points.length ? <>
@@ -48,9 +52,16 @@ export default function IncomeDistribution({ data, onSelect }: { {ticks.map((tick) => {usd(tick)})} {groups.map((group, index) => { const y = index * 70 + 40; - return - {categories.get(group.category)} - {group.provider} · {group.model} · n={group.stats.n} + const firstInCategory = index === 0 || groups[index - 1].category !== group.category; + const categoryRowCount = categoryGroups.find((entry) => entry.category === group.category)!.rows.length; + return + {firstInCategory && <> + {index > 0 && } + {categories.get(group.category)}({categoryRowCount}组) + {categoryRowCount > 1 && } + } + {group.provider} · {group.model} · n={group.stats.n} + {group.stats.n < 3 && {group.stats.n === 1 ? "仅1个产品,无法判断组内离散度" : "仅2个产品,不代表领域典型收入"}} 样本均值 {usd(group.stats.mean)} 样本中位数 {usd(group.stats.median)} @@ -62,12 +73,13 @@ export default function IncomeDistribution({ data, onSelect }: { ; })} -
圆点:产品观测 竖线:样本均值 菱形:样本中位数。按业务类别与支付来源分组,点击点或下表产品名可看原始证据。
+
圆点:产品观测 竖线:样本均值 菱形:样本中位数。业务类别标题下各行按支付平台{splitModels && "及收费与交付模式"}分别统计;n为该组产品数。点击点或下表产品名可看原始证据。
-
{groups.map((group) => - +
{metric}样本统计;金额按显示值计算,CV使用样本标准差
类别 / 来源 / 模式n均值中位数均值/中位数CV去除最大值后的均值 / 下降幅度产品与限制
{categories.get(group.category)}{group.provider} · {group.model}{group.stats.n}{usd(group.stats.mean)}{usd(group.stats.median)}{group.stats.meanMedianRatio?.toFixed(2) ?? "—"}{group.stats.cv?.toFixed(2) ?? "—"}{usd(group.stats.meanWithoutLargest)}{percent(group.stats.largestRemovalDrop)}
{categoryGroups.map(({ category, rows }) => {rows.map((group, index) => + {index === 0 && } + - )}
{metric}样本统计;每行是类别下的一个统计组,CV使用样本标准差
业务类别支付平台 / 收费模式n均值中位数均值/中位数CV去除最大值后的均值 / 下降幅度产品与限制
{categories.get(category)}{rows.length}个统计组{group.provider}{group.model}{group.stats.n}{usd(group.stats.mean)}{usd(group.stats.median)}{group.stats.meanMedianRatio?.toFixed(2) ?? "—"}{group.stats.cv?.toFixed(2) ?? "—"}{usd(group.stats.meanWithoutLargest)}{percent(group.stats.largestRemovalDrop)} {group.points.map((point) => {" "})}{group.stats.n < 3 ? "样本少于3,不能据此判断领域典型收入。" : "自选的小样本,须结合产品年龄、成本与留存评估。"}
+ )})} :

当前窗口和创立年份没有符合条件的 MRR 记录。

}
统计规则与解释

每个产品只取符合条件的最新一条平台 MRR;币种为美元,要求有效数据日期、确定数值及支付来源。公司 ARR、估算、融资、目标和近30日收入均不进入这里。未知值不补零。

CV=样本标准差÷均值,标准差分母为 n−1;n=1 不算 CV。n=2 时均值必然等于中位数,不能据此认为均值有代表性。较大的均值/中位数差距、CV或移除最大值的变化,仅提示进一步核查样本构成。

支付商与平台的退款、试用、年付、币种及指标计算可能不同(平台方法说明)。不能将 MRR 与收款差异、单个高值或高离散度直接解释为财务造假。比较商业模式还需要毛利、获客成本、留存、人工交付和客户集中度。

{sample.excluded.length > 0 &&
未纳入的 MRR 记录({sample.excluded.length})
}