DaZuiZui commented on issue #17939:
URL: https://github.com/apache/iotdb/issues/17939#issuecomment-4715596718

   ## 1. 功能定义
   
   `FFT` 是 IoTDB table model 中的内置表值函数,用于对按分区、按时间排序的实值数值序列执行快速傅里叶变换,并返回频域结果。
   
   它适合处理如下场景:
   
   - 对单个设备的一段时序数据做频域分析;
   - 对多个设备或 tag group 分别做 FFT;
   - 同时对多个数值列做 FFT,例如 `temperature`、`speed`、`voltage`;
   - 输出每个频率 bin 对应的复数结果,即实部和虚部。
   
   v1 只提供 `FFT`,不单独暴露 `DFT`。`FFT` 可视为频域分析的实际高性能实现。是否增加单独的 `DFT` 
TVF,可以在后续版本根据明确需求再讨论。
   
   ---
   
   ## 2. 语法定义
   
   ### 2.1 基本语法
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, device_id, temperature, speed
       FROM sensor
     ) PARTITION BY device_id ORDER BY time
   );
   ```
   
   ### 2.2 带显式采样间隔
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, device_id, temperature, speed
       FROM sensor
     ) PARTITION BY device_id ORDER BY time,
     SAMPLE_INTERVAL => 1ms
   );
   ```
   
   ### 2.3 只转换部分列
   
   `FFT` v1 不提供 `VALUE` 参数。如果用户只想转换部分数值列,应在 `DATA` 子查询中提前投影。
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, device_id, temperature
       FROM sensor
     ) PARTITION BY device_id ORDER BY time,
     SAMPLE_INTERVAL => 1s
   );
   ```
   
   ---
   
   ## 3. 参数定义
   
   ### `DATA`
   
   必选参数。
   
   `DATA` 是输入表参数,提供待转换的时间序列数据。
   
   ```sql
   DATA => (
     SELECT time, device_id, temperature, speed
     FROM sensor
   ) PARTITION BY device_id ORDER BY time
   ```
   
   含义:
   
   - `PARTITION BY`:定义 FFT 的分组单位。每个 partition 独立执行一次 FFT。
   - `ORDER BY`:定义输入序列顺序。v1 要求按时间列升序。
   - 非时间列、非分区列中的数值列会被自动识别为待转换 value columns。
   
   ### `SAMPLE_INTERVAL`
   
   可选参数。
   
   表示采样间隔,使用 duration literal,例如:
   
   ```sql
   SAMPLE_INTERVAL => 1ms
   SAMPLE_INTERVAL => 1s
   ```
   
   含义:
   
   - 如果提供 `SAMPLE_INTERVAL`,则使用该值计算物理频率;
   - 如果未提供,则在每个 partition 内根据时间列推断:
   
   ```text
   sample_interval = (last_time - first_time) / (row_count - 1)
   ```
   
   v1 中暂不提供 `SAMPLE_RATE`,避免和 `SAMPLE_INTERVAL` 同时存在时产生歧义。
   
   ---
   
   ## 4. 输入列规则
   
   ### 4.1 时间列
   
   v1 默认使用 `time` 列作为时间列。
   
   时间列用于:
   
   - 定义输入顺序;
   - 在未提供 `SAMPLE_INTERVAL` 时推断采样间隔;
   - 校验每个 partition 内时间是否升序。
   
   v1 暂不提供 `TIMECOL` 参数。如果未来 table model 中存在非默认时间列需求,可以再扩展。
   
   ### 4.2 分区列
   
   `PARTITION BY` 中出现的列作为 partition columns。
   
   这些列不会参与 FFT,但会原样保留到输出中,用于标识每一组 FFT 结果属于哪个设备或 tag group。
   
   ### 4.3 待转换数值列
   
   除时间列和分区列外,所有支持的数值列都会被转换。
   
   v1 支持的数据类型:
   
   ```text
   INT32
   INT64
   FLOAT
   DOUBLE
   ```
   
   这些列会被转换为 double 数组后参与 FFT。
   
   v1 不支持以下类型作为 value column:
   
   ```text
   BOOLEAN
   TEXT / STRING
   BLOB
   DATE
   TIMESTAMP
   ```
   
   如果 `DATA` 中除时间列和分区列外没有任何可转换的数值列,应报错。
   
   ---
   
   ## 5. 输出 Schema
   
   对于如下输入:
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, device_id, temperature, speed
       FROM sensor
     ) PARTITION BY device_id ORDER BY time,
     SAMPLE_INTERVAL => 1ms
   );
   ```
   
   输出 schema 为:
   
   ```text
   device_id
   frequency_index
   frequency
   temperature_real
   temperature_imag
   speed_real
   speed_imag
   ```
   
   ### 输出列含义
   
   ### partition columns
   
   来自 `PARTITION BY` 的列,例如:
   
   ```text
   device_id
   region
   factory_id
   ```
   
   用于标识当前 FFT 结果所属的输入分区。
   
   ### `frequency_index`
   
   类型:
   
   ```text
   INT64
   ```
   
   含义:
   
   FFT 输出数组中的下标,也就是 frequency bin index。
   
   对于长度为 `n` 的输入序列,v1 输出完整频谱,因此:
   
   ```text
   frequency_index = 0, 1, 2, ..., n - 1
   ```
   
   ### `frequency`
   
   类型:
   
   ```text
   DOUBLE
   ```
   
   含义:
   
   当前 frequency bin 对应的物理频率。
   
   计算逻辑与 `numpy.fft.fftfreq(n, d=sample_interval)` 对齐。
   
   对于长度为 `n`、采样间隔为 `d` 的输入序列:
   
   ```text
   frequency = frequency_index / (n * d)              when frequency_index < 
ceil(n / 2)
   frequency = (frequency_index - n) / (n * d)        otherwise
   ```
   
   因此 full spectrum 中后半部分会出现负频率。
   
   ### `<column>_real`
   
   类型:
   
   ```text
   DOUBLE
   ```
   
   含义:
   
   对应输入数值列 FFT 结果的实部。
   
   例如:
   
   ```text
   temperature_real
   speed_real
   ```
   
   ### `<column>_imag`
   
   类型:
   
   ```text
   DOUBLE
   ```
   
   含义:
   
   对应输入数值列 FFT 结果的虚部。
   
   例如:
   
   ```text
   temperature_imag
   speed_imag
   ```
   
   ---
   
   ## 6. 行为细节
   
   ### 6.1 每个 partition 独立执行 FFT
   
   例如:
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, device_id, temperature
       FROM sensor
     ) PARTITION BY device_id ORDER BY time
   );
   ```
   
   如果有三个 `device_id`,则每个设备各自执行一次 FFT,各自产生一组 frequency bins。
   
   ### 6.2 多个数值列共享同一频率轴
   
   对于同一个 partition,`temperature` 和 `speed` 使用相同的:
   
   ```text
   frequency_index
   frequency
   ```
   
   但各自有独立的复数结果:
   
   ```text
   temperature_real
   temperature_imag
   speed_real
   speed_imag
   ```
   
   ### 6.3 时间排序要求
   
   v1 要求每个 partition 内时间升序。
   
   如果发现某个 partition 中时间不是升序,应抛出异常。
   
   是否要求严格递增建议定义为:
   
   ```text
   time[i] < time[i + 1]
   ```
   
   也就是不允许相同时间戳。这样可以避免采样间隔推断和频率轴计算出现歧义。
   
   ### 6.4 采样间隔推断
   
   如果未提供 `SAMPLE_INTERVAL`:
   
   ```text
   sample_interval = (last_time - first_time) / (row_count - 1)
   ```
   
   如果某个 partition 的 `row_count < 2`,则无法推断采样间隔,应报错。
   
   如果提供了 `SAMPLE_INTERVAL`,则即使 partition 只有 1 行,也可以计算 FFT 和 frequency。
   
   ### 6.5 均匀采样假设
   
   v1 假设输入序列是均匀采样的。
   
   暂不校验每一对相邻时间戳的间隔是否完全一致,只校验时间升序。用户需要保证输入数据语义上是均匀采样序列。
   
   ### 6.6 Null 值处理
   
   建议 v1 采用简单规则:
   
   如果待转换数值列中存在 `NULL`,则报错。
   
   原因是 FFT 需要 dense numeric array。插值、填充、忽略 null 等行为都属于数据预处理策略,不建议在 v1 的 FFT TVF 
内隐式完成。用户可以在 `DATA` 子查询中提前过滤或填充。
   
   ---
   
   ## 7. v1 暂不支持的能力
   
   v1 不支持:
   
   - `DFT` 独立 TVF;
   - `VALUE` 参数;
   - `TIMECOL` 参数;
   - `SAMPLE_RATE` 参数;
   - `N` 参数;
   - `NORM` 参数;
   - `SPECTRUM` 参数;
   - one-sided spectrum;
   - amplitude / phase 输出列;
   - 自动插值或补齐不规则采样数据;
   - 对复数输入做 FFT。
   
   这些能力可以根据后续用户需求逐步加入。
   
   ---
   
   ## 8. 示例
   
   ### 示例 1:单设备单列 FFT
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, temperature
       FROM sensor
       WHERE device_id = 'd1'
     ) ORDER BY time,
     SAMPLE_INTERVAL => 1s
   );
   ```
   
   输出:
   
   ```text
   frequency_index | frequency | temperature_real | temperature_imag
   0               | 0.0       | ...              | ...
   1               | 0.25      | ...              | ...
   2               | -0.5      | ...              | ...
   3               | -0.25     | ...              | ...
   ```
   
   ### 示例 2:按设备分区
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, device_id, temperature
       FROM sensor
     ) PARTITION BY device_id ORDER BY time,
     SAMPLE_INTERVAL => 1ms
   );
   ```
   
   输出:
   
   ```text
   device_id | frequency_index | frequency | temperature_real | temperature_imag
   d1        | 0               | ...       | ...              | ...
   d1        | 1               | ...       | ...              | ...
   d2        | 0               | ...       | ...              | ...
   d2        | 1               | ...       | ...              | ...
   ```
   
   ### 示例 3:同一分区内转换多个数值列
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, device_id, temperature, speed
       FROM sensor
     ) PARTITION BY device_id ORDER BY time
   );
   ```
   
   输出:
   
   ```text
   device_id
   frequency_index
   frequency
   temperature_real
   temperature_imag
   speed_real
   speed_imag
   ```
   
   ### 示例 4:用户自行选择需要转换的列
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, device_id, speed
       FROM sensor
     ) PARTITION BY device_id ORDER BY time,
     SAMPLE_INTERVAL => 10ms
   );
   ```
   
   这里只有 `speed` 会被转换。
   
   ---
   
   ## 9. 推荐 v1 最小语法
   
   最终建议 v1 先落到这个最小形态:
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, partition_columns..., numeric_columns...
       FROM table_name
     ) PARTITION BY partition_columns... ORDER BY time,
     SAMPLE_INTERVAL => duration_literal
   );
   ```
   
   其中 `SAMPLE_INTERVAL` 可省略:
   
   ```sql
   SELECT *
   FROM FFT(
     DATA => (
       SELECT time, partition_columns..., numeric_columns...
       FROM table_name
     ) PARTITION BY partition_columns... ORDER BY time
   );
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to