4. 数据清洗与预处理:文本去噪、去重、标准化、处理表情符号与特殊字符

数据清洗,说白了就是给原始文本「洗澡」。

我做了这么多年量化分析,见过太多人拿着脏数据硬跑模型。结果呢?模型精度上不去,回测曲线漂亮得像假的一样。嗯,其实不是模型不行,是数据本身就有问题。

社交媒体情绪指数的基础是文本。但社交文本有多脏?你想想看——错别字、表情符号、@用户、URL链接、重复转发、广告水军……这些东西不清理干净,后面的分析全是白费功夫。

4.1 文本去噪:把垃圾信息筛出去

去噪,就是去掉那些对情绪分析没用的东西。

我个人习惯把噪声分成三类:

噪声类型 例子 处理方式
格式噪声 HTML标签、URL、@用户、#话题# 正则表达式直接剔除
内容噪声 广告、刷屏、无意义重复 规则过滤 + 模型判别
语言噪声 错别字、火星文、拼音缩写 词典映射 + 纠错模型

举个例子。我在做某电商平台的舆情监控时,发现大量「加V信xxxxx领红包」的垃圾评论。这些内容对情绪分析毫无价值,反而会拉偏模型。

核心原则:去噪不是越狠越好。过度清洗会丢失情感信息。比如「这手机真TMD垃圾」里的「TMD」虽然是脏话,但恰恰是强烈的负面情绪信号。

import re

def text_denoise(text):
    # 去掉URL
    text = re.sub(r'http\S+', '', text)
    # 去掉@用户
    text = re.sub(r'@\w+', '', text)
    # 去掉#话题#,但保留话题文字
    text = re.sub(r'#', '', text)
    # 去掉多余空格
    text = re.sub(r'\s+', ' ', text).strip()
    return text

4.2 文本去重:别让重复数据骗了你

社交媒体的重复数据有多严重?我见过一个极端案例——某条负面新闻下面,同一个ID发了37遍一模一样的评论。如果不做去重,这条负面情绪会被放大37倍。

去重分两个层次:

  • 完全去重:一模一样的内容,只保留一条
  • 近似去重:内容相似度超过阈值(比如95%),合并处理

我的经验:完全去重用哈希就够了。近似去重我推荐MinHash或SimHash,处理千万级数据也不慢。

from simhash import Simhash

def dedup_by_simhash(texts, threshold=0.95):
    seen = []
    unique = []
    for t in texts:
        h = Simhash(t)
        if not any(h.distance(s) < threshold for s in seen):
            seen.append(h)
            unique.append(t)
    return unique

4.3 文本标准化:把「方言」翻译成「普通话」

标准化,就是把各种不规范的表达统一起来。

我曾经处理过一批微博数据,发现「哈哈哈」「哈哈哈哈」「哈哈哈哈哈」出现了几十种变体。如果不标准化,模型会认为它们是不同的词。

标准化的几个关键点:

  1. 统一大小写:英文全部转小写
  2. 全角半角转换:中文用全角,英文数字用半角
  3. 表情符号映射:😊 → [微笑] 或直接保留
  4. 重复字符压缩:「好棒棒棒棒」→「好棒」

注意:重复字符压缩要谨慎。「哈哈哈」压缩成「哈」会丢失情感强度。我一般保留2-3个重复字符作为情感增强信号。

import unicodedata

def normalize_text(text):
    # 全角转半角(字母数字部分)
    text = unicodedata.normalize('NFKC', text)
    # 压缩重复字符(保留2个)
    import re
    text = re.sub(r'(.)\1{2,}', r'\1\1', text)
    return text

4.4 表情符号与特殊字符处理

表情符号是社交媒体情绪分析的金矿。一个😡比一百字吐槽都管用。

我的处理策略是:

  • 保留表情符号:直接作为特征输入模型
  • 或者映射为文本:😊 → happy, 😢 → sad
  • 特殊字符:比如「!!!」,表示强烈情绪,保留但压缩

为什么会这样?因为很多情绪模型在纯文本上表现一般,加上表情符号后准确率能提升5-10%。

import emoji

def process_emojis(text):
    # 方案一:保留原样
    # return text
    
    # 方案二:映射为文本
    return emoji.demojize(text, delimiters=("", ""))

4.5 完整清洗流水线

在实际项目中,我习惯把清洗步骤串成一条流水线。每一步都是可配置的,方便调试。

class TextCleaner:
    def __init__(self):
        self.pipeline = []
    
    def add_step(self, func, name):
        self.pipeline.append((func, name))
    
    def clean(self, text):
        for func, name in self.pipeline:
            text = func(text)
        return text

# 使用示例
cleaner = TextCleaner()
cleaner.add_step(text_denoise, '去噪')
cleaner.add_step(normalize_text, '标准化')
cleaner.add_step(process_emojis, '表情处理')

raw_text = "😡这手机太垃圾了!!!@小明 http://xxx.com"
clean_text = cleaner.clean(raw_text)
print(clean_text)  # 输出: 这手机太垃圾了!!!

避坑指南:我曾经在清洗时把「不」字误删了,结果「我不喜欢」变成了「我喜欢」,情绪完全反转。从那以后,我每次清洗完都会抽样检查,确保语义没变。

4.6 本章知识体系

下面这张图,是我自己总结的数据清洗核心流程。你照着这个框架走,基本不会漏掉关键步骤。

社交媒体文本清洗流水线 原始文本 文本去噪 文本去重 文本标准化 表情符号处理 ↓ 每个步骤的详细处理内容 去噪内容 URL链接 @用户提及 广告垃圾 HTML标签 去重策略 完全去重 近似去重 SimHash MinHash 标准化内容 大小写统一 全半角转换 重复字符压缩 错别字纠正 表情处理 保留原符号 映射为文本 情感强度标记 特殊字符压缩 清洗后的干净文本

嗯,数据清洗这一步确实枯燥,但它是整个情绪指数编制的基石。我见过太多团队在模型上花大功夫,结果数据源没洗干净,最后全白干。

记住一句话:垃圾进,垃圾出。你清洗得越仔细,后面的分析就越靠谱。

专注资料整理