统计词话(一)

不知道这个标题是否有足够的吸引力把你骗进来。如果你认为统计是一个到处充满了期望方差分布回归随机多元和概率的东西,那么……你可能是对的,不过本文想要告诉你的是,你其实还可以用统计来做一些你关心的事情,比如现在,我们既谈风月,也谈统计。:D

相信大家对宋词都不会陌生。无论你是否喜欢,总还是可以吟诵出几句名篇来的。如果你经常找一些宋词来读的话,你可能会发现一个有趣的现象,那就是有些词语或意象似乎特别受到词人的青睐,像是东风,明月,芳草等等。当然,对于这个现象,不同的人有不同的看法。一种观点是这些意象往往具有特定的含义,或是抒发离恨,或是寄托相思,总之是把人们的情感倾注在了这些最常见的事物之中,让人触景生情;而另一种看法则觉得词的雕琢痕迹太明显,内容也相对单调,使得用词容易造成重复。本文当然不是来探讨这些话题的,而是想用统计的方法来给大家展示一下究竟有哪些话语被词人一次一次地书写,被读者一遍一遍地传唱。

从统计的角度来看,上面这个问题其实非常简单,无非就是计算一下宋词之中词语出现的频率,然后做一个排序就可以了。但这个问题对于中文来说恰恰是最难攻克的一个环节。在英语中,词语与词语之间有着天然的分隔符,但对于中文,只有句子之间有标点符号,句子之内只能通过词语的含义来进行辨别。这也就是为什么在文本挖掘领域中,中文的分词依然是一个富有挑战性的任务的原因。

不过好在宋词本身的形式帮了我们很大的忙。首先,宋词的句子一般都非常短,这相当于已经有了一次粗略的词语划分;其次,宋词的用词也很简洁,一个词一般是两个字,偶尔可能有三个字、四个字,超过四个字的词就非常罕见了。于是我们就有一种比较“野蛮”的做法,来对宋词中的用词进行划分。

举个例子来说,《青玉案》中的这句“东风夜放花千树”,如果把所有可能的两个字的组合列出来,就是:

东风  风夜  夜放  放花  花千  千树

如果再把三个字的可能组合列出来,则有:

东风夜  风夜放  夜放花  放花千  花千树

如果不考虑其它的可能,那么总共就有11个词语。当然,这其中会有很多无意义的组合,但是我们可以预想的是,这些无意义的组合将不太可能大规模地重复出现,因此在排序的过程中它们自动地就被排在高频词语之后了。通过这种做法,宋词中的每句话大体都能分成10个左右的词语单位,然后对所有的这些词语单位进行频数统计,就可以得到最终的高频词语列表了。

下面就通过一段R程序来进行一次实际的分析,用到的数据是从网络上整理的《全宋词》电子资料,其中可能有部分字词不正确。完整的程序和数据可以从这里下载

首先,当然是读取数据。

txt=read.csv("SongPoem.csv",colClasses="character");

接下来提取出宋词的内容,并根据标点符号对句子进行分割。

sentences=strsplit(txt$Sentence,",|。|!|?|、");
sentences=unlist(sentences);
sentences=sentences[sentences!=""];

对句子进行分割后需要检查一遍,如果有些句子的长度超过了15个字,那么很可能是错误的字符,应该剔除掉。

s.len=nchar(sentences);
sentences=sentences[s.len<=15];
s.len=nchar(sentences);

下面的这个函数非常重要,其作用就是按照之前的做法把所有可能的字的组合计算出来。这里只是考虑了两个字的组合。

splitwords=function(x,x.len) substring(x,1:(x.len-1),2:x.len);

接下来就好办了,无非就是应用上面的函数对句子进行拆分,然后统计词频并排序。

words=mapply(splitwords,sentences,s.len,SIMPLIFY=TRUE,USE.NAMES=FALSE);
words=unlist(words);
words.freq=table(words);
words.freq=sort(words.freq,decreasing=TRUE);
words.freq[1:100];

最后的结果如下:

排序  词语    频数          排序  词语    频数
1     □□     1584         51    匆匆    357
2     东风    1379         52    芙蓉    356
3     何处    1231         53    今日    354
4     人间    1164         54    扁舟    351
5     风流    843          55    西湖    350
6     归去    818          56    憔瘁    349
7     春风    800          57    消息    347
8     西风    782          58    桃花    343
9     归来    768          59    何事    335
10    江南    760          60    一片    333
11    相思    759          61    神仙    332
12    梅花    725          62    一声    331
13    千里    668          63    黄花    330
14    多少    653          64    心事    330
15    回首    649          65    鸳鸯    328
16    如今    647          66    十分    327
17    明月    646          67    人生    324
18    阑干    632          68    断肠    323
19    年年    605          69    佳人    323
20    万里    587          70    长安    321     
21    一笑    579          71    东君    319     
22    黄昏    561          72    桃李    319     
23    当年    537          73    而今    318     
24    芳草    533          74    为谁    317     
25    天涯    531          75    无情    307     
26    相逢    523          76    去年    306     
27    尊前    519          77    天气    306     
28    一枝    510          78    不是    305     
29    风雨    500          79    海棠    305     
30    流水    481          80    少年    305     
31    风吹    474          81    今夜    304     
32    依旧    469          82    不似    303     
33    多情    458          83    十年    303     
34    风月    452          84    行人    300     
35    当时    451          85    谁知    300     
36    故人    445          86    寂寞    299     
37    斜阳    444          87    肠断    297     
38    无人    443          88    江上    297     
39    不知    426          89    悠悠    297     
40    深处    424          90    富贵    295     
41    不见    416          91    时候    295     
42    时节    407          92    昨夜    295     
43    凄凉    404          93    几度    292     
44    平生    394          94    月明    292     
45    春色    393          95    何时    291     
46    无限    381          96    青山    291     
47    一点    374          97    蓬莱    290     
48    功名    366          98    往事    290     
49    杨柳    363          99    如何    287     
50    天上    361          100   惟有    287

需要解释一下的是,排在第一位的方框是词库中的一些出错的字符,直接略去即可。

至此,真相大白。至于这个结果意味着什么,就留给读者自己细细品读吧。

统计词话(一)》有40个想法

      1. 何出此言啊,呵呵。恐怕“后之视今,亦犹今之视昔”啊。:-)

      2. 有抽风问路怀旧,这数学不好是从哪儿看出来的?数数只能从一数到十?一笑一枝一点一片十分十年……

  1. 重剑无锋,大巧不工;此文之谓也。

    以后喝酒应该让每人根据抽中的高频意境吟诵相应的词句,背不出来则罚酒。

    文章名字起得极好,贴切传神又兼具文学味道和统计味道,陶醉+拜服~~~

  2. 随意一扫,竟发现横着连读也不错(31开始):

    风吹今夜依旧
    不似多情
    十年风月
    行人当时谁知
    故人寂寞

    1. 是不是随机抽样任意组合都很可能成为一首词?宋词和政治题、思想汇报似乎有某种相似性啊 😀

      1. 虽然词的格律要求严格,但随机抽样任意组合产词估计还是行,不过品味高的肯定少了.
        灵光乍现,想我这种不会作词的人,拿着这个词料库让电脑自己按一个词牌往里面填,说不定都可以成为一个”伪词人”.:)

      1. 哈哈, 我当时就正在想可以按辛弃疾的来填词呢..
        原来现在以前国外的诗歌自动生成现在在中国的各种古典文化也有少进展了..
        宋词应该算好做的…诗就难写了, 文言文就更那个了…

      2. “希望我们的研究,能够弥补我国在诗歌自动生成学术性研究方面的不足”——这种东西开开玩笑还可以,真的当学术研究我怎么想怎么觉得没谱……已经有的诗词都读不完了,不知道有谁会去读自动生成的诗词。不管自动生成的诗词如何漂亮通顺,它们都没有特定的意境和人的情感。

  3. 你可以用这个程序分析一下范仲淹和辛弃疾词的区别,或者分析一下婉约派和豪放派词的不同之处。也许是更近一步。

    1. 方向上来说,好像没有北风、西风、南风,只有东风。我怎么想起麻将来了,东西南北中发白……

      这“古道西风瘦马”可能是看前人总是打东风都腻了才打出西风来的,还是各朝代的气象条件不一样?……

      话说回来为啥从没听说过什么诗词里出现过南风呢?夏天诗人们都干啥去了?

      1. 那個那個,西風有排第八的(or 7th)。。
        南風是真的沒什麽印象。可能南風在天熱刮得多,基本沒被察覺到。

      2. 主要是春愁是一个很广泛的主题,东风就多。西风也不少,不过估计被金风分掉了一些。北风放在词里就有点俗了,诗里面会被朔风分走一些。南风的隐喻相对差些,早些的南风不竞是死声,而且有南面称孤一说,对着老板吹的风还是少些为妙,自然的南方时节也不大合适写词,吹到南风后多半要写“一点浩然气千里快哉风”之类的句子

  4. 这些口口字符有可能是在GB2312编码下显示不出来,比如《殢人娇》的第一个字。貌似贺铸和无名氏的口口最多。我不知道原始数据是什么编码以及UTF8是不是会好一些。

    这数据还有一个小问题,Title和Title2基本上相同,要么就是Title2为空,此时往往是上一首词不完整,最后一句挪到了下一行。

    数据再进一步量化一下,也许能做出一篇不错的论文。

    1. 忘了说了,这个csv是我整理后的结果,title是词牌名,title2是词牌正名,因为不知道怎么翻译所以就干脆title和title2了。有的词牌内容丢失了,就标注为“失调名”,这时候词牌正名就是空的,而词的内容也往往不完整。

  5. 这篇给我们博客带来不少清新之风啊。关于南风,《诗经》里有“凯风南来”,不过直接拿这两个字入诗的还真不多。又,以前叫填词,对着曲谱,一个萝卜对应一个坑,有时候为了适应格式,出来的东西,真跟自动生成的一样。

  6. 不错不错,不知分词后结果会不会变。我一直想用R做一个集句词的工具,还想弄个古诗词分词的,不过一直没开始做。有空可以一起研究一下,哈哈。

    1. 所附的所有数据(原始数据、程序脚本、结果图表,等)已过期,能不能发给菜鸟一份。谢谢,邮箱yinuo9257@163.con谢谢!

      1. 你好,实在抱歉,忘了续期了。
        现在可以下载了。http://u.115.com/file/f39c476cae

  7. 请问在用Excle整理文本数据的时候有些什么具体的格式要求呢?

  8. 我按照你说的在R中重新验证了下,R提示指令错误,不知为何
    splitwords=function(x,x.len) substring(x,1:(x.len-1),2:x.len);
    > words=mapply(splitwords,sentences,s.len,SIMPLIFY=TRUE,USE.NAMES=FALSE);
    Error in mapply(splitwords, sentences, s.len, SIMPLIFY = TRUE, USE.NAMES = FALSE) :
    object ‘sentences’ not found
    > words=unlist(words);
    Error in unlist(words) : object ‘words’ not found

发表评论

邮箱地址不会被公开。 必填项已用*标注