实现斯坦福ML垃圾邮件分类器:词干提取后保留原格式而非数组
解决PorterStemmer处理后转字符串+邮箱特殊处理的方案
咱先梳理下你的需求:用PorterStemmer做词干提取后,既要把整封邮件还原成空格分隔的字符串,又要单独处理邮箱里的单词(词干化),还不能把邮箱里的词干存入特征数组。下面给你一套可行的实现思路和代码:
1. 基础操作:词干列表转字符串
如果是普通单词列表,直接用' '.join()方法就能把列表转成空格分隔的字符串,比如:
from nltk.stem import PorterStemmer stemmer = PorterStemmer() words = ["going", "until", "jurong", "point", "crazy", "available", "only"] stemmed_list = [stemmer.stem(word) for word in words] result_str = ' '.join(stemmed_list) print(result_str) # 输出: go until jurong point crazi avail onli
2. 处理邮箱地址的特殊逻辑
重点是要先识别邮件里的邮箱地址,对邮箱内的单词做词干提取,但不把这些词干加入特征数组。这里可以用正则匹配邮箱,再拆分邮箱的各个部分处理:
完整代码示例
import re from nltk.stem import PorterStemmer stemmer = PorterStemmer() def process_email(email_content): # 用来存储最终的处理后字符串的片段 processed_parts = [] # 用来存储非邮箱部分的词干(用于特征数组) feature_words = [] # 正则匹配邮箱地址(简单匹配,可根据需求调整) email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' # 把邮件内容拆成「非邮箱部分」和「邮箱部分」的列表 tokens = re.split(f'({email_pattern})', email_content) for token in tokens: # 如果当前token是邮箱地址 if re.fullmatch(email_pattern, token): # 拆分邮箱的用户名和域名 username, domain = token.split('@', 1) # 处理用户名:按.、_等分隔符拆分单词,词干化后重新拼接 stemmed_username = '.'.join([stemmer.stem(word) for word in re.split(r'[._]', username)]) # 处理域名:同样拆分后词干化,注意保留后缀的完整(比如.com不要改) domain_parts = domain.split('.') stemmed_domain_parts = [stemmer.stem(part) for part in domain_parts[:-1]] + [domain_parts[-1]] stemmed_domain = '.'.join(stemmed_domain_parts) # 重新组合成邮箱地址 stemmed_email = f'{stemmed_username}@{stemmed_domain}' processed_parts.append(stemmed_email) # 邮箱里的词干不加入feature_words数组 else: # 处理普通文本:先拆分单词,词干化,再转回字符串 words = re.findall(r'\b\w+\b', token.lower()) # 转小写并提取单词 stemmed_words = [stemmer.stem(word) for word in words] if stemmed_words: processed_parts.append(' '.join(stemmed_words)) # 把这些词干加入特征数组 feature_words.extend(stemmed_words) # 把所有片段拼接成最终的空格分隔字符串 final_email_str = ' '.join(processed_parts) return final_email_str, feature_words # 测试示例 test_email = "Go until jurong point crazy available only. Contact me at hello_world@example.com or test_user@mail.org" processed_str, features = process_email(test_email) print("处理后的邮件字符串:") print(processed_str) # 输出: go until jurong point crazi avail onli contact me at hello_world@exampl.com or test_user@mail.org print("\n用于特征数组的词干:") print(features) # 输出: ['go', 'until', 'jurong', 'point', 'crazi', 'avail', 'onli', 'contact', 'me', 'or']
代码说明
- 用正则拆分邮件内容,把邮箱和普通文本分开处理,避免邮箱被拆成零散单词
- 邮箱的用户名和域名分别拆分后词干化,再重新组合成邮箱格式,保证邮箱的可读性
- 普通文本部分词干化后不仅拼接成字符串,还把词干存入
feature_words数组,而邮箱里的词干则跳过这一步 - 正则匹配邮箱的模式可以根据实际需求调整(比如支持更复杂的邮箱格式)
这样就能同时满足你三个需求:词干提取后保持空格分隔字符串、处理邮箱内的词干、邮箱词干不存入数组啦~
内容的提问来源于stack exchange,提问作者Omni Scientist




