第一次看到老师给的题目的时候,我就很怀疑:通过每个章回虚词出现的频率(频数),就能判别是否是同一作者?
大概出现的虚词:
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[:10],data2[110:]),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[:10],data2[110:]),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')