在Python中完全复制R的文本预处理

6 投票
2 回答
2653 浏览
提问于 2025-04-18 00:55

我想用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

CountVectorizerTfidfVectorizer 可以根据需要进行自定义,具体的说明可以在 文档 中找到。特别是,你可能需要编写一个自定义的分词器,这个分词器是一个函数,它接收一篇文档并返回一个词汇列表。可以使用 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

在处理数据的预处理步骤上,nltktm之间要做到完全一致似乎有点棘手。所以我觉得最好的办法是用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中——为了让CountVectorizerDocumentTermMatrix之间的数据匹配,你只需要去掉长度小于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)

如你所见,现在两个方法存储的元素和词的数量完全一致。

撰写回答