中文自然语言处理(NLP)(三)运用python jieba模块计算知识点当中关键词的词频
续上次的随笔之后,我已经基本掌握了jieba和xlrd的基本操作,现在开始运用这两个模块对数据进行一些处理。
情景大概如下:excel文件当中存有一些题目,这些题目标注了其所属的知识点,现在需要获取的数据有:每一道题目当中的关键词(通过分词获得);这些关键词在各个知识点当中出现的概率;每个知识点当中这些关键词出现的百分比,这三项内容。
第一项目标数据只要对各个题目进行分词操作,再对停用词表人工地调整一下即可,主要在于获取后两项百分比。
第一次考虑到这个问题,由于对python不够熟悉,我想到用字典(dict)这种数据结构来存储数据,也就是以关键词作为字典名,每一个知识点作为该字典的一个键值,这个关键词在知识点所属的题目当中出现一次就将字典当中相应键值的数值加1,这样就获得了一组以所有出现过的关键词为名的字典,再将字典当中的频率除以总次数就可以得到概率了。但是通过这个字典组,能够得到的只是某一个关键词在各个知识点当中出现的概率,要想得到某个知识点当中,各个关键词出现的概率,还需要建立另外的一个以知识点为名的字典组,这是非常繁琐的。而且假设有m个关键词,n个知识点的话,这种方法就要占用2*m*n的空间。
下面贴的代码是建立以关键词为名的字典组的代码,写的很垃圾所以很冗长,没什么时间就不用看了,大致流程就是:提取所有需要分词的文本、同时提取所有的知识点>将这些文本进行分词以获取所有出现的关键词>将这些关键词、知识点去重,获得了所有关键词和知识点的索引>for循环遍历这些关键词来构建词典。同样的流程再将另外的词典组构建出来即可。
1 def striped(excel_path): 2 3 #在excel当中已经借助excel自带的排序功能将数据按照三级知识点进行了排序,意思是数据是分片连续的 4 #这里先暂时只做单一三级知识点的题目 5 excel_file=xlrd.open_workbook(excel_path)#为xlrd库设置工作路径 6 sheet=excel_file.sheet_by_index(0)#这里选取excel文件当中的sheet1:教辅题目作为样本 7 countRow=sheet.nrows#获取总行数 8 text_num=1#题干对应第2列,编号应当为1 9 knowledge_num=11#三级知识点对应第十列,编号列应当为9 10 #先为各个知识点创建一个列表 11 knowledge_points=[] 12 stop_list=set_stop_dict_txt('D:/+大创/数学题库/stop_words1.txt') 13 #先加载停用词表 14 #第一个for循环实际上是为了获取所有的单知识点索引 15 all_txt='D:/+大创/数学题库/demo2/all_text.txt' 16 for x in range(1,countRow): 17 listrow=sheet.row(x) 18 knowedge_point=listrow[knowledge_num].value 19 divi=knowedge_point.split('^.^') 20 if len(divi)==1 and divi[0] not in knowledge_points: 21 knowledge_points.append(divi[0]) 22 #将单知识点添加进去,条件限制为:单知识点并且是第一次出现,防止重复之后还要进行去重复操作 23 all_file=open('D:/+大创/数学题库/demo2/all_text.txt','w',encoding='utf-8') 24 for i in range(1,countRow): 25 listrow=sheet.row(i) 26 knowedge_point=listrow[knowledge_num].value 27 divi=knowedge_point.split('^.^') 28 if len(divi)==1: 29 que_data=listrow[1].value 30 ss='%s'%que_data 31 all_file.write(ss+'\n') 32 all_file.close() 33 read_txt=open('D:/+大创/数学题库/demo2/all_text.txt',encoding='utf-8').read() 34 all_words_with_attr=[(x.word,x.flag)for x in psg.cut(read_txt) if x.flag=='n' and x.word not in stop_list] 35 num_of_words=len(all_words_with_attr) 36 word_list=[] 37 word_is_apprared={} 38 word_apprared_num={} 39 for x in all_words_with_attr: 40 word_is_apprared[x]=False 41 word_apprared_num[x]=0 42 for x in all_words_with_attr: 43 if word_is_apprared[x]==False: 44 word_is_apprared[x]=True 45 word_apprared_num[x]=word_apprared_num[x]+1 46 word_list.append(x) 47 else: 48 word_apprared_num[x]=word_apprared_num[x]+1 49 #现在词典word_apprared_num当中存储的就是每一个词语的出现次数,列表word_list当中存储的就是所有的词语 50 51 52 appeared={}#创建字典来记录知识点是否出现过,方便之后统计知识点出现的次数 53 num_of_points={}#创建字典来记录知识点出现的次数 54 has_appeared={} 55 num_of_all={}#存储每一个词在所有分词结果当中的出现次数 56 for x in knowledge_points: 57 appeared[x]=False#初始值赋为假 58 num_of_points[x]=0#出现次数初值赋为0 59 has_appeared[x]=False#这个字典创建的目的是方便之后的题干提取工作 60 for i in range(1,countRow): 61 listrow=sheet.row(i) 62 point=listrow[knowledge_num].value 63 point_divi=point.split('^.^') 64 if len(point_divi)==1: 65 if appeared[point_divi[0]]==False: 66 appeared[point_divi[0]]=True 67 num_of_points[point_divi[0]]=num_of_points[point_divi[0]]+1 68 #出现状况设置为真,出现次数自加 69 else: 70 #已经出现过一次,出现次数自加就完事了 71 num_of_points[point_divi[0]]=num_of_points[point_divi[0]]+1 72 point_texts={} 73 for i in range(1,countRow): 74 list_row=sheet.row(i) 75 know_point=list_row[knowledge_num].value 76 if know_point in knowledge_points: 77 #这个知识点是单知识点中的一个 78 if has_appeared[know_point]==False: 79 #说明这个知识点是第一次出现 80 has_appeared[know_point]=True 81 texts_list=[]#创建一个列表来存储题干信息 82 text=list_row[text_num].value#将对应的题干单元格的内容提取出来 83 texts_list.append(text)#将文本存入list当中 84 point_texts[know_point]=texts_list#以这个文本为相应的知识点创建字典词条,一个知识点对应一个列表,之后加入进来的题干就在list里面添加 85 #向下一行的知识点进行检测,看它是否是一个新的,没出现过的知识点 86 #但是还是要注意我们现在要的是单一的知识点 87 i=i+1 88 list_row=sheet.row(i) 89 next_point=list_row[knowledge_num].value 90 next_divi=next_point.split('^.^') 91 if len(next_divi)==1: 92 while has_appeared[next_point]==True: 93 texts_list.append(list_row[text_num].value) 94 #这时是出现过的状况,将这一行对应的题干也要添加进去 95 #接下来继续向下一行进行检测 96 i=i+1 97 list_row=sheet.row(i) 98 next_point=list_row[knowledge_num].value 99 next_divi=next_point.split('^.^') 100 if len(next_divi)!=1: 101 break#这就相当于是检测到了另外的知识点,不再循环下去 102 with open('D:/+大创/数学题库/demo2/striped.txt','w+',encoding='utf-8')as f: 103 for x in knowledge_points: 104 f.write('{0}:{1}\n'.format(x,point_texts[x])) 105 f.close() 106 #现在已经将同一个知识点下的题干信息保存在txt文件当中了,格式为:"知识点:题干+题干+。。。" 107 #接下来进行分词 108 #考虑到txt文件一行的最大长度为1024个字,但是有的题干信息过长,就会导致折行,用readlines()方法就会有不必要的麻烦 109 #所以这里分词的文本获取就使用写文件之前的词典,生成的txt文件仅做展示和代码检测用 110 point_words={} 111 words_num_of_point={} 112 result_path='D:\+大创\数学题库\demo2\striped_with_fre.txt' 113 result_path_big='D:\+大创\数学题库\demo2\striped_with_fre_big.txt' 114 f=open(result_path_big,'w+',encoding='utf-8') 115 for point in knowledge_points: 116 text_to_cut=point_texts[point]#这是这个知识点应当被分词的文本 117 str_to_cut1=[]#将列表类型转换为str类型而设置的空列表 118 for sen in text_to_cut: 119 str_sen=str(sen) 120 str_to_cut1.append(str_sen) 121 str_to_cut="".join(str_to_cut1) 122 cut_text=[(x.word,x.flag)for x in psg.cut(str_to_cut) if (x.flag=='n' or x.flag=='l') and x.word not in stop_list] 123 cut_list=list(cut_text) 124 #在分词结果当中仅仅使用词性为名词的词语,并且这个词本身不在停用词表当中 125 point_words[point]=cut_text 126 words_num_of_point[point]=len(cut_list)#jieba.cut()返回的是一个generator,这里将其转化为list方便统计长度(也就是知识点的关键词总数) 127 #这样就可以将所有的分词结果按知识点存入词典当中 128 #格式为:[('词1','词性'),('词2','n')...] 129 #但是这里的分词结果当中还是有重复的,还是要进行去重和计数工作 130 word_apprared={}#创建词典存储知识点的词汇的出现情况 131 word_num={}#这个词典中存储的是词在这个知识点的次数 132 word_frequency={}#创建词典存储词汇的出现频率,计算公式为:词汇在这个知识点中的出现次数/这个知识点的词汇总数 133 words_only=[] 134 for word in cut_text: 135 word_apprared[word[0]]=False 136 word_num[word[0]]=0 137 for word in cut_text: 138 if word_apprared[word[0]]==False: 139 #这是这个词语第一次出现 140 word_apprared[word[0]]=True 141 word_num[word[0]]=word_num[word[0]]+1 142 words_only.append(word[0]) 143 else: 144 #这个词语不是第一次出现 145 word_num[word[0]]=word_num[word[0]]+1 146 words_with_fre=[] 147 for x in words_only: 148 pair=[] 149 pair.append(x) 150 fre=word_num[x]/len(cut_list) 151 pair.append(fre) 152 words_with_fre.append(str(pair)) 153 #f.write(point+':'+x+','+fre+'\n') 154 #print(point+':'+','.join(words_with_fre)) 155 f.write(point+':'+','.join(words_with_fre)+'\n') 156 f.close()
这次的随笔当中涉及的方法不是最好的,下次把改进过的方法写出来(也很麻烦就是了),由于学艺不精,写的代码很繁琐,如果有不同的看法的话,还请大家不吝赐教。