第一次看到老师给的题目的时候,我就很怀疑:通过每个章回虚词出现的频率(频数),就能判别是否是同一作者?
大概出现的虚词:
cell=['之','其','或','亦','方','于','即','皆','因','仍','故','尚','乃','呀','吗','咧','罢咧','啊','罢','罢了','么','呢','了','的','着','一','不','把','让','向','往','是','在','别','好','可','便','就','但','越','再','更','比','很','偏','儿']
当然,首先考虑到:比如说“之”这个字,就有很多用法,1 助词 2动词 3代词,而动词是不属于虚词的,但是怎么判断文章中出现的”之“是不是虚词呢?好像得用到语义分析,但是某文智系统都不靠谱:
就算了吧,就简单实现一下对特定的这45个字(词)的每章回出现的频率(频数)进行统计就完事了。
先挖个坑,如果以后有时间的话,研究研究文本的特征挖掘,写个[下](估计是不太可能了)。
首先,读入txt文件。[该txt中一行代表一个段落]
file=open('redmansiondream.txt',encoding="ansi") file_context=file.readlines()
这样就把txt中的信息存到列表file_context里了,每个内容代表一个段落中所有的文字。
其次,使用正则表达式分割file_context,本代码采用的是判断”第XXX回“出现的段号,即file_context中内容为"第XXX回"的下标,并把每回的开始段号和结束段号存储在二维数组中。
(ps:习惯了C和mat的for循环 python的for不太习惯 所以循环都是用的while)
t=0 #第t回 #提取每个章回的开始结束段号 while i<len(file_context): key=re.compile(u'第[\u4e00-\u9fa5]{0,6}回') #正则表达式查找字符串‘第X回’ 其中X长度为[0,6] pa=re.findall(key,file_context[i]) if (len(pa)==1) & (file_context[i][0]=='第')& (len(file_context[i])<30):#因内容中有提及之前章回之嫌,判断字符串是否位于段首 t=t+1 index[t-1][0]=i#开始段号 if t!=1: index[t-2][1]=i #结束段号 i=i+1 index[119][1]=len(file_context)-2 #最后一回段号
这样,我们就得出了每一回的起始段号和结束段号,就相当于实现了”分割“。
之后,对于每一回,分别计算出45个指定的字出现的次数,并储存在二维数组中。
data1 储存频率,data2储存频数
i=0 while i<120: start=index[i][0]+1 end=index[i][1] num=0;#统计该章回总共字数 回车符不计 while start<end:#统计该章回每一段的词频 num=num+len(file_context[int(start)])-1; start=start+1 c=0 while c<len(cell): data2[i][c]=data2[i][c]+len(re.findall(cell[c],file_context[int(start)])) #匹配得到的次数累加 c=c+1 #得出的结果 比如 '罢'的次数中包括'罢咧'的次数 应减减去 k1=0 while k1<len(cell): k2=0 while k2<len(cell): if k1!=k2: if (cell[k1] in cell[k2])==True: #判断是否是子串 如果是 前者的值变为前者的值减后者的值 data2[i][k1]=data2[i][k1]-data2[i][k2] k2=k2+1 k1=k1+1 #求频率 k=0 while k<len(cell): data1[i][k]=data2[i][k]*1000/num; #数值太小 统一乘以1000 k=k+1; i=i+1
从而得出频率和频数。
首先使用K-means算法进行聚类:
自己写的k-means(有点长乱,可以略过):
#2_means-----(频数版) k1=np.zeros(46) k2=np.zeros(46) i=0 #随机初始化2类均值 直接指定第11段为一类 101段为2类 while i<len(cell): k1[i]=data2[10][i] k2[i]=data2[100][i] i=i+1 classi=np.zeros(120) #每一回章所属于的类别 取值 1或 2 while True: ret=1 #判断是否退出循环的条件 d=0 while d<len(data2): c=0 dis1=0 dis2=0 while c<len(cell): dis1+=(data2[d][c]-k1[c])*(data2[d][c]-k1[c]) dis2+=(data2[d][c]-k2[c])*(data2[d][c]-k2[c]) c=c+1 dis1=math.sqrt(dis1) dis2=math.sqrt(dis2) class_old=classi[d] if dis1<=dis2: classi[d]=1 else: classi[d]=2 if class_old!=classi[d]: ret=0 d=d+1 #重新计算两类均值 先清0 num1=0 num2=0 com=0 i=0 while i<len(cell): k1[i]=0 k2[i]=0 i=i+1 while com<len(classi): if classi[com]==1: num1+=1 cop=0 while cop<len(k1): k1[cop]+=data2[com][cop]; cop+=1 else: num2+=1 cop=0 while cop<len(k1): k2[cop]+=data2[com][cop]; cop+=1 com+=1 cop=0 while cop<len(cell): k1[cop]=math.sqrt(k1[cop]) k2[cop]=math.sqrt(k2[cop]) cop+=1 if ret==1: break i=0
from sklearn.cluster import KMeans model=KMeans(n_clusters=2) s=model.fit(data2) print(model.labels_)
最后两者分别得出的结果如下:
总体看来这两个类分布的错落有致,无法区分(我就说怎么可能靠谱)
试试用决策树分类的效果:令前10个为1类(label 0),后10个为2类(label 1),对中间100回进行预测:
from sklearn.ensemble import RandomForestRegressor X_train=np.concatenate((data2[:20],data2[100:]),0) Y_train=np.array([0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,]) rf=RandomForestRegressor(46) rf.fit(X_train,Y_train) X_test=np.concatenate((data2[10:50],data2[50:110]),0) Y_test=rf.predict(X_test) i=0 while i<len(Y_test): Y_test[i]=round(Y_test[i]) i=i+1结果如下:
貌似效果和K-means差不多。
然后再试试层次聚类的效果:
import scipy.cluster.hierarchy as sch import matplotlib.pylab as plt disMat = sch.distance.pdist(data2,'euclidean') Z=sch.linkage(disMat,method='average') P=sch.dendrogram(Z) plt.savefig('C:/Users/71405/Desktop/大数据分析/14/plot_dendrogram.png')
结果:
(啥都看不清。。。。)
就这样了,效果很差,以后有时间研究一下特征提取,来代替虚词词频。
完整的越到后越懒得写注释的代码:
# -*- coding: utf-8 -*- """ Created on Thu May 31 08:24:07 2018 @author: Type真是太帅了 """ import re import numpy as np import math data1=np.zeros((120,46)) #储存120回中的46个虚词的出现频率 data2=np.zeros((120,46)) #储存120回中的46个虚词的出现频数 index=np.zeros((120,2)) #统计每回开始的段号和结束的段号(均不包括) cell=['之','其','或','亦','方','于','即','皆','因','仍','故','尚','乃','呀','吗','咧','罢咧','啊','罢','罢了','么','呢','了','的','着','一','不','把','让','向','往','是','在','别','好','可','便','就','但','越','再','更','比','很','偏','儿'] file=open('redmansiondream.txt',encoding="ansi") file_context=file.readlines() #把所有内容读入到file_context里 i=1 t=0 #第t回 #提取每个章回的开始结束段号 while i<len(file_context): key=re.compile(u'第[\u4e00-\u9fa5]{0,6}回') #正则表达式查找字符串‘第X回’ 其中X长度为[0,6] pa=re.findall(key,file_context[i]) if (len(pa)==1) & (file_context[i][0]=='第')& (len(file_context[i])<30):#因内容中有提及之前章回之嫌,判断字符串是否位于段首 t=t+1 index[t-1][0]=i#开始段号 if t!=1: index[t-2][1]=i #结束段号 i=i+1 index[119][1]=len(file_context)-2 #最后一回段号 i=0 while i<120: start=index[i][0]+1 end=index[i][1] num=0;#统计该章回总共字数 回车符不计 while start<end:#统计该章回每一段的词频 num=num+len(file_context[int(start)])-1; start=start+1 c=0 while c<len(cell): data2[i][c]=data2[i][c]+len(re.findall(cell[c],file_context[int(start)])) #匹配得到的次数累加 c=c+1 #得出的结果 比如 '罢'的次数中包括'罢咧'的次数 应减减去 k1=0 while k1<len(cell): k2=0 while k2<len(cell): if k1!=k2: if (cell[k1] in cell[k2])==True: #判断是否是子串 如果是 前者的值变为前者的值减后者的值 data2[i][k1]=data2[i][k1]-data2[i][k2] k2=k2+1 k1=k1+1 #求频率 k=0 while k<len(cell): data1[i][k]=data2[i][k]*1000/num; #数值太小 统一乘以1000 k=k+1; i=i+1 #2_means-----(频数版) k1=np.zeros(46) k2=np.zeros(46) i=0 #随机初始化2类均值 while i<len(cell): k1[i]=data2[10][i] k2[i]=data2[100][i] i=i+1 classi=np.zeros(120) #每一回章所属于的类别 取值 1或 2 while True: ret=1 #判断是否退出循环的条件 d=0 while d<len(data2): c=0 dis1=0 dis2=0 while c<len(cell): dis1+=(data2[d][c]-k1[c])*(data2[d][c]-k1[c]) dis2+=(data2[d][c]-k2[c])*(data2[d][c]-k2[c]) c=c+1 dis1=math.sqrt(dis1) dis2=math.sqrt(dis2) class_old=classi[d] if dis1<=dis2: classi[d]=1 else: classi[d]=2 if class_old!=classi[d]: ret=0 d=d+1 #重新计算两类均值 先清0 num1=0 num2=0 com=0 i=0 while i<len(cell): k1[i]=0 k2[i]=0 i=i+1 while com<len(classi): if classi[com]==1: num1+=1 cop=0 while cop<len(k1): k1[cop]+=data2[com][cop]; cop+=1 else: num2+=1 cop=0 while cop<len(k1): k2[cop]+=data2[com][cop]; cop+=1 com+=1 cop=0 while cop<len(cell): k1[cop]=math.sqrt(k1[cop]) k2[cop]=math.sqrt(k2[cop]) cop+=1 if ret==1: break i=0 from sklearn.cluster import KMeans model=KMeans(n_clusters=2) s=model.fit(data2) print(model.labels_) from sklearn.ensemble import RandomForestRegressor X_train=np.concatenate((data2[:20],data2[100:]),0) Y_train=np.array([0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,]) rf=RandomForestRegressor(46) rf.fit(X_train,Y_train) X_test=np.concatenate((data2[10:50],data2[50:110]),0) Y_test=rf.predict(X_test) i=0 while i<len(Y_test): Y_test[i]=round(Y_test[i]) i=i+1 import scipy.cluster.hierarchy as sch import matplotlib.pylab as plt disMat = sch.distance.pdist(data2,'euclidean') Z=sch.linkage(disMat,method='average') P=sch.dendrogram(Z) plt.savefig('C:/Users/71405/Desktop/大数据分析/14/plot_dendrogram.png')