在Python中完全复制R的文本预处理
我想用Python来处理一堆文档,就像我在R语言中做的那样。比如说,给我一个初始的文档集合,叫做corpus
,我希望最后得到一个处理过的文档集合,这个集合的结果和下面的R代码生成的结果是一样的:
library(tm)
library(SnowballC)
corpus = tm_map(corpus, tolower)
corpus = tm_map(corpus, removePunctuation)
corpus = tm_map(corpus, removeWords, c("myword", stopwords("english")))
corpus = tm_map(corpus, stemDocument)
在Python中有没有简单或者直接的方法来做到这一点?有没有办法确保结果完全相同?
举个例子,我想把下面这段话处理成:
@Apple耳机真是太棒了!这是我用过的最好听的入耳式耳机!
处理后的结果应该是:
耳机 棒 最好听 入耳式耳机 我用过的
2 个回答
1
CountVectorizer
和 TfidfVectorizer
可以根据需要进行自定义,具体的说明可以在 文档 中找到。特别是,你可能需要编写一个自定义的分词器,这个分词器是一个函数,它接收一篇文档并返回一个词汇列表。可以使用 NLTK 来实现:
import nltk.corpus.stopwords
import nltk.stem
def smart_tokenizer(doc):
doc = doc.lower()
doc = re.findall(r'\w+', doc, re.UNICODE)
return [nltk.stem.PorterStemmer().stem(term)
for term in doc
if term not in nltk.corpus.stopwords.words('english')]
示例:
>>> v = CountVectorizer(tokenizer=smart_tokenizer)
>>> v.fit_transform([doc]).toarray()
array([[1, 1, 1, 2, 1, 1, 1, 1, 1]])
>>> from pprint import pprint
>>> pprint(v.vocabulary_)
{u'amaz': 0,
u'appl': 1,
u'best': 2,
u'ear': 3,
u'ever': 4,
u'headphon': 5,
u'pod': 6,
u'sound': 7,
u've': 8}
(我链接的例子实际上使用了一个类来缓存词形还原器,但使用函数也可以。)
3
在处理数据的预处理步骤上,nltk
和tm
之间要做到完全一致似乎有点棘手。所以我觉得最好的办法是用rpy2
在R中进行预处理,然后把结果拉到Python里来:
import rpy2.robjects as ro
preproc = [x[0] for x in ro.r('''
tweets = read.csv("tweets.csv", stringsAsFactors=FALSE)
library(tm)
library(SnowballC)
corpus = Corpus(VectorSource(tweets$Tweet))
corpus = tm_map(corpus, tolower)
corpus = tm_map(corpus, removePunctuation)
corpus = tm_map(corpus, removeWords, c("apple", stopwords("english")))
corpus = tm_map(corpus, stemDocument)''')]
接着,你可以把它加载到scikit-learn
中——为了让CountVectorizer
和DocumentTermMatrix
之间的数据匹配,你只需要去掉长度小于3的词:
from sklearn.feature_extraction.text import CountVectorizer
def mytokenizer(x):
return [y for y in x.split() if len(y) > 2]
# Full document-term matrix
cv = CountVectorizer(tokenizer=mytokenizer)
X = cv.fit_transform(preproc)
X
# <1181x3289 sparse matrix of type '<type 'numpy.int64'>'
# with 8980 stored elements in Compressed Sparse Column format>
# Sparse terms removed
cv2 = CountVectorizer(tokenizer=mytokenizer, min_df=0.005)
X2 = cv2.fit_transform(preproc)
X2
# <1181x309 sparse matrix of type '<type 'numpy.int64'>'
# with 4669 stored elements in Compressed Sparse Column format>
让我们来验证一下这和R的结果是否一致:
tweets = read.csv("tweets.csv", stringsAsFactors=FALSE)
library(tm)
library(SnowballC)
corpus = Corpus(VectorSource(tweets$Tweet))
corpus = tm_map(corpus, tolower)
corpus = tm_map(corpus, removePunctuation)
corpus = tm_map(corpus, removeWords, c("apple", stopwords("english")))
corpus = tm_map(corpus, stemDocument)
dtm = DocumentTermMatrix(corpus)
dtm
# A document-term matrix (1181 documents, 3289 terms)
#
# Non-/sparse entries: 8980/3875329
# Sparsity : 100%
# Maximal term length: 115
# Weighting : term frequency (tf)
sparse = removeSparseTerms(dtm, 0.995)
sparse
# A document-term matrix (1181 documents, 309 terms)
#
# Non-/sparse entries: 4669/360260
# Sparsity : 99%
# Maximal term length: 20
# Weighting : term frequency (tf)
如你所见,现在两个方法存储的元素和词的数量完全一致。