做中文稿的关键词排查,动作通常都一样:把整段正文贴进频率统计、切到词频、等一张榜单。第一次拿到「总单词数 0」和一行「暂无数据」时,很难把它和刚贴进去的那 25 个汉字联系起来——字明明都在框里。
空表不是坏了,是抽取只认 ASCII 字母
模板第 152 行的抽取正则是 /[a-zA-Z]+/g,词的定义就是连续 ASCII 字母串。同一句「中文根本不按词切分所以字频统计会把每个汉字单独计数」交给文本频率统计,两个 tab 给出两份完全不同的东西:字符频率下是 25 字符、23 项榜单,榜首「字」2 次 8.0%,第二项「计」同为 8.0%,其余上榜的汉字各 1 次 4.0%,23 项显示占比加起来正好 100.0%;英文词频下是 0 个词,把每个汉字之间敲上空格再喂进去仍然是 0——它不按空白切词,手动分词救不回来。
图 1:同一句 25 字中文的两份榜单
汉字、标点、全角空格在这张榜里同权
字符模式的跳过名单只有空格、换行、制表、回车这 4 个字符(模板第 123 行),其余全部计入。前 5 名里出现「。」并不奇怪:43 字符的段落里它以 3 次 7.0% 挤进前 5;全角空格更不在跳过名单,5 字符的 A B C 里它以 2 次 40.0% 直接坐榜首。
分母也带着这些字符:58 字符的模板混排样例只计到 49 个,41 项占比之和只有 83.5%,缺的 16.5% 正是被跳过却又留在分母里的空白。计数单位是 UTF-16 码元,50 个表情符加 abc 长度是 103、代码点只有 53,一个表情被拆成 2 项、各 50 次 48.5%。
停用词开关改的不只是名次,还有分母
模板自带那段 117 字符、24 个词的英文样例最能暴露口径。停用词字段留空时榜单 17 项,榜首 the 4 次 16.7%,逐行占比加起来 100.3%——多出的部分是 17 行 toFixed(1) 的舍入残差。把默认那张 30 词的表打开,榜首换成 fox 2 次 8.3%,第 1 到第 4 名 fox、over、dog、again 同频并列,不同单词 17 个减到 11 个,占比之和掉到 62.6%。
图 2:停用词开与关的 top5 与占比之和
这 62.6% 不是留存率:分母仍是过滤前的 24 个词,11 项计数之和只有 15 个词次,24 减 15 = 9 才是被抹掉的词次。开关带来的不是名次位移而是换人——原本并列第 5 档的四个词被抬成榜首,唯一的第 1 名 the 直接消失。
勾上「区分大小写」,停用词表半失效
词表里的 30 项装载时被统一转成小写,匹配却拿原样大小写查表。勾上「区分大小写」后句首的 The 逃过过滤,榜首变成 The 2 次 8.3%,统计栏照样报排除停用词 30 个。停用词清空再勾同一个开关,the 家族被拆成 The 2 与 the 2 两行、各 8.3%,而合并口径本是一行 4 次 16.7%——任何一行都不到真实占比的一半。
同频并列时,三个模式三套次序
三个模式用的是同一个按次数降序的比较器,排序稳定,于是并列名次完全取决于排序前数组的来源,而这三处来源不同:
- 字符模式取自
Object.keys,整数型键被提到最前按数值升序:输入1234567890的榜首是 0,b a a b 5 5 z给出 5、b、a、z,数字 5 被顶到第 1 位(15.4%)。 - 词模式也取自
Object.keys,但键只能是字母串,于是纯首次出现序:zebra apple zebra apple里 zebra 2 次 50.0% 排在 apple 之前,字典序会给出相反结果。 - 关键词模式就是你写列表的顺序,与文本里谁先出现无关。
带数字的词还会整体塌缩:w0 到 w4999 这 5000 个编号词只剩 1 行 w 5000 次 100.0%,版本号、SKU 这类名单全被并成同一行。
关键词那一栏的百分比不是密度
它的分子是命中次数、分母是字符数。43 字符的默认样例里「人工智能」出现 3 次,界面显示 7.0%;按实际占用的 12 个字符算是 27.9%,差 4 倍。列表还不去重:同一个词写 3 行再加 1 行别的,关键词数报 4 个、总命中次数报 10 次,而文本里真实只有 4 处。匹配也不重叠,aaa 里找 aa 报 1 次 33.3%(真实 2 个位置),aaaa 报 2 次 50.0%(真实 3 个)。榜单也没有取前 N 项的开关,1000 行关键词就渲染 1000 行。
| 模式 | 什么算一项 | 占比分母 | 并列时谁在前 | 中文实测 |
|---|---|---|---|---|
| 字符频率 | 非空白字符,含标点与全角空格 | 文本长度,空白留在里面 | 数字插队,其余按首次出现 | 25 字符出 23 项,榜首 8.0% |
| 英文词频 | 连续 ASCII 字母串 | 过滤前的词数 24 | 首次出现序 | 同一句话 0 个词 |
| 关键词 | 列表里的每一行,不去重 | 字符数(非占用字符) | 列表的书写序 | 3 次显示 7.0%,实占 27.9% |
这份榜单能回答什么,不能回答什么
能回答的是相对多少:463 字符的一段固定英文里榜首确实是 e(50 次),与单字母频率表的说法一致,但工具打印 10.8%,只按 379 个字母算是 13.2%,教科书那个 12.7% 两头都不沾——差额全来自留在分母的 78 个空白与 6 个标点。
不能回答的是中文的词:词由 ASCII 字母序列定义,中文在这张榜上永远不出现,关键词那栏又拿字符数当分母。真要用的话,可以先用文本排序去重把关键词列表压成唯一行,免得重复行把总命中次数抬高;如果本来只要「这篇稿子多少字」那种总量,字数统计讲的是另一套口径,跟排名无关。
Q:中文为什么永远是 0 个词?
词的定义是 /[a-zA-Z]+/g 抓到的连续 ASCII 字母,汉字进不了这个字符类。逐字敲空格仍是 0——它不按空白切词;重音字母反倒被当切分符,café 只剩 caf 一项。
Q:占比加起来不等于 100%,是算错了吗?
三种成因实测都存在:停用词留空时 17 项之和 100.3%,是逐行保留一位小数的舍入残差;字符模式 41 项之和只有 83.5%,因为空白被跳过却留在分母;停用词打开后 11 项之和 62.6%,因为分母仍是过滤前的 24 词。能不能相加,取决于分子与分母是不是同一份集合。
Q:并列同频时榜首到底算谁?
看模式:字符模式里 ASCII 数字按数值升序插队,1234567890 的榜首是 0;词模式按首次出现,zebra 排在 apple 前;关键词模式照抄列表书写顺序。三套都不是字典序,同频时不要把第 1 行当唯一答案。