如何让scikit-learn的CountVectorizer以标点分隔分词且不含标点?
解决CountVectorizer跨标点生成n-gram的问题
你遇到的问题是默认CountVectorizer的行为导致的:它会先剥离标点,再将剩余单词视为连续序列,所以"data. format"会被处理成["data", "format"],进而生成二元ngram"data format"。要实现以标点为分隔符、不包含标点,同时阻止跨标点的n-gram,可以用以下两种方法解决:
方法1:拆分分句独立生成ngram
先按标点把文本拆分成独立分句,每个分句单独生成ngram,这样跨标点的词不会被组合:
import re import pandas as pd from sklearn.feature_extraction.text import CountVectorizer from sklearn.preprocessing import MultiLabelBinarizer c = ["data. format", "data are format hello world"] def extract_ngrams(text, ngram_range=(1,2)): # 按非单词/空白字符分割分句,过滤空内容 clauses = re.split(r'[^\w\s]', text) clauses = [clause.strip() for clause in clauses if clause.strip()] ngrams = [] # 对每个分句单独生成ngram vectorizer = CountVectorizer(ngram_range=ngram_range) for clause in clauses: vec = vectorizer.fit_transform([clause]) ngrams.extend(vectorizer.get_feature_names_out()) return ngrams # 提取每个文本的ngram列表 corpus_ngrams = [extract_ngrams(text) for text in c] # 统计频率并生成DataFrame mlb = MultiLabelBinarizer() ngram_counts = mlb.fit_transform(corpus_ngrams) df = pd.DataFrame(ngram_counts, columns=mlb.classes_) print(df)
运行后,第一个文本只会有data和format两个一元ngram,不会出现跨标点的data format二元ngram。
方法2:自定义预处理与分词逻辑
通过预处理把标点替换成特殊分隔符,打断词的连续序列,同时过滤掉分隔符:
import re import pandas as pd from sklearn.feature_extraction.text import CountVectorizer c = ["data. format", "data are format hello world"] def custom_preprocessor(text): # 把标点替换成特殊分隔标记 return re.sub(r'[^\w\s]', ' __DELIM__ ', text) def custom_tokenizer(text): tokens = text.split() # 过滤分隔标记,只保留有效单词 return [token for token in tokens if token != '__DELIM__'] vectorizer = CountVectorizer( ngram_range=(1,2), preprocessor=custom_preprocessor, tokenizer=custom_tokenizer, # 禁用默认token_pattern,避免过滤短词 token_pattern=None ) X = vectorizer.fit_transform(c) terms = vectorizer.get_feature_names_out() df = pd.DataFrame(X.todense(), columns=terms) print(df)
这种方法通过分隔标记打破词的连续性,既不会把标点纳入分词结果,也不会生成跨标点的ngram。
为什么默认处理没生效?
默认CountVectorizer的token_pattern是r"(?u)\b\w\w+\b",它只会提取由字母/数字/下划线组成的至少2个字符的词,所以会自动剥离data.中的句号,但不会改变词的顺序连续性——剥离标点后,data和format仍被视为同一序列中的连续词,因此会生成二元ngram。
内容的提问来源于stack exchange,提问作者user20c
相关产品推荐
相关产品推荐

