4、数据清洗基础:处理缺失值、重复值,统一日期格式,重置索引,为分析打好基础

说实话,我见过太多人一拿到数据就急着跑模型。结果呢?跑出来的结果自己都不敢信。数据清洗这事儿,看着不起眼,但绝对是整个分析流程的基石。你想想看,地基没打好,楼盖得再高也是危房。

我个人习惯,拿到任何数据集,第一件事不是看收益率,而是先做清洗。今天咱们就把这块硬骨头啃下来。

4.1 缺失值处理:别让「空」坑了你

纳斯达克的数据里,缺失值太常见了。比如某只股票停牌了,或者数据源没更新,那一行就是空的。我刚开始做量化的时候,直接用了 dropna() 把缺失行全删了,结果回测曲线漂亮得不像话——后来才发现,我把亏损最多的几天全删了。

⚠️ 注意: 千万别无脑删除缺失值!先搞清楚缺失的原因。

处理缺失值,我一般分三步走:

  1. 先查缺:用 df.isnull().sum() 看看每列缺了多少
  2. 再定性:是随机缺失?还是因为某些特定条件导致的缺失?
  3. 后处理:根据情况选择删除、填充或插值

举个例子,某只股票某天没有交易数据,我会用前一天的收盘价填充:

# 用前向填充处理缺失值
df['close'].fillna(method='ffill', inplace=True)

# 或者用均值填充
df['volume'].fillna(df['volume'].mean(), inplace=True)

我曾经遇到过一个坑:某只小盘股连续三天没交易,用前向填充后,那三天的收益率全是0。这直接导致我的波动率计算偏小。后来我改用线性插值,效果就好多了。

💡 我的经验: 金融时间序列数据,优先用前向填充(ffill)。如果缺失较多,考虑用插值法。

4.2 重复值处理:别让数据「胖」了

重复数据在纳斯达克数据里其实不多见,但一旦出现,后果很严重。你想想看,同样的日期、同样的价格出现了两次,计算收益率时就会多出一行0,累计收益直接偏掉。

我一般这样处理:

# 检查重复行
print(df.duplicated().sum())

# 删除重复行,保留第一个
df.drop_duplicates(inplace=True)

# 按指定列检查重复(比如按日期和股票代码)
df.drop_duplicates(subset=['date', 'symbol'], keep='first', inplace=True)

这里有个细节:keep='first' 保留第一次出现的行,keep='last' 保留最后一次。我个人习惯用 first,因为数据源通常是按时间顺序排列的。

🔑 关键点: 重复值检查一定要结合业务逻辑。比如同一只股票在同一天出现两次收盘价,那肯定有问题。

4.3 统一日期格式:时间线不能乱

日期格式不统一,是我见过最多的问题。有的数据源用 2024-01-15,有的用 01/15/2024,还有的用 20240115。如果不统一,排序、合并、计算时间差全都会出错。

我的标准做法:

# 统一转换为 datetime 类型
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')

# 设置日期为索引
df.set_index('date', inplace=True)

# 按日期排序
df.sort_index(inplace=True)

为什么要设置成索引?因为 pandas 对时间索引有大量优化,比如按年/月/日切片、重采样、滚动计算,都特别方便。

⚠️ 注意: 转换日期时,如果遇到无法解析的格式,pd.to_datetime() 会报错。可以用 errors='coerce' 参数,把无法解析的变成 NaT(缺失时间)。

我记得有一次,某个数据源的日期列里混进了「2024-02-30」这种不存在的日期。用 errors='coerce' 处理后,这些行变成了 NaT,我再手动排查,才发现是数据录入错误。

4.4 重置索引:让数据「归位」

重置索引听起来简单,但用不好会出大问题。尤其是当你做了筛选、排序、分组之后,索引会变得乱七八糟。

我常用的场景:

# 筛选后重置索引
df_filtered = df[df['volume'] > 1000000]
df_filtered.reset_index(drop=True, inplace=True)

# 或者保留原日期索引
df_filtered.reset_index(inplace=True)

drop=True 这个参数很关键。如果设为 True,原来的索引会被丢弃;如果设为 False,原来的索引会变成一列数据。

💡 我的习惯: 如果后续还要按时间分析,我会保留日期索引,不重置。如果只是做统计计算,我会重置索引,让行号从0开始,更清爽。

4.5 知识体系总览

下面这张图,是我自己总结的数据清洗流程。每次拿到新数据,我就按这个步骤走一遍,基本不会漏掉什么。

数据清洗核心流程 原始数据 第一步:处理缺失值 检查 → 定性 → 填充/删除/插值 第二步:处理重复值 检查重复 → 按业务逻辑删除 第三步:统一日期格式 转换为 datetime → 设为索引 → 排序 第四步:重置索引(可选)

4.6 实战:完整的数据清洗流水线

最后,我把自己常用的清洗流程封装成一个函数。你直接拿去用就行:

def clean_nasdaq_data(df):
    """
    纳斯达克数据清洗流水线
    """
    # 1. 处理缺失值:前向填充
    df.fillna(method='ffill', inplace=True)
    # 如果开头还有缺失,用后向填充
    df.fillna(method='bfill', inplace=True)

    # 2. 删除重复行
    df.drop_duplicates(subset=['date', 'symbol'], keep='first', inplace=True)

    # 3. 统一日期格式
    df['date'] = pd.to_datetime(df['date'], errors='coerce')
    # 删除日期为 NaT 的行
    df.dropna(subset=['date'], inplace=True)

    # 4. 设置日期索引并排序
    df.set_index('date', inplace=True)
    df.sort_index(inplace=True)

    return df

# 使用示例
df_clean = clean_nasdaq_data(df_raw)
print(f"清洗前:{df_raw.shape},清洗后:{df_clean.shape}")

嗯,这套流程我用了好几年,在几十个数据集上验证过。不能说百分百完美,但应付90%的纳斯达克数据是没问题的。

数据清洗这事儿,说白了就是「磨刀不误砍柴工」。你花10分钟把数据洗干净,后面跑模型、做回测,至少能省下2小时排查错误的时间。这笔账,怎么算都划算。


专注资料整理