4.2 英文分词及词性标注

与中文分词相比,英文分词要简单很多。在英文中,单词之间是以空格作为自然分界符组成语句,语句之间再利用标点分隔组成大篇幅文本,所以我们可以简单的利用标点进行分句处理,利用空格进行分词处理。设计分句函数的思路很简单,英文文本中出现的标点一般为逗号“,”、句点“.”和问号“?”,假设我们有一段英文文本,可以先将文本按照句点分割成若干小段的文本,再将各小段文本按照内部出现的逗号或者问号再次切分。基于这样的思路,我们即使不利用外部的工具包,也能够通过调用 Python 中的内置函数来构建一个简单的英文分词器。

例 1 按标点分句


In [1]:def sentence_split(sentence): # 编写分句函数
           text_sen = []
           for s in sentence.split('.'):
               if '?' in s:
                   tsxt_sen.extend(s.split('?'))
               elif ',' in s:
                   tsxt_sen.extend(s.split(','))
               else:
                   text_sen.append(s)
            return tsxt_sen
In [2]:text="text minming with python,TEXT MINING WITH PYTHON.Text Mining?With Python."
       sentence_split(text)
Out[2]:['text minming with python',
        'TEXT MINING WITH PYTHON',
        'Text Mining',
        'With Python',
        '']

例 2 按空格分词


In [3]:text_word=[]
       for i in sentence_split(text):
           if i != "":
               text_word.append(i.split(" "))
In [4]:text_word
Out[4]:[['text', 'minming', 'with', 'python'],
        ['TEXT', 'MINING', 'WITH', 'PYTHON'],
        ['Text', 'Mining'],
        ['With', 'Python']]

对于上例中结构比较简单的文本,可以自行编写函数进行处理,但是对于结构、内容更加复杂的英文文本,类似的操作就很难进行全面、细致的处理了,如对下例所示语句进行分句处理:

例 3


In [5]:text= "Good muffins cost $3.88\nin New York.  Please buy me two of them.\nThanks."
       sentence_split(text)
Out[5]:['Good muffins cost $3',
        '88\nin New York',
        '  Please buy me two of them',
        '\nThanks',
        '']

由于句中存在“$3.88”、“\n”等特殊表述,直接用自定义函数进行分句,并不能达到理想效果,本节将介绍几个英文分词工具,利用这些工具可以自动、快速实现分词处理,无需自行编写复杂的分句、分词函数,进而将更多的注意力集中在更加复杂的文本数据分析过程。

4.2.1 NLTK

作为基于 Python 的自然语言处理前沿平台, NLTK 为我们提供了一套更为专业的英文分词工具,相比于调用 Python 的内置函数, NLTK的英文分词工具模式更加丰富,并且在去除停用词、词干化处理方面更为优秀。

4.2.1.1 tokenize 分词包

tokenize 是 NLTK 的分词包,其中的函数可以识别英文词汇和标点符号对文本进行分句或分词处理。

(1) sent_tokenize

sent_tokenize 为 tokenize 分词包中的分句函数,返回文本的分句结果,调用方式为:sent_tokenize(text, language='english')

参数说明:

text:需要分句处理的文本

language:nltk.tokenize.punkt 中包含了很多预先训练好的分词模型,参数 language 即为模型的名称

例 1


In [1]:import nltk
       from nltk.tokenize import sent_tokenize
       text= "Good muffins cost $3.88\nin New York.  Please buy me two of them.\nThanks."
In [2]:sent_tokenize(text)
Out[2]:['Good muffins cost $3.88\nin New York.',
        'Please buy me two of them.',
        'Thanks.']

(2)word_tokenize

word_tokenize 为 tokenize 分词包中的分词函数,返回文本的分词结果,调用方式为:word_tokenize(text, language='english')

参数说明:

text: 需要分词处理的文本

language: nltk.tokenize.punkt 中包含了很多预先训练好的分词模型,参数 language 即为模型的名称

例 2


In [3]:from nltk.tokenize import word_tokenize
       word_tokenize(text)
Out[3]:['Good',
        'muffins',
         'cost',
         '$',
         '3.88',
         'in',
         'New',
         'York',
         '.',
         'Please',
         'buy',
         'me',
         'two',
         'of',
         'them',
         '.',
         'Thanks',
         '.']
In [4]:[word_tokenize(t) for t in sent_tokenize(text)]
Out[4]:[['Good', 'muffins', 'cost', '$', '3.88', 'in', 'New', 'York', '.'],
        ['Please', 'buy', 'me', 'two', 'of', 'them', '.'],
        ['Thanks', '.']]

(3)regexp 模块:正则表达式分词

对于包含比较复杂词型(如 $10、10%)的字符串,以上的分词算法往往不能实现精确分割,此时需要借助正则表达式来完成分词任务。所谓正则表达式(Regular Expression),就是一个描述指定的规则的字符序列,可以用来检查一个字符串是否与该规则匹配,用正则表达式分词就是按照正则表达式指定的规则对字符串进行分割的过程,可以根据实际情况自行编写正则表达式。正则表达式的相关内容本书不做特别的介绍,感兴趣的读者可以参看深度阅读部分推荐的读物学习。NLTK 提供了 regexp 模块支持正则表达式分词,其中包括 regexp_tokenize 、wordpunct_tokenize、blankline_tokenize 等正则分词函数。

(3.1)RegexpTokenizer 类

RegexpTokenizer 是 regexp 模块下一个类,可以自行定义正则表达式进行分词,调用该类下的分词方法需要先实例化,实例化方式为:实例=RegexpTokenizer(pattern, gaps, discard_empty, flags)

参数说明:

pattern:必填参数,构建分词器的模式,即正则表达式字符串

gaps:可选参数,设置为 True 时,正则表达式指定识别标识符之间的间隔,默认缺失值为 False,即正则表达式用来识别标识符本身

discard_empty:可选参数,设置为 True 时,去除任何由分词器产生的空符“"”,只有当参数“gaps”取值为“True”时分词器才会差生空符

flags:可选参数,编译分词器模式的正则标识,默认使用的是 re.UNICODE | re.MULTILINE | re.DOTALL

实例化后即可利用该类下的分词方法进行分词处理,分词方法调用方式为:实例.tokenize(text)

例 3


In [5]:from nltk.tokenize import RegexpTokenizer
       text="The four-poster canopy bed made in U.S.A. costs $600. The seller stake out 40% of the profit."
       tokenizer = RegexpTokenizer('\w+|\$[\d\.]+|\S+')
       "/".join(tokenizer.tokenize(text))
Out[5]:'The/four/-poster/canopy/bed/made/in/U/.S.A./costs/$600./The/seller/stake/out/40/%/of/the/profit/.'

也可以直接调用函数 regexp_tokenize 实现同样的分词效果,调用方式为:regexp_tokenize(text, pattern, gaps=False, discard_empty=True, flags=56)

参数说明:

text:必填参数,需要分词的字符串

pattern:必填参数,构建分词器的模式,即正则表达式字符串

gaps:可选参数,设置为 True 时,正则表达式指定识别标识符之间的间隔,默认缺失值为 False,即正则表达式用来识别标识符本身

discard_empty:可选参数,设置为 True 时,去除任何由分词器产生的空符“"”,只有当参数“gaps”取值为“True”时分词器才会差生空符

flags:可选参数,编译分词器模式的正则标识,默认使用的是 re.UNICODE | re.MULTILINE | re.DOTALL

例 4


In [6]:from nltk.tokenize import regexp_tokenize
In [7]:"/".join(word_tokenize(text))
Out[7]:'The/four-poster/canopy/bed/made/in/U.S.A./costs/$/600/./The/seller/stake/out/40/%/of/the/profit/.'
In [8]:pattern = r"""(?x)                   # 设置以编写较长的正则条件
                     (?:[A-Z]\.)+           # 缩略词 
                     |\$?\d+(?:\.\d+)?%?    # 货币、百分数
                     |\w+(?:[-']\w+)*       # 用连字符链接的词汇
                     |\.\.\.                # 省略符号 
                     |(?:[.,;"'?():-_`])    # 特殊含义字符 
                  """  
In [9]:"/".join(regexp_tokenize(text,pattern))
Out[9]:'The/four-poster/canopy/bed/made/in/U.S.A./costs/$600/./The/seller/stake/out/40%/of/the/profit/.'

从上例中两个分词函数返回的结果可以看出,利用正则表达式进行分词可以更加有针对性的解决待分词语句中有特殊格式的词汇。

(3.2)RegexpTokenizer 子类:用预先定义好的正则表达式分词

(a)WhitespaceTokenizer

WhitespaceTokenizer 子类,可以直接将字符串按照空格(包括space, tab, newline)分词,效果相当于利用字符串 split 方法。调用方式为:实例.tokenize(text)

例 5


In [10]:from nltk.tokenize import WhitespaceTokenizer
        text="The four-poster canopy bed made in U.S.A. costs $600.\nThe seller stake out 40% of the profit." # 第二句换行
In [11]:print text
        The four-poster canopy bed made in U.S.A. costs $600.
        The seller stake out 40% of the profit.
In [12]:tokenizer_space=WhitespaceTokenizer()
        "/".join(tokenizer_space.tokenize(text))
Out[12]:'The/four-poster/canopy/bed/made/in/U.S.A./costs/$600./The/seller/stake/out/40%/of/the/profit.'

(b)WordPunctTokenizer

WordPunctTokenizer 子类,用正则表达式 “`\w+|\w\s+” 将字符串切分成字母和非字母字符,分词方法调用方式为:实例.tokenize(text)

例 6


In [13]:from nltk.tokenize import WordPunctTokenizer
        tokenizer_punct=WordPunctTokenizer()
        "/".join(tokenizer_punct.tokenize(text))
Out[13]:'The/four/-/poster/canopy/bed/made/in/U/./S/./A/./costs/$/600/./The/seller/stake/out/40/%/of/the/profit/.'

也可以直接调用函数 wordpunct_tokenize 实现同样的分词效果,调用方式为:wordpunct_tokenize(text)

例 7


In [14]:from nltk.tokenize import wordpunct_tokenize
        "/".join(wordpunct_tokenize(text))
Out[14]:'The/four/-/poster/canopy/bed/made/in/U/./S/./A/./costs/$/600/./The/seller/stake/out/40/%/of/the/profit/.'

(c)BlanklineTokenizer

BlanklineTokenizer 子类, 将空行作为分隔符进行分词,空行是指不包含任何字符的行,空格 space 和制表符 tab 除外,相应的正则表达式为:'\s\n\s\n\s*'。分词方法调用方式为:实例.tokenize(text)

例 8


In [15]:from nltk.tokenize import BlanklineTokenizer
        text="The four-poster canopy bed made in U.S.A. costs $600.\n\nThe seller stake out 40% of the profit." # 第二句与第一句间空一行
In [16]:print text
        The four-poster canopy bed made in U.S.A. costs $600.

        The seller stake out 40% of the profit.    
In [17]:tokenizer_blank = BlanklineTokenizer()
        tokenizer_blank.tokenize(text)
Out[17]:['The four-poster canopy bed made in U.S.A. costs $600.',
         'The seller stake out 40% of the profit.']

也可以直接调用函数 blankline_tokenize 实现同样的分词效果,调用方式为:blankline_tokenize(text)

例 9


In [18]:from nltk.tokenize import blankline_tokenize
        blankline_tokenize(text)
Out[18]:['The four-poster canopy bed made in U.S.A. costs $600.',
         'The seller stake out 40% of the profit.']

(4)stanford 模块

tokenize 包中的 stanford 模块是 NLTK 的 Stanford Tokenizer 接口,模块中定义的 StanfordTokenizer 类提供了利用分词工具 PTBTokenizer 进行分词的方法,调用方式为:实例.tokenize(text)

例 10


In [19]:from nltk.tokenize import StanfordTokenizer
        tokenizer_stan = StanfordTokenizer()
        tokenizer_stan.tokenize("Good muffins cost $3.88\nin New York.  Please buy me\ntwo of them.\nThanks.")
Out[19]:['Good', 'muffins', 'cost', '$', '3.88', 'in', 'New', 'York', '.', 'Please', 'buy', 'me', 'two', 'of', 'them', '.', 'Thanks', '.']

(5)sexpr 模块

tokenize 包中的 sexpr 模块是用来识别字符串中的带括号的表示形式,特别之处在于,该模块可以将字符串同时按照空格和括号切分。可以通过该模块下的 SExprTokenizer 类的方法实现括号表示的切分,首先将类实例化:实例 = SExprTokenizer( parens='()', strict=True)

参数说明:

parens:设置识别的括号形式,默认为()

strict:若括号表示不完全(如只包含半个括号)则会返回错误提示信息,参数 strict 取值为 False 时,则会进行正常切分处理,切分时将不完全的括号当做字符处理,默认缺失值为 True

实例方法调用方式为:实例.tokenize(text)

例 11


In [20]:from nltk.tokenize import SExprTokenizer
        tokenizer_sexpr=SExprTokenizer()
        tokenizer_sexpr.tokenize("ab(c,d)((e f) g) h")
Out[20]:['ab', '(c,d)', '((e f) g)', ' h']
In [21]:tokenizer_sexpr.tokenize("a b)(c d e f g h")
        ValueError: Un-matched close paren at char 13
In [22]:tokenizer_sexpr=SExprTokenizer(strict=False)
        tokenizer_sexpr.tokenize("a b)(c d e f g h")
Out[22]:['a', 'b', ')', '(c d e f g h']
In [23]:tokenizer_sexpr.tokenize("ab{c,d}{{e f} g} h")
Out[23]:['ab{c,d}{{e f} g} h']
In [24]:tokenizer_sexpr=SExprTokenizer(parens="{}")
        tokenizer_sexpr.tokenize("ab{c,d}{{e f} g} h")
Out[24]:['ab', '{c,d}', '{{e f} g}', ' h']

也可以直接调用函数 sexpr_tokenize 达到相同的切分效果,调用方式为:sexpr_tokenize(text)

例 12


In [25]:from nltk.tokenize import sexpr_tokenize
        sexpr_tokenize('(a b (c d)) e f (g)')
Out[25]:['ab', '(c,d)', '((e f) g)', ' h']

(6)util 模块

util 模块提供了几个可以返回分词结果在原语句起始位置的函数,包括:regexp_span_tokenize、string_span_tokenize 等。

(6.1)regexp_span_tokenize

regexp_span_tokenize 函数按照给定的正则表达式对字符串进行分词处理,并返回分词后各个词的起始位置,形式如 (start, end) 的元组。调用方式为:regexp_span_tokenize(text,regexp)

参数说明:

text:需要分词的字符串

regexp:规定分割标识的正则表达式,不能为空

例 13


In [26]:from nltk.tokenize.util import regexp_span_tokenize
        text="Good muffins cost $3.88\nin New York.  Please buy me\ntwo of them.\nThanks."
        list(regexp_span_tokenize(text, r'\s'))
Out[26]:[(0, 4),(5, 12),(13, 17),(18, 23),(24, 26),(27, 30),(31, 36),(38, 44),(45, 48),(49, 51),(52, 55),(56, 58),(59, 64),(65, 72)]

(6.2)string_span_tokenize

string_span_tokenize 函数按照给定的分隔符进行分词,并返回分词后各个词的起始位置,形式如 (start, end) 的元组。调用方式为:regexp_span_tokenize(text,sep)

参数说明:

text:需要分词的字符串

sep:分隔符,即分词依据

例 14


In [27]:from nltk.tokenize.util import string_span_tokenize
list(string_span_tokenize(text, " "))
Out[27]:[(0, 4),(5, 12),(13, 17),(18, 26),(27, 30),(31, 36),(37, 37),(38, 44),(45, 48),(49, 55),(56, 58),(59, 72)]

4.2.1.2 去除停用词

文本经过简单的而分词处理后,还会包含大量的无实际意义的通用词,需要过滤掉,NLTK 提供了一份英文停用词词典供使用者直接使用,可以通过以下方式查看停用词词典:

例 15


In [28]:from nltk.corpus import stopwords
        english_stopwords = stopwords.words("english")
        print english_stopwords[0:10]
        [u'i', u'me', u'my', u'myself', u'we', u'our', u'ours', u'ourselves', u'you', u'your']
In [29]:len(english_stopwords)
Out[29]:153
In [30]:for i in word_tokenize("Everything is OK. I can do it all by myself."):
            if i not in english_stopwords:
                print i
        Everything
        OK
        .
        I
        .

从上面的结果可以看出,对于停用词典中未涵盖的大写停用词和标点并未过滤掉,这就要求在去除停用词前进行小写化处理,后续再过滤掉多余的标点符号。

例 16


In [31]:english_punctuations = [',', '.', ':', ';', '?', '(', ')', '[', ']', '!', '@', '#', '%', '$', '*'] # 自定义英文表单符号列表
        for i in word_tokenize("Everything is OK. I can do it all by myself."):
            if i.lower() not in english_stopwords: # 过滤停用词
                if i not in english_punctuations: # 过滤标点符号
                    print i
        Everything
        OK

4.2.1.3 词干化处理

词干化处理( Stemming ) 就是去除形态词缀得到对应词根的过程,是英文特有的处理过程,比如说同一个英文单词有单数复数的变形(如 apple 和 apples)、ing 和 ed 等时态的变形(doing 和 did)、人称代词不同谓语的变形等(like 和likes),这些词虽然形式上有细微差别,但是都对应着相同的词根,在某些情况下应该当做相同的词处理(比如计算相关性),这就需要进行词干化处理。

NLTK 的 stem 包提供了几个相关模块进行词干化处理,包括 Lancaster Stemmer, Porter Stemmer (词干化处理有三大主流算法:Porter Stemming、Lovins stemmer 和 Lancaster Stemming)。

(1)lancaster 模块

lancaster 模块是基于 Lancaster Stemming 算法的词干分析模块,该模块下定义了 LancasterStemmer 类,通过调用该类下的 stem 方法可以实现英文词汇的词干化处理,调用方式为:实例.stem(word)

例 17


In [32]:from nltk.stem.lancaster import LancasterStemmer
        st1 = LancasterStemmer()
        words=['fishing', 'crying', 'likes', 'meant', 'owed','was', 'did', 'done', 'women',"avaliable"]
        for word in words:
            print word,st1.stem(word)
        fishing fish
        crying cry
        likes lik
        meant meant
        owed ow
        was was
        did did
        done don
        women wom
        avaliable avaly

(2)porter 模块

porter 模块是基于 Porter Stemming 算法的词干分析模块,该模块下定义了 PorterStemmer 类,通过调用该类下的 stem 方法可以实现英文词汇的词干化处理,调用方式为:实例.stem(word)

例 18


In [33]:from nltk.stem.porter import PorterStemmer
        st2 = PorterStemmer()
        for word in words:
            print word,st2.stem(word),st1.stem(word)
        fishing fish fish
        crying cri cry
        likes like lik
        meant meant meant
        owed owe ow
        was wa was
        did did did
        done done don
        women women wom
        avaliable avali avaly

(3)regexp 模块

regexp 模块是基于正则表达式识别词缀模式进行词干处理的模块,该模块下定义了 RegexpStemmer 类,通过调用该类下的 stem 方法可以实现英文词汇的词干化处理,实例化方式为:实例= RegexpStemmer(regexp, min=0)

参数说明:

regexp:用来识别词缀的正则表达式

min:需要词干化处理词汇的最小长度,默认缺失值为 0

stem 方法调用方式为:实例.stem(word)

例 19


In [34]:from nltk.stem.regexp import RegexpStemmer
        st3 = RegexpStemmer('ing$|s$|e$|able$', min=4)
        for word in words:
            print word,st3.stem(word),st2.stem(word),st1.stem(word)
        fishing fish fish fish
        crying cry cri cry
        likes like like lik
        meant meant meant meant
        owed owed owe ow
        was was wa was
        did did did did
        done don done don
        women women women wom
        avaliable avali avali avaly

4.2.1.4 NLTK 提供的语料库

(1)下载语料库

nltk.download() 是 Downloader 类下的一个方法,可以用于下载 NLTK 语料和相关的包,利用该方法下载 NLTK 语料的步骤如下:

In [1]:nltk.download()

@todo 插入下载截图 4.2.1.4-1

在 “Downloader>”后输入框输入“d”,回车确认后在下方出现新的 Identifier 输入框:

@todo 插入截图 4.2.1.4-2

此时可以直接输入需要下载的包的名称,也可以输入“l”查看所有包的列表:

@todo 插入截图 4.2.1.4-3

在 Identifier 输入框输入“book”,回车确认下载 NLTK 图书语料库合集

@todo 插入截图 4.2.1.4-4

在 Identifier 输入框输入“all-corpora”,回车确认下载 NLTK 全部语料库

(2)查看下载的语料

执行 nltk.download() 命令后,在“Downloader>”后输入框输入“l”,回车确认后可以查看已经下载的包:

@todo 插入截图 4.2.1.4-5

在上图 list 的第九行可以看到已经下载的 Brown 语料,继续回车可以继续向下查看。对于已经下载的语料库,可以通过以下方式查看:


In [2]:from nltk.corpus import brown
       print brown.readme() # readme 方法可以返回语料库内容
       BROWN CORPUS

       A Standard Corpus of Present-Day Edited American
       English, for use with Digital Computers.

       by W. N. Francis and H. Kucera (1964)
       Department of Linguistics, Brown University
       Providence, Rhode Island, USA

       Revised 1971, Revised and Amplified 1979

       http://www.hit.uib.no/icame/brown/bcm.html

       Distributed with the permission of the copyright holder,
       redistribution permitted.

In [3]:from nltk.book import *
       *** Introductory Examples for the NLTK Book ***
       Loading text1, ..., text9 and sent1, ..., sent9
       Type the name of the text or sentence to view it.
       Type: 'texts()' or 'sents()' to list the materials.
       text1: Moby Dick by Herman Melville 1851
       text2: Sense and Sensibility by Jane Austen 1811
       text3: The Book of Genesis
       text4: Inaugural Address Corpus
       text5: Chat Corpus
       text6: Monty Python and the Holy Grail
       text7: Wall Street Journal
       text8: Personals Corpus
       text9: The Man Who Was Thursday by G . K . Chesterton 1908

以上执行结果列举了几个 NLTK Book 的示例,输入语料名称即可直接查看语料:

In [4]:text1 Out[4]:

text1 属于 Text 类,在 Text 类中,NLTK 提供了一些常用的文本搜索方法:

(2.1)collocations

collocations 方法用于搜索 Text 文本中的去除停用词后的固定搭配词组,可以自主设置固定搭配的限制条件,调用方式为:Text.collocations(num=20, window_size=2)

参数说明:

num:输出搭配词组个数的最大值,默认缺失值为 20

window_size:形成固定搭配词组中的单词之间可以间隔的距离,即间隔超过该参数值的单词就不能形成词组,默认缺失值为 2 ,也是可以设置的最小值


In [5]:text1.collocations(num=10, window_size=2)
       Sperm Whale; Moby Dick; White Whale; old man; Captain Ahab; sperm
whale; Right Whale; Captain Peleg; New Bedford; Cape Horn

(2.2)common_contexts

common_contexts 方法可以查找给定单词的上下文,并给出最相似的频繁出现的上下文结构,调用方式为:Text.common_contexts(words, num=20)

参数说明:

words:用于做相似检索的单词,多个单词需放在同一个列表中并用逗号间隔

num:允许生成的词汇数,默认缺失值为 20


In [6]:text1.common_contexts(["very","great"],num=10) #查找使用"very"或"great"的相同结构的上下文
       the_body a_white a_long

(2.3)concordance

concordance 方法可以搜索文本中指定词语出现的位置,输出词语所在上下文,调用方式为:Text.concordance(word, width=79, lines=25)

参数说明:

word:需要检索的词语

width:输出的上下文的长度,默认缺失值为 79

lines:输出的上下文的行数,默认缺失值为 25


In [7]:text1.concordance("very",lines=5)
       Displaying 5 of 322 matches:
        horse - whales , which had bones of very great value for their teeth , of whi
       n inward bruise ." -- KING HENRY . " Very like a whale ." -- HAMLET . " Which 
       itself ," said Mr . Webster , " is a very striking and peculiar portion of the
       egree , some time or other , cherish very nearly the same feelings towards the
       o eat and sleep meanwhile . It was a very dubious - looking , nay , a very dar

(2.4)count

count 方法用于统计文中某个单词出现的次数,调用方式为:Text.count(word),参数 “word" 即为需要统计的单词。


In [8]:text1.count("very")
Out[8]:311

(2.5)similar

similar 方法用于查找与指定单词有相同使用语境的单词,会先列出相似度最高的单词,调用方式为:Text.similar(word, num=20),参数 num 设置生成的单词数。


In [8]:text1.similar("very")
       so a same the but last first too and as in pretty only other white that is strange now entire

(2.6)findall

findall 方法用于找出文中符合指定正则表达式形式为文本内容,正则表达式中的符号需用尖角括号(<>)括起,调用方式为:Text.findall(regexp)


In [9]:text1.findall("<a>(<.*>)<man>")
       monied; nervous; dangerous; white; white; white; pious; queer; good;
       mature; white; Cape; great; wise; wise; butterless; white; fiendish;
       pale; furious; better; certain; complete; dismasted; younger; brave;
       brave; brave; brave

对于不属于 NLTK 文本库的其他字符串,可以利用 Text() 函数将其转换为 Text 对象,转换为 Text 对象后上述方法都可以用于字符串的分析。

4.2.1.5 tag 词性标注包

tag 包除定义了一些词性标注的类外,还提供了部分词性标注的接口。它定义的几个词性标注器均以分词结果列表作为输入,对应返回每一个分词结果的词性,多数标注器都是根据训练语料构建的,比如一元语法模型(unigram )词性标注器,对于给出的词汇,该标注器会在训练语料中查找每个词汇出现最多的词性并对其进行相应的标注,对于训练集中不存在的词汇,其词性会被标注为“None”。

(1)词性标注函数 pos_tag

函数 pos_tag 是利用 NLTK 推荐的词性标注器对指定词汇列表进行词性标注的函数,词性标注结果以列表形式返回,列表元素为词汇和对应词性构成的元组。调用方式为:pos_tag(tokens, tagset=None)

参数说明:

tokens:需要进行词性标注的词汇列表

tagset:使用的词性标记集。同一词性可以有不同的标注词,利用该参数可以进行标注词的规约,例如 universal 标记集,其标注词及含义如下表所示:

@todo 插入标注词含义表

例 1


In [1]:from nltk.tag import pos_tag
       from nltk.tokenize import word_tokenize
       pos_tag(word_tokenize("Good muffins cost $3.88\nin New York.  Please buy me\ntwo of them.\nThanks."))
Out[1]:[('Good', 'JJ'),('muffins', 'NNS'),('cost', 'VBP'),('$', '$'),('3.88', 'CD'),('in', 'IN'),('New', 'NNP'),('York', 'NNP'),('.', '.'),('Please', 'NNP'),('buy', 'VB'),('me', 'PRP'),('two', 'CD'),('of', 'IN'),('them', 'PRP'),('.', '.'),('Thanks', 'NNS'),('.', '.')]
In [2]:pos_tag(word_tokenize("Good muffins cost $3.88\nin New York.  Please buy me\ntwo of them.\nThanks."), tagset='universal')
Out[2]:[('Good', u'ADJ'),('muffins', u'NOUN'),('cost', u'VERB'),('$', u'.'),('3.88', u'NUM'),('in', u'ADP'),('New', u'NOUN'),('York', u'NOUN'),('.', u'.'),('Please', u'NOUN'),('buy', u'VERB'),('me', u'PRON'),('two', u'NUM'),('of', u'ADP'),('them', u'PRON'),('.', u'.'),('Thanks', u'NOUN'),('.', u'.')]

词性标注的一个比较重要的应用领域就是智能朗读,即将文本转换为语音,如果一个词汇同时有多种词性并且有不同的发音,那么准确识别词性对于语音准换是至关重要的,如下例展示的情况。

例 2


In [3]:pos_tag(word_tokenize("They desert the treasure in the desert."), tagset='universal')
Out[3]:[('They', u'PRON'),('desert', u'VERB'),('the', u'DET'),('treasure', u'NOUN'),('in', u'ADP'),('the', u'DET'),('desert', u'NOUN'),('.', u'.')]

语句"They desert the treasure in the desert."中,词汇“desert”出现了两次,但是两次的词性都不相同,前一次是动词,发音为“dɪˈzɜːt”,后一次是名词,发音为“ˈdɛzət”。利用词性标注函数 pos_tag 可以正确识别该词的词性,如果需要转换为语音的话就不会出现读音错误。

(2)词性标注函数 pos_tag_sents

函数 pos_tag_sents 是利用 NLTK 推荐的词性标注器对指定语句列表进行词性标注的函数,每一个语句都由词汇列表构成。调用方式为:pos_tag_sents(sentences, tagset=None)

参数说明:

sentences:需要进行词性标注的语句列表,每一个语句都由词汇列表构成

tagset:使用的词性标记集,如 universal, wsj, brown 等

例 3


In [4]:from nltk.tag import pos_tag_sents
       from nltk.tokenize import sent_tokenize
       sents="Good muffins cost $3.88\nin New York.  Please buy me\ntwo of them.\nThanks."
       pos_tag_sents([word_tokenize(i) for i in sent_tokenize(sents)]) #利用 sent_tokenize 进行分句后再通过 word_tokenize 进行分词,得到分词后的语句列表
Out[4]:[[('Good', 'JJ'),('muffins', 'NNS'),('cost', 'VBP'),('$', '$'),('3.88', 'CD'),('in', 'IN'),('New', 'NNP'),('York', 'NNP'),('.', '.')],
        [('Please', 'NNP'),('buy', 'VB'),('me', 'PRP'),('two', 'CD'),('of', 'IN'),('them', 'PRP'),('.', '.')],
        [('Thanks', 'NNS'), ('.', '.')]]       
In [5]:pos_tag_sents([word_tokenize(i) for i in sent_tokenize(sents)], tagset='universal')
Out[5]:[[('Good', u'ADJ'),('muffins', u'NOUN'),('cost', u'VERB'),('$', u'.'),('3.88', u'NUM'),('in', u'ADP'),('New', u'NOUN'),('York', u'NOUN'),('.', u'.')],
        [('Please', u'NOUN'),('buy', u'VERB'),('me', u'PRON'),('two', u'NUM'),('of', u'ADP'),('them', u'PRON'),('.', u'.')],
        [('Thanks', u'NOUN'), ('.', u'.')]]

(3)StanfordPOSTagger 类

stanford 是 tag 包中提供 Stanford 标注器接口的模块,StanfordPOSTagger 是该模块下定义的词性标注类。要使用该模块,需要事先下载标注模型,下载网址为:http://nlp.stanford.edu/software。该类实例化方式为:实例 = StanfordPOSTagger(model,path,encoding="UTF-8")

参数说明:

model:基于训练集的模型

path:可选参数,Stanford 标注器文件路径

encoding:可选参数,训练集编码,默认缺失值为 "UTF-8"

实例化后可以调用 tag 方法进行词性标注,调用方式为:实例.tag(tokens),其中参数 tokens 即为需要词性标注的词汇列表。

例 4


In [6]:from nltk.tag import StanfordPOSTagger
       st = StanfordPOSTagger('english-bidirectional-distsim.tagger')
       st.tag('What is the airspeed of an unladen swallow ?'.split())
Out[6]:[('What', 'WP'), ('is', 'VBZ'), ('the', 'DT'), ('airspeed', 'NN'), ('of', 'IN'), ('an', 'DT'), ('unladen', 'JJ'), ('swallow', 'VB'), ('?', '.')]

此外,tag 包还提供了多个词性标注模块,包括:基于统计词性标注器 TnT 的模块 tnt、基于转换规则的词性标注模块 brill、基于 CRFSuite 的词性标注模块 crf 等,想进一步学习使用相关模块可以参见 4.4 节提供的深度阅读材料。

(4)读取已标注语料

在 NLTK 提供的部分语料中已经标注了词性,使用 tagged_words 方法即可查看已经标注的词性。

例 5


In [7]:nltk.corpus.brown.tagged_words(tagset='universal')
Out[7]:[(u'The', u'DET'), (u'Fulton', u'NOUN'), ...]

4.2.1.6 亚马逊英文评论语料分析实例

(1)数据抓取

抓取亚马逊美国站上一款鞋子(https://www.amazon.com/FAYALE-Driving-Cowhide-Leather-Lace-Up/dp/B01ASME6VI) 的评论语料作为分析实例,共抓取评论文本 187 条,保存为 csv 格式文件 shoes_review.csv,并上传到 Jupyter Notebook 文件列表,新建一个 Python notebook,读取文本数据,并将数据保存到列表 corpus 中:


In [1]:import csv
       corpus=[]
       with open("shoes_review.csv") as f:
          reader=csv.reader(f)
          for i in reader:
              corpus.append(i)

查看 corpus 内元素形式:


In [2]:corpus[0:3]
Out[2]:[['id','product_id','author_name','author_id','helpful','rating','has_purchased','summary','content','published_at','created_at','updated_at','meta'],
 ['R3BANSXTDHC16U','B017D4ORMM','mari','A1SOIEM6ZWUK9B','','4','TRUE','great shoes',
  'Great color …… trendy style.','2016-07-16 00:00:00','2016-07-19 15:47:56','2016-07-19 15:47:56','{"Size": "6 B(M) US", "Color": "Wine Red", "variation_attribute": "Size: 6 B(M) US|Color: Wine Red"}'],
 ['R37EXA4Y7A32CS','B017D4ORMM','JR Taylor','A1QHRBBD0O63F1','','2','TRUE','no return information and feel like an 8',"I wear  …… at all.",'2016-07-12 00:00:00','2016-07-19 15:47:56','2016-07-19 15:47:56','{"Size": "9 B(M) US", "Color": "White", "variation_attribute": "Size: 9 B(M) US|Color: White"}']]

可以看到 corpus 第一个列表元素即为 csv 文件的第一行,即抓取的字段,包括:id、product_id、author_name、author_id、helpful、rating、has_purchased、summary、content、published_at、created_at、updated_at、meta,分别代表评论id、商品id、评论者姓名、评论者id、评论是否有帮助、评论得分、是否真实购买、评论摘要、评论内容、评论发布时间、评论创建时间、评论更新时间、商品属性,利用这些丰富的信息可以得到很有价值的分析结论。

(2)分词处理

(2.1)提取评论文本

将商品所属子类 id 为 3 的商品评论文本保存到列表 review 中:


In [3]:review=[]
       with open("shoes_review.csv") as f:
           reader=csv.reader(f)
           reader.next() # 跳过第一行字段内容
           for i in reader:
               review.append(i[8].decode('utf-8'))#以文件保存格式对内容进行解码,获得unicode字符串
In [4]:len(review) # 查看评论数
Out[4]:187
In [5]:for i in review[25:28]:
           print i + "\n"
       They fit very comfortably. I am a nurse working 12 hour shifts and these are very easy on my feet. They are as close to barefoot and still wear shoes. No arch support, but great for wide feet.

       would buy another pair! Very comfortable!!!

       Wore it 2 times and the stitching started coming undone.

(2.2)分句

对提取的英文评论文本进行分句处理,使用的是 sent_tokenize 函数,分句的结果保存在列表 sent 中。


In [6]:import nltk
       from nltk.tokenize import sent_tokenize
       sent=[]
       for i in review:
           sent.append(sent_tokenize(i))
In [7]:sent[0:3]
Out[7]:[[u'Great color, wear well but bought a size larger as recommended, but should have bought my reg.',
         u'shoe size.',
         u'Very comfortable but had to put in some inner soles to make it fit better.',
         u'Would like to try another color but will buy my size.',
         u'Many compliments on this trendy style.'],
        [u'I wear a nine- period.',
         u'I read the reviews and ordered my exact size.',
         u'The shoes arrived with no order form, no return information and feel like an 8.',
         u'I will never be able to wear them.',
         u"FYI, they are as cute in person as online, but I'm stuck with a shoe I can't wear...at all."],
        [u'Very comfortable']]

从分句的结果可以看出,每一条评论文本都被分割成若干句,且保存在一个列表中。

(2.3)分词

对以上分句的结果进一步做分词处理,这里使用最常用的分词函数 word_tokenize,分词的结果保存在列表 words 中。


In [8]:from nltk.tokenize import word_tokenize
       words=[]
       for i in sent:
           for j in i:
               words.extend(word_tokenize(j))
In [9]:words[0:3]
Out[9]:[u'Great', u'color', u',']

(2.4)小写处理

分词结果中有部分大写的字母,为了提高后续去除停用词、语意分词等过程的处理、分析效果,需要先进性小写处理,直接利用字符串的 lower 方法即可,将处理结果保存在列表 words_lower 中。


In [10]:words_lower=[i.lower() for i in words]
        words_lower[0:3]
Out[10]:[u'great', u'color', u',']

(2.5)去除标点符号和停用词

分词结果列表中还存在大量的标点符号和停用词,利用自定义标点符号列表以及 NLTK 提供的停用词典进行过滤,过滤后的分词结果保存在 words_clear 列表中。


In [10]:from nltk.corpus import stopwords
        english_stopwords = stopwords.words("english")
        english_punctuations = [',', '.', ':', ';', '?', '(', ')', '[', ']', '!', '@', '#', '%', '$', '*', '...'] # 自定义英文表单符号列表
        words_clear=[]
        for i in words_lower:
            if i not in english_stopwords: # 过滤停用词
                if i not in english_punctuations: # 过滤标点符号
                    words_clear.append(i)
In [11]:print "/".join(words_clear[0:10])
        great/color/wear/well/bought/size/larger/recommended/bought/reg

(2.6)词干化处理

利用 porter 模块即 Porter Stemming 算法进一步进行词干化处理,将词干化后的结果保存在列表 words_stem 中。


In [12]:from nltk.stem.porter import PorterStemmer
        st = PorterStemmer()
        words_stem=[st.stem(word) for word in words_clear]

(2.7)简单的统计汇总

经过以上几步的处理,可以初步得到一份比较清爽的分词结果,读者可以根据实际需求选择合适的分词、词干化等方法,并且可以在此结果基础上进一步过滤掉较短的单词、纠正拼写错误的单词等等,下面仅利用分词的结果做几个简单的统计汇总。

利用函数 Text() 将分词结果转换为 Text 格式,名称为 word_text


In [13]:from nltk.text import Text
        word_text=Text(words_stem)

识别评论文本中常用固定词组搭配:


In [14]:word_text.collocations(num=20, window_size=2)
        arch support; love shoe; true size; mani compliment; differ color;
        read review; fit perfect; super comfort; everi color; wide feet; dark
        blue; well made; second pair; appreci fayal; dissip quickli; skirt
        short; someon said; extrem comfort; car around; skinni jean

结果显示,出现次数最多的词组为 arch support (足弓垫),此外 fit perfect、super comfort、well made、extrem comfort 等经常出现的好评词组显示了评论者较高的评价。

利用 Counter 计数器统计出现次数最多的前 20 个单词:


In [15]:from collections import Counter
        words_counter=Counter(words_stem)
        words_counter.most_common(20)
Out[15]:[(u'shoe', 152),(u'comfort', 93),(u'love', 63),(u'color', 47),(u'fit', 43),(u'wear', 41),(u'order', 40),(u'size', 39),(u'cute', 37),(u'pair', 35),(u"n't", 32),(u'like', 31),(u'look', 28),(u'great', 23),(u'day', 22),(u'feet', 22),(u'realli', 22),(u'buy', 22),(u'comfi', 20),(u'one', 19)]

计数结果显示,出现次数最多的词是“shoe”,共出现了 152 次,其次是 comfort、love、color 等。通过查看高频词上下文相关内容,可以了解评论的具体内容:


In [16]:word_text.concordance("comfort",lines=10)
        Displaying 10 of 93 matches:
                                            comfort put inner sole make fit better woul
        son onlin 'm stuck shoe ca n't wear comfort 've gotten plenti compliment shoe c
        t 've gotten plenti compliment shoe comfort wear day wear true 9 long toe found
        orter toe hit end rather wide still comfort bought arch insert arch support com
        ort bought arch insert arch support comfort shoe bought love help prevent feet 
        rch shoe meet qualif worth tri cute comfort shoe hesit order review state right
        shoe imposs keep side shoe lace tie comfort fit great rub even lot walk love mu
        t skinni jean etc feel like slipper comfort love first pair black order order p
        pair packag came felt like noth bag comfort order differ style color ca n't bea
        ld back case return love hope order comfort love size fit perfect need buy diff

如果想直接查看原始评论文本,可以通过索引查看。

(3)词性标注


In [17]:from nltk.tag import pos_tag
        pos_tag(words_stem,tagset='universal')
Out[17]:[(u'great', u'ADJ'),
         (u'color', u'NOUN'),
         (u'wear', u'NOUN'),
         ...]

筛选出形容词和名词,分别保存在列表 ADJ 和 NOUN 中:


In [18]:ADJ=[]
        NOUN=[]
        for a,b in pos_tag(words_stem,tagset='universal'):
            if b=="ADJ":
                ADJ.append(a)
            elif b=="NOUN":
                NOUN.append(a)
In [19]:len(ADJ) # 查看形容词个数
Out[19]:523
In [20]:len(NOUN) # 查看名词个数
Out[20]:1203

查看出现次数较多的形容词和名词:


In [21]:c1=Counter(ADJ)
        for i in c1.most_common(10):
            print i[0],i[1]
        fit 39
        cute 27
        great 23
        super 13
        nice 13
        perfect 11
        big 10
        red 10
        right 9
        much 9
In [22]:c2=Counter(NOUN)
        for i in c2.most_common(10):
            print i[0],i[1]
        shoe 131
        comfort 88
        color 42
        order 40
        size 39
        love 33
        pair 32        
        day 22
        feet 22
        comfi 19

从词性标注统计结果可以初步推断,购买者最常用来描述商品的形容词是“fit”,即“合脚”,此外“cute"、"great"等词出现的次数也比较多,除了名词“shoe”以外,出现次数较多的名词有“comfort”、“color”、“size”等,可以初步推断购买者更加关注鞋子的舒适度、颜色以及尺寸。

4.2.2 Pattern

Pattern 工具库中的 en 模块提供了英文词性标注、情感分析、动名词变换等工具,其中 Parser 类及相关方法可以实现英文分词和词性标注处理,使用的是一个含有 100000 个词汇及对应词性的词典,对于词典中未包含的词汇,根据词汇后缀及上下文词汇进行判断,准确率在 95% 左右,对于用词不规范的情境下准确率会更低。词性简写对照表可以参见官方文档(http://www.clips.ua.ac.be/pages/mbsp-tags)。

4.2.2.1 tag 函数

通过调用函数 tag 可以直接 将英文语句切分并以元组形式返回词语及词语的词性标注结果,调用方式如下:tag(string, tokenize=True, encoding='utf-8', **kwargs)

参数说明:

string:待处理英文语句

tokenize:在分词时是否将标点符号和单词分开

encoding:所输入的英文语句的编码

例 1


In [1]:import pattern
       from pattern.en import tag
       tag("I eat *pizza !with a fork.",tokenize=False)
Out[1]:[(u'I', u'PRP'),
        (u'eat', u'VBP'),
        (u'*pizza', u'NN'),
        (u'!with', u'IN'),
        (u'a', u'DT'),
        (u'fork.', u'NN')]
In [2]:tag("I eat *pizza !with a fork.")
Out[2]:[(u'I', u'PRP'),
        (u'eat', u'VBP'),
        (u'*', u'SYM'),
        (u'pizza', u'NN'),
        (u'!', u'.'),
        (u'with', u'IN'),
        (u'a', u'DT'),
        (u'fork', u'NN'),
        (u'.', u'.')]

4.2.2.3 parse 函数

通过调用函数 parse 可以直接 将英文语句切分并返回词性标注结果,除此之外还可以识别句子成分、进行词干化处理等等。调用方式如下:parse(string, tokenize=True, tags=True, chunks=True, relations=False, lemmata=False, encoding='utf-8', **kwargs)

参数说明:

string:待处理英文语句

tokenize:在分词时是否将标点符号和单词分开

tags:是否进行词性标注

chunks:是否切分词组

relations:是否识别句子成分,如主语、宾语

lemmata:是否进行词干处理

encoding:所输入的英文语句的编码

例 2


In [3]:from pattern.en import parse
       parse('I eat pizza with a fork.')
Out[3]:u'I/PRP/B-NP/O eat/VBP/B-VP/O pizza/NN/B-NP/O with/IN/B-PP/B-PNP a/DT/B-NP/I-PNP fork/NN/I-NP/I-PNP ././O/O'

输出结果为字符串形式,每个词的分析结果用空格分开,以“I”为例,结果“I/PRP”中,第一项“PRP”表示词性“pronoun, personal”,即人称代词,其余表示词组类型。Pattern 提供了 pprint 函数美化以上输出结果。


In [4]:from pattern.en import pprint
       pprint(parse('I eat pizza with a fork.',relations=True,lemmata=True))

          WORD   TAG    CHUNK   ROLE   ID     PNP    LEMMA   

             I   PRP    NP      SBJ    1      -      i       
           eat   VBP    VP      -      1      -      eat     
         pizza   NN     NP      OBJ    1      -      pizza   
          with   IN     PP      -      -      PNP    with    
             a   DT     NP      -      -      PNP    a       
          fork   NN     NP ^    -      -      PNP    fork    
             .   .      -       -      -      -      .

results matching ""

    No results matching ""