4、数据采集与清洗:数据源识别、数据提取技巧、数据清洗规则

好,咱们进入第四讲。数据采集与清洗,说白了就是「找数据」和「洗数据」。我见过太多审计同行,花80%的时间在整理数据,真正分析的时间反而没多少。这其实是个大坑。我个人习惯是,把数据采集和清洗流程化、工具化,效率能翻好几倍。

4.1 数据源识别:你得知道去哪儿找

数据源识别,听起来高大上,其实就是搞清楚「数据在哪儿」。我刚开始做审计时,经常被财务部的人带着跑,他们给什么我就用什么。后来发现,很多关键数据其实藏在别的地方。

常见的审计数据源,我大致分了这么几类:

数据源类型 典型来源 我踩过的坑
财务系统 金蝶、用友、SAP 导出的Excel经常有合并单元格,一处理就乱
业务系统 ERP、CRM、OA 业务数据跟财务数据对不上,口径不一致
外部数据 工商、税务、银行流水 格式五花八门,PDF、图片都有
非结构化数据 合同扫描件、邮件、聊天记录 OCR识别率低,得人工复核
我的小技巧: 拿到一个项目,先别急着要数据。花半天时间,跟IT部门聊一聊,搞清楚数据流转路径。你会发现,很多数据其实可以从数据仓库直接拉,省去跟业务部门反复沟通的时间。

4.2 数据提取技巧:怎么把数据「拿」出来

数据源找到了,接下来就是提取。这里我分几种常见场景来讲。

4.2.1 从数据库直接提取

如果你能直接连数据库,那是最理想的。我个人习惯用SQL,简单高效。举个例子,从SAP HANA里提取某个月的销售数据:

-- 提取2024年1月销售数据
SELECT 
    VBELN AS 销售订单号,
    POSNR AS 行项目,
    MATNR AS 物料编码,
    KWMENG AS 数量,
    NETWR AS 净额
FROM VBAK
JOIN VBAP ON VBAK.VBELN = VBAP.VBELN
WHERE ERDAT BETWEEN '20240101' AND '20240131'
  AND VKORG = '1000';  -- 销售组织

嗯,这里要注意:数据库权限问题。我曾经遇到过,IT部门给了只读权限,但表名是加密的,根本看不懂。后来我学乖了,先要一份数据字典,或者让DBA帮忙写个视图。

4.2.2 从Excel/CSV提取

大部分审计数据还是以Excel形式存在。我建议用Python的pandas库来处理,比手动操作快得多。

import pandas as pd

# 读取Excel文件
df = pd.read_excel('财务数据.xlsx', sheet_name='总账', header=0)

# 查看前5行,确认数据格式
print(df.head())

# 如果有多层表头,需要指定header参数
df = pd.read_excel('财务数据.xlsx', sheet_name='总账', header=[0, 1])
避坑指南: 我曾经遇到过Excel文件里隐藏了行和列,直接读取会漏数据。所以,读取前最好先检查一下文件属性,或者用openpyxl库先解析一下。

4.2.3 从PDF/图片提取

这个场景比较头疼。银行回单、合同扫描件,很多都是PDF或图片。我一般用OCR技术,但准确率不是100%。

import pytesseract
from PIL import Image

# 读取图片
img = Image.open('银行回单.jpg')

# OCR识别
text = pytesseract.image_to_string(img, lang='chi_sim')

# 提取关键信息(比如金额、日期)
import re
amount = re.findall(r'\d+\.\d{2}', text)
print(amount)

说实话,OCR识别出来的数据,我建议一定要人工复核一遍。特别是金额、日期这些关键字段,错一个数字,整个审计结论可能就偏了。

4.3 数据清洗规则:把脏数据「洗」干净

数据提取出来,你会发现各种问题:空值、重复、格式不统一、异常值……不洗一下,根本没法用。我总结了一套清洗规则,按优先级排序。

4.3.1 处理缺失值

缺失值是最常见的问题。我的处理原则是:

  • 关键字段缺失: 比如金额、日期,必须补全。可以找原始凭证,或者跟业务部门确认。
  • 非关键字段缺失: 比如备注、摘要,可以填充为「未知」或直接删除。
  • 整行缺失: 如果一行数据大部分字段都为空,直接删除。
# 用pandas处理缺失值
df = df.dropna(subset=['金额', '日期'])  # 删除关键字段缺失的行
df['备注'] = df['备注'].fillna('未知')   # 非关键字段填充

4.3.2 去除重复数据

重复数据会导致统计结果失真。我一般先按关键字段去重,比如订单号、凭证号。

# 按订单号去重,保留第一条
df = df.drop_duplicates(subset=['订单号'], keep='first')
注意: 有些重复数据是合理的,比如红字冲销。所以去重前,一定要先理解业务逻辑。我曾经因为盲目去重,把一笔重要的调整分录删掉了,后来花了半天时间才找回来。

4.3.3 统一数据格式

同一个字段,不同来源的格式可能不一样。比如日期,有的用「2024-01-01」,有的用「2024/01/01」,还有的用「2024年1月1日」。我习惯统一成标准格式。

# 统一日期格式
df['日期'] = pd.to_datetime(df['日期'], format='mixed')

# 统一金额格式(去掉千分位分隔符)
df['金额'] = df['金额'].str.replace(',', '').astype(float)

4.3.4 处理异常值

异常值往往意味着风险点。我一般用箱线图或标准差法来识别。

# 用标准差法识别异常值
mean = df['金额'].mean()
std = df['金额'].std()
df = df[(df['金额'] > mean - 3*std) & (df['金额'] < mean + 3*std)]

不过,异常值不一定是错误。比如一笔大额采购,金额远超正常范围,这恰恰可能是审计重点。所以,识别出异常值后,不要直接删除,先标记出来,后续重点核查。

4.3.5 数据一致性校验

最后一步,是校验数据的一致性。比如,总账的借方发生额应该等于贷方发生额,销售订单的金额应该等于发票金额。如果不一致,说明数据有问题,需要回溯。

# 校验借贷平衡
debit_sum = df[df['借贷方向'] == '借']['金额'].sum()
credit_sum = df[df['借贷方向'] == '贷']['金额'].sum()

if abs(debit_sum - credit_sum) > 0.01:
    print('借贷不平衡,差异金额:', debit_sum - credit_sum)
我的经验: 数据清洗不是一次性工作。我一般会写一个清洗脚本,每次拿到新数据,跑一遍脚本,然后人工抽检。这样既保证了效率,又降低了出错概率。

好了,数据采集与清洗这部分就讲到这里。说白了,就是「找数据」要准,「拿数据」要快,「洗数据」要狠。下一章,咱们聊聊数据分析的具体方法。