1.3 观测、变量与数据集
分析生命周期要求我们先理解数据,再清洗和计算。本节会建立完成这项工作所需的词汇。矩形表格看起来很直观,但许多严重错误都始于一个没有回答的问题:一行究竟代表什么?
先看一张配送表:
| order_id | zone | delivery_minutes | on_time |
|---|---|---|---|
| A104 | North | 31 | yes |
| A105 | South | 52 | no |
| A106 | North | 28 | yes |
观测(observation)是对研究实体或事件的一次记录。这里每行是一笔订单,所以观测单位是订单。变量(variable)是所有观测都按照同一含义记录的属性,例如区域或配送分钟数。值(value)是某条观测在某个变量上的具体记录,例如订单 A105 的配送时间 52。
“每行一笔订单”描述了数据集的粒度(grain),也叫详细程度。应当把它写成一句完整的话。如果无法完成“每行代表……”这句话,就不要开始聚合。
混合粒度会制造隐形重复
假设另一份导出中,每行代表订单内的一件商品。订单 A104 买了三种商品,因此出现三次。新表的粒度是订单明细(order line),不再是订单。此时数行数得到的是商品项数,不是订单数。
如果订单级数值在每条商品记录上重复,危险就会出现:
| order_id | product | order_total |
|---|---|---|
| A104 | noodles | 42 |
| A104 | tea | 42 |
| A104 | fruit | 42 |
对 order_total 求和会得到 126,但这笔订单实际只有 42。算术本身没有错,是数据模型不适合这种算术。要计算订单收入,应只保留一份订单级数值,或者先把明细金额聚合到订单粒度。
相互关联的数据集可以使用不同粒度:
- 顾客表每行一位顾客。
- 订单表每行一笔订单。
- 订单明细表每行是一笔订单中的一种商品。
- 每日摘要表每行可以是某个日期、某个区域的一条记录。
在不同粒度之间移动,需要明确的聚合或连接操作。后续章节会用 pandas 实现这些操作。现在先养成一个习惯:每次计数、求平均或连接前,先说出数据粒度。
存储类型不等于分析含义
数据类型(data type)描述值如何存储、允许进行哪些操作;数据角色(data role)描述字段在分析中的含义。二者有关,但并不相同。
| 角色 | 含义 | 示例 | 常见有效操作 |
|---|---|---|---|
| 标识符 | 区分实体的标签 | order_id、customer_id | 判断相等、检查唯一、连接 |
| 数值 | 测量或计数得到的量 | 分钟数、数量、价格 | 算术、排序、聚合 |
| 无序类别 | 没有等级的命名分组 | 区域、付款方式 | 判断相等、计数、分组比较 |
| 有序类别 | 存在顺序的分组 | 低、中、高 | 判断相等、排序、谨慎汇总 |
| 布尔值 | 两种逻辑状态 | 是否准时、是否退款 | 逻辑运算、计算比例 |
| 日期时间 | 时间点或时间间隔 | 下单时间、配送日期 | 排序、计算时长、重采样 |
| 文本 | 自由书写的语言 | 投诉内容 | 搜索、解析、语言处理 |
员工编号 10021 可能保存为整数,但对编号求平均没有意义。1 至 5 分的评分有顺序,可是从 1 到 2 的体验变化未必与从 4 到 5 完全相同。日期若保存为文本,在解析为日期时间以前就无法可靠计算时长。
模式是一份数据契约
模式(schema)记录预期字段、角色、类型、允许值、单位和缺失规则,将隐藏假设变成可执行检查。配送数据的部分模式可以这样写:
order_id是非空标识符,在订单粒度上必须唯一。
delivery_minutes是以分钟为单位的数值,存在时不能为负。
zone是类别,只能属于{North, Central, South}。
on_time是布尔值,由已经记录的阈值派生。
模式并不是宣称现实一定整洁,而是说明分析期待什么,以及违反期待时如何处理。出现新区域可能是业务发生了合理变化,不一定是坏数据。好的验证会连同上下文报告违反项,而不是盲目删除所有意外。
整洁结构让问题更容易表达
一个实用默认原则是整洁数据(tidy data):每个变量占一列,每条观测占一行,每类观测单位放在一张表中。它不是唯一存储方式,却与筛选、分组、绘图和建模工具自然配合。
sales_jan、sales_feb、sales_mar 这样的月份宽表把月份值编码进了列名。整洁版本会使用一列 month 和一列 sales。第 6 章将讲解如何在这些形式之间重塑数据。现在最重要的认识是:数据结构决定了哪些操作简单,也决定了哪些错误更容易发生。
到这里,我们已经声明了粒度和模式。最后一节要准备一个工作环境,让这些声明、执行检查的代码和分析结果都能从干净状态重新运行。