做电商数据分析的,怕是没人没被订单数据坑过。早年间我还是个只会“拉数”不会“看数”的小跟班,第一次独立出月度销售报表,从ERP系统里把订单明细导出来,原样求和算销售额,结果财务那边的收款流水跟我算的总数差了将近8万块。那晚我反复核对到凌晨,才发现问题根本不在Excel公式,而在订单表本身:同一个订单号重复出现了好几行,退款单被当作正向销售加总,状态还是“已支付”但支付金额栏却是负数,几万条记录里还散落着一堆创建时间为空的数据。从那时起我才真正意识到,所谓数据,尤其是订单数据,从来不是打开就能直接用的报表,而是需要先花大力气做数据清洗的原始物料。
后来几年,我陆续接手过多个电商项目,也慢慢总结出一套从脏数据到准确反映业务事实的处理流程。这个过程绕不开Excel,但真正让我效率翻倍的还是pandas。今天这篇就是一次完整复盘,讲讲订单数据里最常见的脏数据长什么样,为什么会出现这些脏数据,以及怎么用pandas做数据清洗和数据预处理。如果你是刚接触数据清洗的数据分析师、运营或刚转行的数据工程师,这篇内容应该能帮你少走很多弯路。
1. 一次对账失败,暴露了订单数据的真实面目
1.1 当初那份订单表,到底有多脏
先说回那次差点让我想转行的对账。系统导出的订单表大概有十几个字段:订单号、下单时间、支付时间、发货时间、商品金额、实付金额、退款金额、订单状态、买家ID、收货省份、支付渠道。看着挺规整,Excel筛选也能用,但真实情况是:同一个订单号因为商品有多个SKU,明细被拆成了三行,而且每一行都带一个相同的支付金额,直接sum之后等于把一个多SKU订单的成交额算了好几遍。
更离谱的是退款数据。表格里既有整单退款的订单,也有部分退款的订单,但很多“退款金额”字段是手工填的,有的填正数,有的填负数,有的退款流水已经发生,订单状态却还停在“已支付”。我当时不懂,把退款金额为负数的那几条直接减掉,把退款金额为正数的几条又原样加回去,最后跟财务流水对不上,就是被这种字段口径不一致给坑了。再往下翻,买家ID一列也有不少空值,因为有些订单来自游客结算,有些是第三方App扫码下单,用户还没注册就被系统记录了一笔订单,ID自然为空。如果拿这些数据直接做用户复购分析,马上就会少算一批人。
再说日期字段。Excel里“2024/3/2 10:31”和“2024-03-02 10:31:00”都能显示成日期,但底层一个是文本,一个是真日期,pandas读进来之后我对着dtype检查才发现,所谓“下单时间”其实是一堆字符串,排序完全不受控。
1.2 脏数据不是“偶尔的错误”,而是业务流程的副产品
很多刚入门的朋友会觉得,脏数据嘛,就是有人录入时不细心。实际上,订单数据脏乱差往往是业务流程自然长出来的副产品。电商订单从下单到支付、发货、确认收货、申请退款、退款完成,中间要经过交易系统、支付网关、库存系统、物流系统、ERP和财务系统。这些系统当初都是为“让交易跑通”而设计的,不是为了给分析师导出一张干净报表。
以订单状态为例,交易系统里真实的状态变化往往是事件流,比如支付成功回调、退款成功回调,每来一个回调就产生一条状态变更日志,但订单主表的“订单状态”字段却不一定同步更新。同步靠消息队列,消息队列堆积或回调失败时,主表里就会留下一个“未完结”的假象。后台运营人工改单、客服关闭恶意订单、仓库拦截发货后的强制退款,同样会让表里字段之间出现矛盾。
这个视角很关键:脏数据不是你用几条规则就能永久杜绝的,因为只要业务流程还在迭代,就会有新的字段被加进来,新的异常情况产生。你要做的,是建立一套能识别、能处理、能留痕的数据清洗链路,而不是抱怨数据本身不干净。
1.3 对账失败的本质:口径和事实没有被定义
那次8万块的差异,表面上是重复统计和正负号混乱,本质上是整个报表从一开始就没有定义清楚“什么叫销售额”。我当时做的事情,是把表里的“实付金额”求和,但这张表里的每一行代表的是订单商品明细,不是一笔收款流水。
同一个订单拆成三行,金额应该除以3,还是应该只取一行,或者用订单头关联明细再聚合?退款单要不要扣减?平台优惠和商家优惠叠加之后,分摊到每个SKU上的金额,跟财务收款账户里实际收到的钱根本就不是一回事。如果这些口径没有先跟业务方确认,你再怎么写数据清洗代码,算出来的数字都会跟财务报表有出入。
所以做数据清洗的第一步,不是急着打开pandas写dropna,而是先把“业务事实”定义出来。只有先知道这条数据到底应该代表什么事实,你才知道哪些行要删、哪些值要改、哪些字段要重构。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动手清洗前,先把“业务事实”定义清楚
2.1 一个订单从生成到完结,都走过哪些业务状态
定义业务事实之前,推荐先梳理一遍订单状态机。绝大多数电商项目的订单生命周期大概是这样的:待支付、已支付、已发货、已签收、已完成、已关闭、已取消、售后中、已退款。这里有两个常见陷阱,一是状态可能不止一个“当前值”,比如一笔订单已经全额退款,但主表状态仍停留在“已完成”;再比如一笔订单已经发货,但用户申请退货后仓库还没确认入库,系统里会同时存在“已发货”和“售后中”两条状态记录。
清洗时我不会直接拿“订单状态”做唯一判断,而是会按优先级重构一个状态字段,比如:
- 如果退款金额大于等于实付金额,业务事实倾向判定为“已全额退款”;
- 如果没有全额退款但存在退款单,判定为“
