数据与分析

数据准备

原始表很少能单独回答一个问题。数据准备把它们联接起来、把列的类型改对,并把整套配方保存下来,好让明天的数据走完全相同的步骤。

位置在 数据 → 数据目录 → 数据准备。产出是一张新的已准备表,与其他表别无二致:可以作图、可以查询、可以挂到智能体上。

为什么这样设计

数据准备的意义不在于「做变换」——那用 SQL 也能做——而在于 可重复性。一个保存好的流程会对刷新后的源数据重跑同样的联接与类型转换,于是下个月的文件里某列改了名时,仪表板不会悄悄腐坏。

画布

左侧面板列出你可以引入的东西,并按它所在的位置分组,这样你随时清楚自己碰的是本地副本还是活的系统:

  • 本地表 ——已上传的文件和此前准备好的表,都已在工作区内。
  • 外部表 ——已接入的数仓与数据库,按 schema.table 展开,这样你能看清自己选的到底是哪个对象。点击某一项会把一份有上限的快照带上画布供你设计,而不是把几百万行拉进浏览器。

拖入一张表让它成为基表,再加入更多表并连接起来。每一步都会实时预览结果行,于是联接出错你会立刻发现,而不是拖到最后。

orders ──┐
         ├── join (customer_id) ──▶ filter ──▶ computed column ──▶ prepared table
customers┘                          status=       margin =
                                    'shipped'     revenue - cost
一个流程就是一张基表加上一串有序的步骤。

联接

选好两侧的键列和联接类型。四种类型,用平实的话说:

内联接
只保留两侧都匹配上的行。未匹配的行会被静默丢掉——之后记得核对行数。
左联接
保留基表的每一行;右侧缺失的值变成 null。当基表就是你的分析单元时,这是安全的默认选择。
右联接
左联接的镜像。通常把两张表调换位置、改用左联接会更清楚。
全联接
两侧全部保留。适合做对账——找出只在一侧存在的记录。

盯住行数

如果联接让行数翻倍,说明某一侧的键不唯一,于是下游每个度量都在重复计算。预览里的行数是发现这件事最省的办法。

列类型

每一列都带有类型,导入时设定,可在准备画布里修改。类型决定下游能用哪些筛选、聚合和图表,所以在这里弄对能省掉之后的排查。

类型适用于
text自由文本
integer整数
decimal小数
date日期——日期筛选和时序图表必需
boolean真 / 假
location地名或地区代码——可启用地图类图表
category一小组反复出现的取值;天然的分组维度
currency金额——下游按货币格式显示
percent比率与占比
id标识符——不会出现在聚合建议里,因为把 id 加起来毫无意义

九种步骤

步骤按顺序生效。任何一步都可以删除,预览会随之更新。

步骤作用配置
计算字段 calc使用公式添加列名称、表达式,以及结果列的类型
筛选行 filter仅保留符合条件的行一个或多个条件,用 AND 或 OR
汇总 aggregate分组汇总分组列,加上度量(求和、平均、计数、去重计数、最小值、最大值)
追加行 append合并另一个数据集中的行源表、要保留的列,以及模式 all 或 distinct
透视 pivot将行值转换为列要展开的列,以及用什么值填充
逆透视 unpivot将列转换为行(宽表 → 长表)要逆透视的列,以及键 / 值两列的名称
拆分列 split将文本拆分到多列源列与分隔符
移除重复项 dedupe删除重复行由哪些列判定为重复
查找和替换 replace替换列中的值列、匹配内容与替换内容

编写计算字段

表达式用的是 SQL,不是电子表格那套公式语言。它会被放进一条 SELECT 作为 (your expression) AS your_column_name里,所以凡是 SQL 选择列表里合法的东西都能用——算术、函数、 CASE,以及对流程中该位置可用的任意列的引用。

SQL
-- arithmetic across columns
revenue - cost

-- a rate, guarding the divide-by-zero that would otherwise produce nulls
CASE WHEN visits > 0 THEN conversions * 1.0 / visits ELSE 0 END

-- bucketing, for grouping later in the flow
CASE
  WHEN amount >= 10000 THEN 'enterprise'
  WHEN amount >= 1000  THEN 'mid-market'
  ELSE 'smb'
END

-- text tidying
lower(trim(email))

-- a month key to summarise by
date_trunc('month', ordered_at)

方言始终是 DuckDB

本地数据集在浏览器预览和服务端定时刷新时都跑在 DuckDB 上——刻意用同一个引擎,这样你在预览里看到的就是定时任务产出的。这意味着 DuckDB 的函数库你都可以用: date_trunc, strftime, regexp_extract, list_aggregate、窗口函数,以及其余各项。

顺序有影响,命名也有影响

步骤按顺序执行,所以计算字段只能引用位于它 之上 的那些列——包括更早的计算字段,而这正是分成若干可读片段层层搭建的预期用法。给每一个都起一个你乐意看到出现在图表坐标轴上的名字:它会成为一个真实的列,仪表板、度量和智能体都按这个名字引用它,之后改名会让所有已经指向它的东西失效。

筛选运算符

除了常规的比较运算符,大家常找的是文本类和空值类运算符: contains, starts with, ends with, is empty 和 is not empty.

「为空」不等于「等于空字符串」

一列里可以存真正的空字符串,也可以存 null,两者的筛选行为不同。如果某个筛选返回的行数比预期少,先查清你的数据里实际是哪一种—— 数据目录 里的列画像会显示空值率。

保存与刷新

  1. 1

    保存流程

    保存的是配方——数据源、联接、各步骤——而不只是产出结果。
  2. 2

    运行它

    产出(或替换)那张已准备表。它会出现在数据目录里,标记为「已准备」。
  3. 3

    给它排计划

    设一个刷新节奏,让这张已准备表按当前的源数据重建。建立在它之上的一切——仪表板、智能体、度量——都会跟着更新。

血缘

已准备表会记录它是由什么构建的,可在 数据目录里查看。删除或重构某张源表之前,先在那里看看有什么依赖它。

什么时候不该用数据准备

  • 一次性的答案 ——直接在 SQL 工作台里写查询就行。
  • 组织内必须达成一致的口径 ——「活跃客户」这类共用定义应该放进 语义层,在那里定义一次、处处复用,而不是烧死在某一张已准备表里。
  • 超大表上的重型变换 ——把它下推到数仓里做,再把结果接进来。