/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
在 AI 搜索可见度检测里,最常见的错误不是算错,是不算方差就直接比大小。
"这次提及率 35%,上次 30%,涨了 5 个点"——这句话如果没带置信区间,等于什么都没说。样本量 20 题、单次采样的情况下,5 个点的差异大概率落在噪声里。本文给一套零依赖的实现:用二项比例估计提及率,用 Wilson 区间处理小样本,用配对 bootstrap 处理复测差值,并给出"多少题才够"的最小样本量估算。
一、口径先行:分母是什么
先定死三件事,否则后面所有区间都是错的。
第三点是实际项目里最容易被忽略的。一次超时被记成"未提及",等于凭空多了一个负样本。
二、点估计:二项比例
提及率本质是一串 0/1 的均值。用 k/n 估计即可,不需要加权——除非做了分层抽样(比如按"医院类问法"和"用品类问法"分层),那要按层权重合并。
from math import sqrt
def rate(hits: int, total: int) -> float:
return hits / total if total else 0.0
简单到不用解释,但它的置信区间不简单。
三、区间估计:为什么用 Wilson 而不是正态近似
常见的 p ± 1.96*sqrt(p(1-p)/n) 在 p 接近 0 或 1 时会崩:算出的下界可能是负数,或者区间宽度为 0。而 GEO 检测里"提及率 3%""提及率 0"是常态——绝大多数中小企业在绝大多数题上就是不被提及。
Wilson 得分区间在小样本与极端比例下表现稳定,且不需要外部库:
def wilson_ci(k: int, n: int, z: float = 1.96) -> tuple:
"""Wilson score interval, 95% by default"""
if n == 0:
return (0.0, 1.0)
p = k / n
denom = 1 + z * z / n
center = (p + z * z / (2 * n)) / denom
half = (z / denom) * sqrt(p * (1 – p) / n + z * z / (4 * n * n))
return (max(0.0, center – half), min(1.0, center + half))
跑几个数看看差别:
| 0/20 | 0.0% | (-0.0%, 0.0%) | (0.0%, 16.1%) |
| 1/20 | 5.0% | (-3.3%, 13.3%) | (0.9%, 23.6%) |
| 3/20 | 15.0% | (-0.6%, 30.6%) | (5.2%, 36.0%) |
| 7/20 | 35.0% | (14.1%, 55.9%) | (18.1%, 55.9%) |
| 18/20 | 90.0% | (76.9%, 103.1%) | (69.9%, 97.2%) |
第一行最能说明问题:20 题全未被提及,正态近似给出"0%,区间宽度为零"——一个自信到荒谬的结论。Wilson 给的是"0% 到 16%",这才是真实的认知水平:没见到,但样本不足以断言它不存在。
四、复测差值:配对 bootstrap
复测的正确比法是配对:同一题、同一平台、同一参数,这次和上次各是什么结果。不能拿"这次的总提及率"减"上次的总提及率"——两批题的难度不同,总体比例会把题目差异混进效果里。
配对差值的最小实现:
import random
def paired_diff_ci(before: list, after: list, iters: int = 5000, seed: int = 42):
"""
before/after: 同长度 0/1 列表, 按 query_id 对齐
返回 (差值点估计, 下界, 上界)
"""
assert len(before) == len(after)
n = len(before)
if n == 0:
return (0.0, 0.0, 0.0)
d = [a – b for a, b in zip(after, before)]
point = sum(d) / n
rnd = random.Random(seed)
boots = []
for _ in range(iters):
s = sum(d[rnd.randrange(n)] for _ in range(n)) / n
boots.append(s)
boots.sort()
lo = boots[int(0.025 * iters)]
hi = boots[int(0.975 * iters) – 1]
return (round(point, 4), round(lo, 4), round(hi, 4))
判读规则只有一条:区间跨过 0,就不能说"变好了"。
这一条能挡掉大部分误报。举个例子:20 题、上次 6 次提及、这次 9 次,点估计 +15 个点,看着很漂亮;配对 bootstrap 出来的区间可能是 (-2%, +32%),跨零,只能写"方向为正、未达可判定水平"。等题量加到 60,同样幅度才可能显著。
五、多少题才够:最小样本量估算
不用公式也能给个可操作的对照表。设目标检出幅度为 Δ(绝对百分点),α=0.05、power=0.8、双侧,粗略估算所需独立单位数(题数,不是调用次数):
| +30pp | 约 26 题 | 约 34 题 | 约 42 题 |
| +15pp | 约 95 题 | 约 125 题 | 约 150 题 |
| +5pp | 约 800 题 | 约 1000 题 | 约 1200 题 |
(按两比例检验近似计算,未做连续性校正;仅供排期参考。)
这张表的现实含义:20 题能看出"有没有"和"大概方向",看不出"涨了 5 个点"。谁拿 20 题的检测结果说"本月提升了 5%",这个结论在统计上不成立。
也正因为如此,把效果承诺写进合同、按"提升 N 个百分点"验收,在工程上是有前提的:题量必须够。题量不够而承诺幅度的,只能靠挑题、挑平台、挑采样次数来凑数。
六、两个坑
坑一:重复计数把分母撑大了。
早期去重键只用了 query_id,没带 sample_k。结果是同一题的 3 次采样被合并成 1 条记录,但分母按 3 倍算,提及率直接被压到三分之一。表上看是"优化失效",实际是键写错了。
修法:去重键统一为 (query_id, sample_k, platform),入库时加唯一索引;统计前先跑一遍 count(distinct key) == 预期总数 的断言,不等就中止。
坑二:并发写入导致样本乱序、配对错位。
复测数据用 3 并发写库,写入完成顺序与 sample_k 不一致。配对脚本按行号对齐,结果把 A 题的"这次"配到了 B 题的"上次"上,差值全是噪音。
修法两条:一是配对前强制 order by query_id, sample_k;二是加校验——两侧的 query_id 集合必须完全相同,多一条少一条直接抛异常,不许静默对齐。
七、这套方法测不出什么
- 测不出因果。 差值显著只说明"这次和上次不一样",不能证明是某项优化导致的。要归因得做对照(同城未优化的对照组),成本高得多。
- 测不出绝对水平。 提及率依赖题目池,换一批题就不能跨批比较。
- 测不出长尾。 区间宽度由 n 决定,长尾问法题量少,结论天然更弱。
- 测不出真实用户分布。 采样是等权重的,真实用户的问法分布并不等权。要贴近真实,得按搜索量加权,而多数企业拿不到这个权重。
结论一句话:先算区间,再谈差值;区间跨零,就不要写"提升"。这一条不解决"排名能不能保证"的问题,但它能让报告里的每个数字都站得住。
你们在做复测比对时,是按总体比例减,还是按题配对?评论区聊聊。
网硕互联帮助中心




评论前必须登录!
注册