本文转载自微信公众号「数仓宝贝库」, 作者赵志强 等. 转载本文请联系数仓宝贝库公众号.
现实世界中的数据量越来越大, 也越来越容易受到噪声, 缺失值和不一致数据等的影响. 数据库太大, 如若有不同的来源, 那么脏数据问题一定会存在, 这是不可避免的. 为了使数据中的各种问题对我们的建模影响最小化, 需要对数据进行预处理.
在实际操作中, 数据预处理通常分为两大步, 一是数据清洗, 二是数据的基本分析. 这两步并不一定是按先后顺序进行的, 通常也会相互影响. 比如, 有的错误数据 (不可能出现的极值), 必须通过基本的统计分析才能发现.
有一种说法, 数据的预处理会占据绝大部分的工作量, 有的甚至会达到所有工作量的 80%, 建模和算法真正的工作量其实只有 20%. 这个结论在互联网或者传统 IT 领域, 特别是面对大量的非结构化数据时, 确实是事实.
所以第一步, 也是非常关键的一步, 就是数据清理. 为了清理数据, 我们必须要知道可能存在的问题, 才能针对相应的问题设计相应的方法.
原始数据可能存在如下三种问题.
数据缺失: 数据缺失的问题在高频数据里面特别常见. 而且由于很多投资者是自己实时下载的数据, 因此即使之后发现也很难弥补.
噪声或者离群点: 由于系统或者人为的失误, 导致数据出现明显的错误, 比如某支股票的价格本应在 12 元左右, 结果突然出现了 100 元的价格数据.
数据不一致: 很多投资者, 为了确保数据正确性, 会使用多个数据源进行交叉验证, 这时往往会出现数据不一致的问题. 即使是同一个数据源, 有时候也会出现数据不一致的问题. 比如期货行情数据, Wind, 文华, MC 的数据都有可能出现不一致的问题, 数据频率越高, 不一致的可能性就越大.
01 缺失值
针对缺失值, 实际操作中, 需要两套程序: 一套程序是检查缺失值, 一套程序是填补缺失值. 一般流程是, 先检查缺失值, 研究缺失值, 选择填补方法, 进行填补, 然后再次检查. 这样迭代循环, 直到将数据缺失控制在可接受范围内.
缺失值, 也有多种类型, 一种是 "正常缺失", 比如股票在某一天停牌, 那么这一天的交易数据就是没有的. 一种是 "非正常缺失", 比如明明有交易, 但就是没有交易数据.
举个例子, 在下载 5 分钟数据的时候, 发现 20160104 的数据都有缺失, 但 Wind 上的数据又显示当天的交易情况为 "交易". 实际情况是当天发生了 "熔断", 因为是新的机制, 所以 Wind 还没来得及准备一个字段用于表示当天的交易状态. 这种情况就属于数据的 "正常缺失", 只是交易状态与数据不一致而已. Wind 的交易状态字段如下图所示.
在检查缺失值时, 这两种缺失需要分辨清楚, 因为不同的缺失值, 处理方法也不一样. 检查好缺失值之后, 就需要进行处理了. 先处理 "非正常缺失", 一般流程具体如下.
1) 检查提取数据是否出错. 有时候, 数据源本身是完整的, 然而自己在提取数据的时候出现了问题. 比如, 笔者在使用市场上某家的金融高频数据的时候, 下载 5 分钟数据计算高频波动率, 发现存在很多缺失的数据. 经该公司后台查询后发现, 他们的数据库其实是有这个数据的, 这说明是在下载数据的过程中出现了问题.
2) 从其他数据源提取. 有的数据源本身就缺失了数据, 对于这种情况可以再寻找另外一个数据源进行补充.
算法填充. 有的时候, 我们没有办法使用多数据源进行补充, 而且有的数据本身就有空缺, 无法补充. 这个时候, 可以退而求其次, 使用算法填充.
常用算法有向前填充和向后填充两种. 所谓向前填充是指使用之前最近的一个数据对空值进行填充. 向后填充是指使用之后最近的一个数据对空值进行填充.
Pandas 提供了一个函数用于数据填充. 示例代码如下:
- df = pd.DataFrame([[np.nan, 2, np.nan, 0],
- ... [3, 4, np.nan, 1],
- ... [np.nan, np.nan, np.nan, 5],
- ... [np.nan, 3, np.nan, 4]],
- ... columns=list('ABCD'))
- df
- A B C D
- 0 NaN 2.0 NaN 0
- 1 3.0 4.0 NaN 1
- 2 NaN NaN NaN 5
向前填充的示例代码如下:
- df.fillna(method='ffill')
- A B C D
- 0 NaN 2.0 NaN 0
- 1 3.0 4.0 NaN 1
- 2 3.0 4.0 NaN 5
- 3 3.0 3.0 NaN 4
除了向前填充, 该函数也支持向后填充, 不过, 要使用特定的值进行填充.
有的数据发生了缺失, 无法使用简单的向前填充或向后填充来处理. 比如, 使用 Wind 下载 a 股复权数据, 会发现交易状态 trade_status 在 1999 年之前都是空值, 虽然实际上是有交易的, 但如果直接按照 trade_status='交易'这个条件来筛选, 将会把 1999 年之前的所有数据都去掉. 这个时候就需要根据逻辑设计一个算法来进行填充, 比如将成交量 volume>0 的都填充为 "交易".
02 噪声或者离群点
噪声或离群点的问题一般有两种情况, 一种是数据错误导致的, 比如本来应该是 10.0 的数据, 错误显示为 10000; 另一种则是其本身是真实数据, 但就是离群点, 比如金融危机中的收益率或者波动率, 可能就非常极端, 成为离群点.
一般的处理步骤具体如下.
1) 通过一定的算法识别出离群点. 一般是使用该数据标准差的多少倍来判断. 比如正太分布中, 正负标准差 3 倍以上的概率是 99.7%, 可以将其认定为可疑离群点.
2) 人工判断离群点是属于错误数据导致的, 还是正常的离群点.
3) 对离群点进行处理. 一般来说, 错误的离群点需要更正或者删除. 正常的离群点则需要另外建模进行分析.
03 数据不一致
为了确保数据的准确性, 有时候需要使用多种数据源进行交叉验证. 比如, 在研究港股的时候, 对比了 Wind 和 Bloomberg 的后复权数据之后, 发现两者存在很大的差别, 这就是数据不一致的问题, 但我们并不能确定哪一个才是正确的, 于是又加入了同花顺和 CSMAR 的数据进行对比, 发现后者与 Wind 的数据是一致的. 所以可以确认是 Bloomberg 的问题, 因而采用 Wind 的数据.
当然, 在实际工作中, 数据清理的问题要远远多于这里介绍的几种, 需要系统性地, 仔细地去处理.
本书摘编自《Python 量化投资: 技术, 模型与策略》, 经出版方授权发布.
来源: http://developer.51cto.com/art/202109/681182.htm