You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

实现斯坦福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

火山引擎 最新活动