前三次链接:中文自然语言处理(NLP)(一)python jieba模块的初步使用 中文自然语言处理(NLP)(二)python jieba模块的进一步学习和xlrd模块 中文自然语言处理(NLP)(三)运用python jieba模块计算知识点当中关键词的词频
问题叙述见上一次随笔。
上次留下来的问题是如果只使用字典功能来统计词频,会出现需要构建两个字典的问题,会浪费空间。在进行了一段时间的python知识的学习之后,发现可以创建一个二维字典(值为这个词在这个知识点下的题目中出现的次数)来存储数据,这样就可以在一个字典当中查询关键词-知识点和知识点-关键词这两种数据了。
1 def striped(data_path,stop_path,result_path): 2 #三个参数:数据库路径(题干路径)、停用词表路径、函数结果输出路径 3 data_file=xlrd.open_workbook(data_path)#为xlrd库设置工作路径 4 data_sheet=data_file.sheet_by_name('Sheet1') 5 countRow=data_sheet.nrows#获取总行数 6 print(countRow) 7 text_num=1 8 class_one_num=7#代表一级知识点在文档中的列号 9 knowledge_num=9#分别代表题干信息和三级知识点在表中的列号 10 knowledge_code_num=6#代表三级知识点编码所在的列 11 stop_list=set_stop_dict_txt(stop_path) 12 knowledge_points=[]#存储所有的知识点索引 13 point_code_list=[]#存储所有三级知识点的代码索引 14 word_list=[]#存储所有的分出来的词的索引 15 point_to_code={}#存储知识点及其代码的对应关系 16 points_need_more_word=[]#需要进行扩充的知识点列表 17 more_path='D:/+大创/数学题库/demo3/more.txt' 18 points_need_more_word=more_texts(data_path,more_path) 19 #由于数学题库里有多知识点的题目,这些知识点之间是用符号'^.^'分隔开的,所以下面需要将这些单元格里面的内容运用split()方法分隔开来,然后取单知识点的题目 20 for i in range(1,countRow): 21 listrow=data_sheet.row(i) 22 knowledge_point=listrow[knowledge_num].value 23 divi=knowledge_point.split('^.^') 24 if len(divi)==1 and divi[0] not in knowledge_points: 25 knowledge_points.append(divi[0]) 26 knowledge_code=listrow[knowledge_code_num].value 27 point_code_list.append(knowledge_code) 28 point_to_code[divi[0]]=knowledge_code 29 #这个for循环向list当中添加了所有的单知识点和他们对应的三级知识点代码 30 all_que_text=open('D:/+大创/数学题库/demo3/all_text.txt','w',encoding='utf-8') 31 for i in range(1,countRow): 32 listrow=data_sheet.row(i) 33 knowledge_point=listrow[knowledge_num].value 34 divi=knowledge_point.split('^.^') 35 if len(divi)==1: 36 question=listrow[text_num].value 37 ss='%s'%question 38 all_que_text.write(ss+' ') 39 all_que_text.close() 40 read_txt=open('D:/+大创/数学题库/demo3/all_text.txt',encoding='utf-8').read() 41 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] 42 word_is_apprared={}#统计词语是否出现过,值为True/False 43 word_apprared_num={}#统计词语出现的总次数 44 for x in all_words_with_attr: 45 word_is_apprared[x]=False 46 word_apprared_num[x]=0 47 for x in all_words_with_attr: 48 if word_is_apprared[x]==False: 49 word_is_apprared[x]=True 50 word_apprared_num[x]=word_apprared_num[x]+1 51 word_list.append(x) 52 else: 53 word_apprared_num[x]=word_apprared_num[x]+1 54 #要达到建立一个可查询的二维表的目的,必须先获得所有知识点和分词所得到的词语的索引,存储为两个list 55 #现在列表:word_list和knowledge_points当中存储的就是这两个索引 56 word_num={}#存储每一个词语的出现的总次数 57 point_num={}#存储每一个知识点出现的总次数 58 for word in word_list: 59 word_num[word[0]]=0 60 for point in knowledge_points: 61 point_num[point]=0 62 #初始化两个词典为0,在接下来的两个for循环里面插入计数 63 #以这两个列表初始化一个二维字典,来存储词语在知识点中出现的次数 64 with open('D:/+大创/数学题库/demo3/word_list.txt','w+',encoding='utf-8')as f: 65 for word in word_list: 66 words='%s'%word[0] 67 f.write(words+' ') 68 with open('D:/+大创/数学题库/demo3/point_list.txt','w+',encoding='utf-8')as f: 69 for point in knowledge_points: 70 points='%s'%point 71 f.write(points+' ') 72 word_point_sheet={} 73 word_code_sheet={} 74 for word in word_list: 75 word_point_sheet[word[0]]={} 76 word_code_sheet[word[0]]={} 77 for point in knowledge_points: 78 word_point_sheet[word[0]][point]=0 79 word_code_sheet[word[0]][point_to_code[point]]=0 80 #先将这个表格当中的数据初始化为0 81 for i in range(1,countRow): 82 listrow=data_sheet.row(i) 83 knowledge_point=listrow[knowledge_num].value 84 divi=knowledge_point.split('^.^') 85 if len(divi)==1: 86 texts=listrow[text_num].value 87 point=divi[0] 88 text='%s'%texts 89 #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] 90 text_cut=[(x.word,x.flag)for x in psg.cut(text) if x.flag=='n' and x.word not in stop_list] 91 for word in text_cut: 92 if (word not in word_list)==False: 93 word_point_sheet[word[0]][point]=word_point_sheet[word[0]][point]+1 94 word_code_sheet[word[0]][point_to_code[point]]=word_code_sheet[word[0]][point_to_code[point]]+1 95 word_num[word[0]]=word_num[word[0]]+1 96 point_num[point]=point_num[point]+1 97 #接下来就是文件写入部分了,将数据按照格式写入txt文件当中 98 with open(result_path,'w+',encoding='utf-8')as f: 99 for word in word_list: 100 f.write('出现词语:'+word[0]+' 知识点出现情况:') 101 appeare_num=0 102 for point in word_point_sheet[word[0]]: 103 appeare_num=word_point_sheet[word[0]][point]+appeare_num 104 for point in knowledge_points: 105 num=word_point_sheet[word[0]][point] 106 if num!=0: 107 f.write(' %s:%f'%(point_to_code[point],num/appeare_num)) 108 f.write(' ') 109 with open('D:/+大创/数学题库/demo3/final_result2.txt','w+',encoding='utf-8')as f: 110 for point in knowledge_points: 111 f.write('知识点:'+point_to_code[point]+' 出现词语:') 112 appeare_num=0 113 for word in word_list: 114 appeare_num=word_point_sheet[word[0]][point]+appeare_num 115 for word in word_list: 116 num=word_point_sheet[word[0]][point] 117 if num!=0: 118 f.write(' %s:%f'%(word[0],num/appeare_num)) 119 f.write(' ') 120 with open('D:/+大创/数学题库/demo3/final_result3.txt','w+',encoding='utf-8')as f: 121 for point in knowledge_points: 122 f.write('知识点代码:'+point_to_code[point]+' 出现词语:') 123 for word in word_list: 124 num=word_point_sheet[word[0]][point] 125 if num!=0: 126 f.write(' %s:%d'%(word[0],num)) 127 f.write(' ') 128 return word_point_sheet,word_list,knowledge_points
上述代码的主要目的在于建立一个列为出现的关键词,行为所有的三级知识点,数据为关键词在该知识点当中出现的次数的二维字典,通过这个二维字典,我们可以经过一些操作之后获得关键词在知识点当中出现的概率和知识点中含有关键词的概率这两个数据。
上述代码的大致流程为:获取所有出现关键词(需要对全文本进行jieba分词操作)和三级知识点(读取excel文件当中的相应列即可)的索引>按照这两个索引再次遍历excel当中的题目文本,构建上述的二维字典。
这次的解决方案较之上次占用空间为2*m*n的方案,只占用m*n的空间,并且在读取相应的数据时比较方便,但是这个方案仍然不是最好的,问题在于不是每一个关键词都在每一个知识点下属的题目当中出现过,有的关键词只在一个或少数几个知识点下属的题目当中出现,但是按照这种策略,还要给这个关键词单独创建一行来记录,这还是存在空间的浪费。
这次把几个月之前的代码整出来整理一下写随笔,发现自己还是代码写得少,在面对一个问题时,往往不能尽快得出一个比较好的解决方案,并且代码风格很差,虽然当时编写这些代码的时候写了很多注释,但是在几个月之后再去看这些代码的时候,还是要花上十几分钟来再次理解,而且还是存在着将功能全部放在一个一百来行的大方法当中实现的毛病,这些之后都要改正。
这次的代码比上次有所改进,但不是最优,如果有不同的看法,还请不吝赐教。