文本预处理的一般流程&jieba分詞&jieba關鍵詞提取

1.jieba分词

注意:使用jieba以前,數據預處理很重要,清除空值重複值以後,同時也要判斷數據是否為字符串,如果不是字符應該刪除該條數據。

如果读取出现中文乱码可以直接这样打开,python2X3X通用

import codecs
with codecs.open('h.csv', 'rb', 'gb2312') as csvfile:
    for line in csvfile:
        print line

文本預處理的一般流程

#讀取數據
df1 = pd.read_excel('ManMade.xlsx')
#選出文本部分並且把非字符串索引存到列表

#如果只需要去除缺失值的话,有下面两种方法:
#第一种把非空部分另存一列再处理
#df1['data'] = df['content'][df['content'].notnull()]
#第二种用dropna函数以后再重新排序
#df = df.dropna(subset=["content"])#subset代表需要看的列,如果content列中某一行为空,删除该行
#重新排序,drop默认为False则索引列会被还原为普通列,否则会丢失。改为True,删除原先的索引使用新的从0开始的索引
#df = df.reset_index(drop = True)

list0 = df1.content.apply(lambda x: len(x) > 2)
#统计长度小于二的個數並且記錄下他們的索引(因为如果文件是CSV格式,
#所有非空内容读出来都是字符串格式,比如错误的记录导致的空格或者数字用pandas读取出来都是字符串格式,
#例如' '或者'0',用isinstance(x, str)无法分辨,虽然jieba分词只要是字符串格式都能切词,
#但是如果jieba分词来读取,则会把它当成int或者float格式,剔除这种数据,最简单的方法就是去除长度过小的数据,
#但是如果是xlsx格式,0读取出来就是int格式,用len(x)就会报错
#那么就可以先用list1中的isinstance(x, str)来筛选)
list1 = df1.content.apply(lambda x: isinstance(x, str))
#另外:null部分会直接跳过,所以一开始一定要先dropna去掉空白部分)
#統計非字符串的個數並且記錄下他們的索引
count_a = 0
list2 = []
for i in range(len(list1)):
    if list1[i]==False:
        list2.append(i)
        count_a += 1
    else:
        pass
#刪除所有非字符串的部分
df2 = df1.drop(list2,axis=0)
#重新排序
df2.reset_index()
#把列表里的內容存到一個字符串里
list_content = [i for i in df2.content]
content = ''.join(list_content)
print(len(content))
##如果有需要也可以对每个文章只选出他的汉字部分,介绍两种常用方法:
#方法1:
#汉字的编码范围是['\u4e00','\u9fa5'],取出一个切完词的文本列表中的全部汉字可以用范围来限定
list1 = ['一堆','汉字','和','?!#','符号']
X,Y = ['\u4e00','\u9fa5']
list2 = [i for i in list1 if X <= i <= Y and i not in stopwords]
#方法2:
#正则表达式
import  re

test='fuck you! 8婆婊子!要啥bike自行车啊!'

result1=re.findall(u'[\u4e00-\u9fa5]',test)  
print (result1)
print (''.join(result1))
#['婆', '婊', '子', '要', '啥', '自', '行', '车', '啊']
#婆婊子要啥自行车啊
result2=re.findall(r'[0-9]',test)
print (''.join(result2))
#8
result3=re.findall(r'[a-z]',test)
print (''.join(result3))
#fuckyoubike

使用 suggest_freq(segment, tune=True) 可调节单个词语的词频,使其能(或不能)被分出来。

jieba.cut 方法接受三个输入参数: 需要分词的字符串;cut_all 参数用来控制是否采用全模式;HMM 参数用来控制是否使用 HMM 模型。
jieba.cut_for_search 方法接受两个参数:需要分词的字符串;是否使用 HMM 模型。该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细。
待分词的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建议直接输入 GBK 字符串,可能无法预料地错误解码成 UTF-8。
jieba.cut 以及 jieba.cut_for_search 返回的结构都是一个可迭代的 generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode),或者用
jieba.lcut 以及 jieba.lcut_for_search 直接返回 list。
jieba.Tokenizer(dictionary=DEFAULT_DICT) 新建自定义分词器,可用于同时使用不同词典。jieba.dt 为默认分词器,所有全局分词相关函数都是该分词器的映射。

# encoding=utf-8
import jieba

seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list))  # 全模式

seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精确模式

seg_list = jieba.cut("他来到了网易杭研大厦")  # 默认是精确模式
print(", ".join(seg_list))

seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  # 搜索引擎模式
print(", ".join(seg_list))


#结果:
#【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学

#【精确模式】: 我/ 来到/ 北京/ 清华大学

#【新词识别】:他, 来到, 了, 网易, 杭研, 大厦    (此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了)

#【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造

并行分詞

  • 原理:将目标文本按行分隔后,把各行文本分配到多个 Python 进程并行分词,然后归并结果,从而获得分词速度的可观提升

  • 基于 python 自带的 multiprocessing 模块,目前暂不支持 Windows

  • 用法:

    • jieba.enable_parallel(4) # 开启并行分词模式,参数为并行进程数
    • jieba.disable_parallel() # 关闭并行分词模式
  • 例子:https://github.com/fxsjy/jieba/blob/master/test/parallel/test_file.py

  • 实验结果:在 4 核 3.4GHz Linux 机器上,对金庸全集进行精确分词,获得了 1MB/s 的速度,是单进程版的 3.3 倍。

  • 注意:并行分词仅支持默认分词器 jieba.dtjieba.posseg.dt

特点:

  • 支持三种分词模式
    –精确模式,试图将句子最精确地切开,适合文本分析;
    –全模式,把句子中所有的可以成词的词语都扫描出来,速度非常快,但不能解决歧义;
    –搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。
    -支持繁体分词
    -支持自定义词典

算法:

基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG)
采用动态规划查找最大概率路径,找出基于词频的最大切分组合
对于未登录词,采用了基于汉字成词能力的HMM模型,使用了Viterbi算法

添加自定义词典:

开发者可以指定自己自定义的词典,以便包含jieba词库里没有的词。虽然jieba有新词识别能力,但是自行添加新词可以保证更高的正确率
用法:jieba.load_userdict(file_name)#file_name为文件类对象 或自定义词典的路径
词典格式:一个词一行:词语,词频(可省略),词性(可省略),用空格隔开,顺序不可颠倒。UTF-8编码。

2.关键词提取:

基于TF-IDF算法的关键词抽取
import jieba.analyse

  • jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=())
    sentence 为待提取的文本
    topK 为返回几个 TF/IDF 权重最大的关键词,默认值为 20
    withWeight 为是否一并返回关键词权重值,默认值为 False
    allowPOS 仅包括指定词性的词,默认值为空,即不筛选
    常用詞性有:["ns", "n", "vn", "v", "nr"]
    注意:選取單個詞性時結尾要用逗號,
    例如["ns",],如果不加逗號,實際上是按照["n","s"]來取詞的。
    詞性表
  • jieba.analyse.TFIDF(idf_path=None) 新建 TFIDF 实例,idf_path 为 IDF 频率文件

关键词提取所使用逆向文件频率(IDF)文本语料库可以切换成自定义语料库的路径

  • 用法:jieba.analyse.set_idf_path(file_name) file_name为自定义语料库的路径

关键词提取所使用停止词(Stop Words)文本语料库可以切换成自定义语料库的路径

  • 用法: jieba.analyse.set_stop_words(file_name) file_name为自定义语料库的路径

基于TextRank算法的关键词提取

  • jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=(‘ns’, ‘n’, ‘vn’, ‘v’)) 直接使用,接口相同,注意默认过滤词性。
  • jieba.analyse.TextRank() 新建自定义 TextRank 实例
    –基本思想:
    1,将待抽取关键词的文本进行分词
    2,以固定窗口大小(默认为5,通过span属性调整),词之间的共现关系,构建图
    3,计算图中节点的PageRank,注意是无向带权图

下面写出jieba关键词提取方法例子

# -*- coding: utf-8 -*-
#基于词语共现图提取方法
import jieba

#import jieba.posseg as psg
import itertools as it
import pandas as pd
#import json
#from elasticsearch import Elasticsearch
#from pyquery import PyQuery as pq
import time

def wordweight(wc1,wc2,samecounts):
    weight = samecounts/(wc1+wc2-samecounts) # Jaccard係數,交集與並集比值
    return weight

#def wordweight(wc1,wc2,samecounts):  
#   weight = samecounts/(wc1*wc2)  # PMI,互信息
#   return weight

t1 = time.time()
#讀數據&清洗數據
list_content = []
df1 = pd.read_excel('ManMade20.xlsx')
list1 = df1.content.apply(lambda x: isinstance(x, str))
count_a = 0
list2 = []
for i in range(len(list1)):
    if list1[i]==False:
        list2.append(i)
        count_a += 1
    else:
        pass
df2 = df1.drop(list2,axis=0)
df2.reset_index()
list_content = [i for i in df2.content]
content = ''.join(list_content)
print(len(content))
#print(content)
t2 = time.time()
#提取關鍵詞
#1.用jieba自带的提取关键词
#一百篇文章做速度测试
#Running time:615.9178113937378 Seconds
#词性限制集合为["ns", "n", "vn", "v", "nr"],表示只能从词性为地名、名词、动名词、动词、人名这些词性的词中抽取关键词。
allow_pos = ('ns', 'nr', 'n', 'vn', 'v', 'an', 'nz', 'vg')
tags = jieba.analyse.extract_tags(content,topK = 100,allowPOS = allow_pos)  # 基於 TF-IDF 算法的關鍵詞提取
# tags = jieba.analyse.textrank(content,topK=100)  # 基於 TextRank 算法的關鍵字提取

t3 = time.time()

sentence = content.split('\n') # '\n'換行,段落
sentence = [e for e in sentence if e not in ('', ' ')]  # 去出空格之类的噪声
wordraw = []
t4 = time.time()

for i in range(len(sentence)):
    #words = [e.word for e in psg.cut(sentence[i]) if e.flag.startswith(('n','a','v'))]
    words = jieba.lcut(sentence[i])
    cleanwords = [e for e in words if e in tags]
    wordraw.append(cleanwords)
wordlist = [list(set(x)) for x in wordraw]  # 文章段落同個詞可能出現多次(詞頻統計基本單位:段落)
#map(lambda x: list(set(x)), wordlist)  #去除第二層list裡面重複值
wordlist = [x for x in wordlist if len(x) > 1]
#filter(lambda x: len(x) > 1, wordlist)  #過濾掉空格和len1的字符串
#把3個以上的詞拆分進行兩兩組合
wordlist1 = [list(it.combinations(x,2)) for x in wordlist]
#map(lambda x: list(it.combinations(x,2)), wordlist)
#拆分組合中的list加到大list中
wordcorpus = [wordlist1[i][j] for i in range(len(wordlist1)) for j in range(len(wordlist1[i]))]
wordcorpus = [sorted(x) for x in wordcorpus]
#print(len(wordcorpus))
#map(lambda x: sorted(x), wordcorpus) #對list名詞進行排序
#wordcorpus = sorted(wordcorpus) #對list中元組進行排序
#wordcorpus = [tuple(x) for x in wordcorpus]
#map(lambda x: tuple(x), wordcorpus)#對名詞進行tuple轉化
#corpus = list(set(wordcorpus)) #排重得到新list,方便后面計算count
keywords={}
#countwords = jieba.lcut(content)
countwords = []

t5 = time.time()

for i in wordraw:
    countwords.extend(i)
    
t6 = time.time()

for i in tags:
    keywords[i] = countwords.count(i)
source = []
target = []
weight = []

t7 = time.time()
for j in range(len(wordcorpus)):
    source.append(list(wordcorpus[j])[0])
    target.append(list(wordcorpus[j])[1])
    weight.append(wordweight(keywords[wordcorpus[j][0]],keywords[wordcorpus[j][1]],wordcorpus.count(wordcorpus[j])))
df={'source':source,'target':target,'weight':weight}
frame = pd.DataFrame(df,columns=['source','target','weight'])
sourceFreq = []
targetFreq = []

t8 = time.time()
for i in range(len(frame)):
    sourceFreq.append(keywords[frame['source'][i]])
    targetFreq.append(keywords[frame['target'][i]])
frame.insert(3,'sourceFreq',sourceFreq)
frame.insert(4,'targetFreq',targetFreq)
jsondata = frame.to_dict(orient='records')
t9 = time.time()

print(jsondata)
print('Running time1: %s Seconds'%(t2-t1))
print('Running time2: %s Seconds'%(t3-t2))
print('Running time3: %s Seconds'%(t4-t3))
print('Running time4: %s Seconds'%(t5-t4))
print('Running time5: %s Seconds'%(t6-t5))
print('Running time6: %s Seconds'%(t7-t6))
print('Running time7: %s Seconds'%(t8-t7))
print('Running time8: %s Seconds'%(t9-t8))
print('Running time0: %s Seconds'%(t9-t1))