Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: gap fill #199

Merged
merged 7 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions en_US/sql-reference/gap_fill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Gap Fill
3 changes: 2 additions & 1 deletion zh_CN/sql-reference/date.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ aside: false
| now() | | TIMESTAMP\_NS | 返回精度为纳秒的配置时区的时间 |
| current\_date() | | Date | 返回当前日期 |
| current\_time() | | String | 返回精度为纳秒的UTC当前时间,不包含日期 |
| date\_bin(interval, expression[, origin-timestamp]) | (INTERVAL<sup>1</sup>, exp[, TIMESTAMP]) | TIMESTAMP | 根据输入的 interval 时间单位对 expression 进行截断,可以指定 origin-timestamp 作为起始时间,不指定则默认为 UNIX epoch in UTC,例如: date\_bin('1 hour', ts) 表示按照每一小时进行截断 |
| date\_bin(interval, expression[, origin-timestamp]) | (INTERVAL<sup>1</sup>, exp[, TIMESTAMP]) | TIMESTAMP | 根据输入的 interval 时间单位对 expression 进行截断,可以指定 origin-timestamp 作为起始时间,不指定则默认为 UNIX epoch in UTC,例如: date\_bin('interval 1 hour', ts) 表示按照每一小时进行截断 |
| date\_bin\_gapfill(interval, expression[, origin-timestamp]) | (INTERVAL<sup>1</sup>, exp[, TIMESTAMP]) | TIMESTAMP | 根据输入的 interval 时间单位对 expression 进行截断,并补全所有缺失的时间窗口。可以指定 origin-timestamp 作为起始时间,不指定则默认为 UNIX epoch in UTC |
| date\_trunc(precision, expression) | (PRECISION<sup>2</sup>, exp) | TIMESTAMP | 根据输入的 precision 精度单位对 expression 进行截断 |
| datetrunc(precision, expression) | | | date\_trunc 的别名 |
| date\_part(part, expression) | (PART<sup>3</sup>, exp) | NUMERIC | 根据指定的 part 获取 expression 的指定部分,例如: date\_part('hour', now()) |
Expand Down
190 changes: 190 additions & 0 deletions zh_CN/sql-reference/gap_fill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# 插值

对时序数据进行分析或观测,通常需要对数据进行降采样,然后针对每个采样窗口中的数据应用聚合函数。
由于原始数据缺失或稀疏,或者采样窗口选取过大,可能导致某些采样窗口中没有数据。这不利于分析或观测时序数据。

为此,Datalayers 提供了插值功能,支持补全查询时间范围内缺失的采样窗口,并使用给定的插值函数对缺失的值进行填充。
补全的功能由 `date_bin_gapfill` 函数提供,插值的功能由插值函数提供。

## 基础语法

为了实现插值,您需要:

1. 对时间戳列应用 `date_bin_gapfill` 函数,并将其作为分组列之一。
- 函数签名:`date_bin_gapfill(<interval>, <timestamp_column>[, <origin>])`。
- `interval`:采样窗口大小,可以为 `INTERVAL` 类型的字面值,或者结果为 `INTERVAL` 类型的表达式。
- `timestamp_column`:时间戳列,列约束中必须包含 NOT NULL。
- `origin`:可选参数,指定根据哪个时间戳以对齐时间窗口。
2. 对聚合列应用插值函数。
- 线性插值:`fill_linear(<aggregate_col>)`。
- 使用该列前一个非空值填充:`fill_prev(<aggregate_col>)`。
- 使用给定值填充:`fill_value(<aggregate_col>, <value>)`。
3. 使用 WHERE 从句给定查询的时间范围。
- 时间范围必须为有限范围,即有限的起始时间戳与有限的结束时间戳。

语法示例:

``` sql
SELECT
date_bin_gapfill(interval 1 hour, time) as window,
fill_linear(avg(usage_user)),
fill_prev(avg(usage_system)),
fill_value(avg(usage_idle), 25.0)
FROM cpu
WHERE time >= '2024-01-16T08:00:00' AND time <= '2024-01-17T12:00:00'
GROUP BY window
```

## date_bin_gapfill 函数

我们提供了一个 [`date_bin`](./date.md) 函数用来将数据划分到采样窗口。在此基础上,`date_bin_gapfill` 函数还会额外补全缺失的采样窗口。让我们用一个简单的例子来解释。

给定原始时间序列:

``` text
+---------------------------+
| 2024-01-16T10:40:00+08:00 |
| 2024-01-16T11:40:00+08:00 |
| 2024-01-16T12:40:00+08:00 |
+---------------------------+
```

将采样窗口大小设定为 30 分钟,应用 `date_bin` 函数后,每个时间戳被划分到它所属的采样窗口。
例如, `2024-01-16T10:40:00+08:00` 这条记录被划分到起始时间戳为 `2024-01-16T10:30:00+08:00` 的采样窗口。

``` text
+---------------------------+
| 2024-01-16T10:30:00+08:00 |
| 2024-01-16T11:30:00+08:00 |
| 2024-01-16T12:30:00+08:00 |
+---------------------------+
```

使用同样的窗口大小,应用 `date_bin_gapfill` 函数后,缺失的采样窗口得到补全。
例如,在起始时间为 `2024-01-16T10:30:00+08:00` 的窗口和起始时间为 `2024-01-16T11:30:00+08:00` 的窗口之间存在一个缺失的、起始时间为 `2024-01-16T11:00:00+08:00` 的采样窗口。这些缺失的采样窗口会被补全。

``` text
+---------------------------+
| 2024-01-16T10:30:00+08:00 |
| 2024-01-16T11:00:00+08:00 |
| 2024-01-16T11:30:00+08:00 |
| 2024-01-16T12:00:00+08:00 |
| 2024-01-16T12:30:00+08:00 |
+---------------------------+
```

与 `date_bin` 函数类似,`date_bin_gapfill` 函数也支持一个可选的 `origin` 参数。这个参数控制采样窗口根据哪个时间戳进行对齐。
`origin` 默认为 UTC 时区的 `1970-01-01T00:00:00` 时间。
对于我们的例子,如果将 `origin` 设置为 `1970-01-01T00:05:00`,则应用 `date_bin_gapfill` 后的结果为:

``` text
+---------------------------+
| 2024-01-16T10:35:00+08:00 |
| 2024-01-16T11:05:00+08:00 |
| 2024-01-16T11:35:00+08:00 |
| 2024-01-16T12:05:00+08:00 |
| 2024-01-16T12:35:00+08:00 |
+---------------------------+
```

## 插值函数

插值函数用来对每个缺失的采样窗口中的聚合列进行填充。我们目前支持三种填充策略,分别对应三个插值函数:

- `fill_linear`:对于一个缺失行,使用该列前一个非空行以及后一个非空行的线性插值进行填充。如果前或后没有非空行,则不填充。
- `fill_prev`:对于一个缺失行,使用该列前一个非空行的值进行填充。如果前面没有非空行,则不填充。
- `fill_value`:对于一个缺失行,使用给定的值进行填充。

特别注意,如果 SQL 语句中包含时间戳列之外的分组列,那么插值会针对每个分组单独执行。这是考虑到不同分组的数据可能没有显著的关联,如果允许跨分组插值,可能得到异常点(Outlier)。

## 其他注意事项

- `date_bin_gapfill` 只能作为投影列,且必须作为分组列。
- 插值函数只能作为投影列,不能出现在其他从句中。
- `date_bin_gapfill` 和插值函数只能作为最外层的表达式。例如 `count(date_bin_gapfill(..))`、`max(fill_prev(avg(usage_user)))` 都是不允许的。
- `date_bin_gapfill` 在一个 SQL 中最多只能出现一次。
- `date_bin_gapfill` 的 `interval` 和 `origin` 参数可以为子面量,也可以为表达式,只要表达式的类型符合要求。
- 插值函数必须和 `date_bin_gapfill` 函数联合使用,但是 `date_bin_gapfill` 函数可以单独使用。
- 每个聚合列最多只能应用一个插值函数。例如 `SELECT date_bin_gapfill(...), fill_linear(avg(usage_user)), fill_prev(avg(usage_user)) FROM CPU ...` 是不允许的。
- 插值后的结果总会使用分组列进行排序。
- 插值后的数据的时间范围为 `WHERE` 从句中指定的时间范围与每个分组内数据的时间范围的较大者。
- `fill_linear`、`fill_prev`、`fill_value` 实际上是函数的别名。它们的标准名称分别为 `interpolate`、`locf`、`value`。在 SQL 语句中,可以随意使用标准名称或者别名。但是在打印的结果集中,我们总是使用标准名称作为列名。

## 示例

给定 `cpu` 表的原始数据如下:

``` text
+---------------------------+------------+------------+--------------+------------+
| time | region | usage_user | usage_system | usage_idle |
+---------------------------+------------+------------+--------------+------------+
| 2024-01-16T10:40:00+08:00 | us-west | 55.0 | 25.0 | 20.0 |
| 2024-01-16T11:40:00+08:00 | us-east | 56.5 | 24.0 | 19.5 |
| 2024-01-16T12:40:00+08:00 | us-central | 54.0 | 26.0 | 20.0 |
| 2024-01-16T15:40:00+08:00 | eu-west | 58.0 | 22.0 | 20.0 |
| 2024-01-16T18:40:00+08:00 | ap-south | 55.5 | 25.0 | 19.5 |
| 2024-01-16T19:40:00+08:00 | eu-east | 59.0 | 21.5 | 19.5 |
| 2024-01-16T22:40:00+08:00 | us-west | 57.0 | 23.5 | 19.5 |
| 2024-01-17T04:40:00+08:00 | us-east | 60.0 | 20.5 | 19.5 |
| 2024-01-17T05:40:00+08:00 | us-central | 58.5 | 22.5 | 19.0 |
| 2024-01-17T08:40:00+08:00 | eu-west | 61.0 | 19.5 | 19.5 |
| 2024-01-17T11:40:00+08:00 | ap-south | 59.5 | 20.0 | 20.5 |
| 2024-01-17T15:20:00+08:00 | eu-east | 60.5 | 19.0 | 20.5 |
| 2024-01-17T18:20:00+08:00 | us-west | 62.0 | 18.5 | 19.5 |
| 2024-01-17T21:20:00+08:00 | us-east | 63.0 | 18.0 | 19.0 |
| 2024-01-18T00:20:00+08:00 | us-central | 61.5 | 19.0 | 19.5 |
| 2024-01-18T03:20:00+08:00 | eu-west | 62.5 | 17.5 | 20.0 |
| 2024-01-18T06:20:00+08:00 | ap-south | 63.5 | 17.0 | 19.5 |
+---------------------------+------------+------------+--------------+------------+
```

将 `region` 和 `time` 作为分组列,使用 `date_bin_gapfill` 对数据进行分组以及窗口补全,
查询 `region = 'eu-west'` 分组内 `usage_user`、`usage_system`、`usage_idle` 三个列的平均值:

``` text
+---------------------------+---------+---------------------+-----------------------+---------------------+
| bin | region | avg(cpu.usage_user) | avg(cpu.usage_system) | avg(cpu.usage_idle) |
+---------------------------+---------+---------------------+-----------------------+---------------------+
| 2024-01-16T08:00:00+08:00 | eu-west | | | |
| 2024-01-16T11:00:00+08:00 | eu-west | | | |
| 2024-01-16T14:00:00+08:00 | eu-west | 58.0 | 22.0 | 20.0 |
| 2024-01-16T17:00:00+08:00 | eu-west | | | |
| 2024-01-16T20:00:00+08:00 | eu-west | | | |
| 2024-01-16T23:00:00+08:00 | eu-west | | | |
| 2024-01-17T02:00:00+08:00 | eu-west | | | |
| 2024-01-17T05:00:00+08:00 | eu-west | | | |
| 2024-01-17T08:00:00+08:00 | eu-west | 61.0 | 19.5 | 19.5 |
| 2024-01-17T11:00:00+08:00 | eu-west | | | |
| 2024-01-17T14:00:00+08:00 | eu-west | | | |
| 2024-01-17T17:00:00+08:00 | eu-west | | | |
| 2024-01-17T20:00:00+08:00 | eu-west | | | |
| 2024-01-17T23:00:00+08:00 | eu-west | | | |
| 2024-01-18T02:00:00+08:00 | eu-west | 62.5 | 17.5 | 20.0 |
| 2024-01-18T05:00:00+08:00 | eu-west | | | |
+---------------------------+---------+---------------------+-----------------------+---------------------+
```

在以上查询中加入插值函数,使用 `interpolate`、`locf`、`value` 插值函数分别对 `usage_user`、`usage_system`、`usage_idle` 列的平均值,进行填充:

``` text
+---------------------------+---------+----------------------------------+-----------------------------+----------------------------------------+
| window | region | interpolate(avg(cpu.usage_user)) | locf(avg(cpu.usage_system)) | value(avg(cpu.usage_idle),Float64(25)) |
+---------------------------+---------+----------------------------------+-----------------------------+----------------------------------------+
| 2024-01-16T08:00:00+08:00 | eu-west | | | 25.0 |
| 2024-01-16T11:00:00+08:00 | eu-west | | | 25.0 |
| 2024-01-16T14:00:00+08:00 | eu-west | 58.0 | 22.0 | 20.0 |
| 2024-01-16T17:00:00+08:00 | eu-west | 58.5 | 22.0 | 25.0 |
| 2024-01-16T20:00:00+08:00 | eu-west | 59.0 | 22.0 | 25.0 |
| 2024-01-16T23:00:00+08:00 | eu-west | 59.5 | 22.0 | 25.0 |
| 2024-01-17T02:00:00+08:00 | eu-west | 60.0 | 22.0 | 25.0 |
| 2024-01-17T05:00:00+08:00 | eu-west | 60.5 | 22.0 | 25.0 |
| 2024-01-17T08:00:00+08:00 | eu-west | 61.0 | 19.5 | 19.5 |
| 2024-01-17T11:00:00+08:00 | eu-west | 61.25 | 19.5 | 25.0 |
| 2024-01-17T14:00:00+08:00 | eu-west | 61.5 | 19.5 | 25.0 |
| 2024-01-17T17:00:00+08:00 | eu-west | 61.75 | 19.5 | 25.0 |
| 2024-01-17T20:00:00+08:00 | eu-west | 62.0 | 19.5 | 25.0 |
| 2024-01-17T23:00:00+08:00 | eu-west | 62.25 | 19.5 | 25.0 |
| 2024-01-18T02:00:00+08:00 | eu-west | 62.5 | 17.5 | 20.0 |
| 2024-01-18T05:00:00+08:00 | eu-west | | 17.5 | 25.0 |
+---------------------------+---------+----------------------------------+-----------------------------+----------------------------------------+
```