跳转至

P3-2.1 为什么已存的记录还不能直接算作数据集

Section ID: P3-2.1 Version: v2026.07.20

很多人一听到数据建模,先想到的是数据库表设计。实际上,data modeling 这个词也经常出现在整理存储结构的语境里。它背后还连着 DSS/BI/DW/OLAP 这类把数据汇总起来再接到决策上的数据系统脉络。换句话说,数据建模从一开始并不只是 AI 的语言,它也生长在“把已存的数据重新组织、重新比较,再连接到判断”这一更宽的流程里。但 AI 和数据分析需要的数据建模,会再往前走一步。这里比起 数据存在哪里,更重要的是 应该把已存的记录重新看成什么样的数据集候选,才能回答某个问题

这一节聚焦的是区分 已存的记录数据集候选。可以先把 dataset 理解成:为了学习或评估而按同一个问题组织起来的一组样本和变量。后面的章节会把 samplefeaturebaselineoutput structure 一起展开,但这里先不急着长篇定义这些词,而是先固定一件事:为什么只有存储结构,还不能直接进入那个阶段。

开头出现的术语也不适合用同样的分量来读。DSSBIDWOLAP 是说明数据使用背景的缩写,而这一节真正的核心概念是 存储结构数据集候选样本单位问题表达结构

区分 这里应该怎么读
DSS/BI/DW/OLAP 把数据汇总并连接到决策的背景语境
存储结构 为了不丢记录而保存数据的表
数据集候选 为了回答同一个问题而把样本重新组织后的可比较表
样本单位 决定什么算一条案例的标准

也就是说,数据集并不是 行数很多的表。它更接近一种表:里面聚着为了回答同一个问题而组织起来的样本,同时也整理好了描述这些样本的列。因此,存储结构里的一行,并不会自动变成数据集里的一条样本。

假设这里有自动执行动作的源数据。按时间累积的控制参数和传感器值,可以直接放进存储表里。每一行可以放一个时间点、一个传感器名、一个测量值和一个控制设定值。这样的结构适合保存记录,也适合在出问题时回头追踪细节流程。

但只看这张表,仍然很难立刻回答 这次动作是不是比平时更长后段下降是不是异常地慢最近 20 次和基准线相比是不是发生了变化 这类问题。因为存储结构里的一行通常表示的是 某一个时间点的记录,而这些问题真正需要的比较单位却是 一次完整动作,或者 把多个动作聚起来的近期区段。所以,光是“有已存记录”这件事,并不等于“已经有数据集”。

要把这种差别看清楚,可以把存储结构和问题表达结构并排放在一起。

区分 一行表示什么 主要目的
存储结构 一个时间点的记录、一次传感器测量、一次控制设定 保存源数据和保留可追踪性
问题表达结构 一次动作的摘要、近期区段比较、基准线聚合 用于比较、解释和学习准备

把这个区分放进真实的表里,可以先用下面三个问题来检查“行”的含义和可比较性。

第一次拿到表时先问的问题 为什么这个问题必要
一行表示一个时间点记录,还是一次动作的摘要? 因为如果行的含义不同,后面的样本单位也会跟着不同
现在这张表能不能直接拿来比较? 因为存储结构可能擅长保存记录,却不擅长比较
如果出现奇怪的值,要回到哪里去看? 因为单靠问题表达结构,并不能解释所有细节原因

这三个问题,是快速区分 手上的记录已经是数据集,还是还只是数据集候选 的标准。第一个问的是行的含义,第二个问的是可比较性,第三个问的是何时要重新打开原始日志。数据建模,就是把已存记录重新表达成能够回答这三个问题的结构。

在存储结构里,重要的是尽可能完整地保留下来。反过来,在问题表达结构里,则必须判断 什么要留下什么可以舍弃。例如,一旦决定把一次完整动作看成一条样本,就可以不再保留数百条逐时刻记录,而改做出 总动作时长前段均值后段下降率跟踪误差 这样的新列。这不是在破坏存储结构,而是在为了回答别的问题,重新设计一种表达方式。

下面这个小表,会立刻看出同一份源数据会因为目的不同而被读成不同结构。

结构 示例列 一行表示什么
存储结构 timestamp, sensor_name, value 一个时间点记录
问题表达结构 event_id, mid_flow_mean, late_drop_rate 一次动作的摘要

用一个小图来看

如果按下面的顺序去读 记录保存为问题重新组织 是在哪里分开的,就会更清楚:已存记录并不会立刻变成数据集。

flowchart TD
    A[已存记录按时间点不断累积] --> B{现在一行到底表示什么}
    B --> C[一行只表示一个被记录的时点]
    C --> D[按问题需要的样本单位重新组织记录]
    D --> E[生成可比较的派生列]
    E --> F[这时才成为数据集候选]

问题情境:确认同一份源记录,在存储结构里是按时间点逐行保存,在数据集候选里则被重新组织成按动作汇总的摘要表。

输入(input):按 event_id 保存逐时刻记录的流量日志表,以及把一次动作承认为一个事件所需的最少时点数 min_points_per_event

期望输出(output):同一份记录被区分成 stored time-step recordsevent-level dataset candidate 这两种不同表角色,并且没有达到标准的事件会从比较候选中排除

要确认的概念:已经存在存储记录,并不等于已经准备好了能回答问题的数据集候选。min_points_per_event 一变,哪些记录能被承认为一次动作样本也会跟着变。

# 这个例子把按时间点存储的记录重新汇总成 event 级的数据集候选。
import pandas as pd

pd.set_option("display.max_columns", None)
pd.set_option("display.width", 120)

min_points_per_event = 3

storage_table = pd.DataFrame(
    [
        {"event_id": "A", "second": 0, "flow": 0.8},
        {"event_id": "A", "second": 1, "flow": 1.4},
        {"event_id": "A", "second": 2, "flow": 1.2},
        {"event_id": "B", "second": 0, "flow": 0.7},
        {"event_id": "B", "second": 1, "flow": 1.1},
        {"event_id": "B", "second": 2, "flow": 0.6},
        {"event_id": "C", "second": 0, "flow": 0.9},
        {"event_id": "C", "second": 1, "flow": 1.0},
    ]
)

dataset_candidate = (
    storage_table.groupby("event_id")
    .agg(
        point_count=("second", "count"),
        duration_seconds=("second", "max"),
        mean_flow=("flow", "mean"),
        late_drop_rate=("flow", lambda values: values.iloc[-1] - values.iloc[-2]),
    )
    .reset_index()
)
dataset_candidate["usable_as_event_sample"] = (
    dataset_candidate["point_count"] >= min_points_per_event
)
usable_candidate = dataset_candidate[dataset_candidate["usable_as_event_sample"]]

print("1) stored time-step records")
print(storage_table)
print()
print(f"2) event-level dataset candidate when min_points_per_event = {min_points_per_event}")
print(dataset_candidate.round(2))
print()
print("3) usable event-level rows for comparison")
print(usable_candidate.round(2))

期望输出:

1) stored time-step records
  event_id  second  flow
0        A       0   0.8
1        A       1   1.4
2        A       2   1.2
3        B       0   0.7
4        B       1   1.1
5        B       2   0.6
6        C       0   0.9
7        C       1   1.0

2) event-level dataset candidate when min_points_per_event = 3
  event_id  point_count  duration_seconds  mean_flow  late_drop_rate  usable_as_event_sample
0        A            3                 2       1.13            -0.2                    True
1        B            3                 2       0.80            -0.5                    True
2        C            2                 1       0.95             0.1                   False

3) usable event-level rows for comparison
  event_id  point_count  duration_seconds  mean_flow  late_drop_rate  usable_as_event_sample
0        A            3                 2       1.13            -0.2                    True
1        B            3                 2       0.80            -0.5                    True

这段输出里首先要看的,是 按原样展示同一份记录的表为了回答问题而重新做出来的表 之间的差别。第一张表里,一行只是一个时间点记录,因此 这次动作的平均流量后段下降率 还看不出来。只有重新组织成第二张表后,一次动作才真正变成一行,也只有这时,才能出现可以直接拿来比较的列。这里可以改动的值是 min_points_per_event。如果设为 3,C 即使有已存记录,也会从可比较的一次动作样本中排除。若降到 2,C 也会成为候选,但后段下降率能不能按同一个含义来比较,还需要重新追问。已经存在存储记录,并不等于已经准备好了能回答问题的数据集候选。

如果把同一份数据继续保持成存储结构不改,也可以很短地看出实际会卡在哪里。

我们马上想问的问题 直接使用存储结构时会出现的问题
这一次完整动作是不是比平时更长? 一行只是时间点记录,所以 一次动作的长度 无法直接看见
能不能只挑出后段下降较慢的动作? 如果不先把后段区间聚合成摘要,就没有比较列
能不能直接比较最近 20 次和此前 200 次? 存储结构里没有指向 一次动作近期区段 的比较单位

所以,存储结构擅长展示 记录了什么,却不会自动决定 什么该按一条案例来比较。所谓重新构造数据集候选,填补的正是这个空白。

这里有一点必须注意。问题表达结构并不会取代存储结构。做出了摘要表,并不意味着原始日志就不再需要。恰恰相反,一旦摘要表里出现奇怪变化,我们还得回到存储结构,重新检查细节时间点。存储结构负责保存证据,问题表达结构负责让比较成为可能。它们不是竞争关系,而是角色不同的连接结构。

把这种关系再压缩一点,可以写成下面这样。

问题 存储结构在这里强吗? 问题表达结构在这里强吗?
实际记录下来的值是什么? 只有一部分
这一次动作整体是什么结构? 很难
最近区段和以往相比是不是变了? 很难

这张表说明,存储结构和问题表达结构的差别,不只是 表长得不一样,而是 能回答的问题不一样。因为“这张表是怎么存的”和“这张表能回答什么问题”并不是同一个问题。所以在 Part 3 的前段,第一件事不是看着记录去想模型名字,而是追问:这些记录应该被重新读成什么样的数据集候选。

从更宽一点的角度看,这一节把 保存源记录重设分析单位生成派生表达 区分成不同层次的工作,并整理出把记录结构提升成问题表达结构的起始条件。

因此,说“已存记录还不是数据集”,比起表示 格式不同,更接近表示 用来回答问题的单位和派生表达还没有定下来

来源与参考资料

  • Google for Developers, Machine Learning Glossary 中的 examplelabeled examplefeature。它把 example 单位和 feature 角色分开说明,因此支持本节的核心判断:存储表中的一行,和可比较样本表中的一行,可能不是同一个意思。 https://developers.google.com/machine-learning/glossary / 确认日期: 2026-07-20
  • Oracle, Introduction to Data Warehousing Concepts. 它说明 data warehouse 是为了 business intelligence activities、query and analysis、维护历史记录和数据分析而设计的结构,因此支持开头的背景说明:DSS/BI/DW/OLAP 会把已存数据连接到决策和分析。 https://docs.oracle.com/en/database/oracle/oracle-database/26/dwhsg/introduction-data-warehouse-concepts.html / 确认日期: 2026-07-20
  • W3C, PROV-Overview. 它同时处理 provenance、derivation、traceability,因此强化了这个上位框架:存储结构保留原始证据,问题表达结构则为不同问题构造派生表达。 https://www.w3.org/TR/prov-overview/ / 确认日期: 2026-07-20
  • Hadley Wickham, Tidy Data, Journal of Statistical Software 59(10), 2014. 它整理了变量、观测值、表结构之间的关系,因此提供了一般原理,说明为什么存储结构中的一行和分析表中的一行不一定表示同一件事。 https://www.jstatsoft.org/article/view/v059i10 / 确认日期: 2026-07-20