跳转至

Time series in Functions(Functions 中的时间序列(Time Series))

Functions supports operations on time series properties. In this page, we cover how to set up and use time series properties in functions.

Initial set up

To use time series in Functions, you will need to have already stored time series in the Ontology. You can follow the steps outlined here to get started.

Once your time series are stored in the ontology, we need to create a code repository for our time series Functions. In this repository, we start by importing Ontology types so we can reference the time series stored in these Ontology types.

Now you're ready to work with time series in Functions.

Working with time series in Functions

To access time series in Functions, start by creating an object-backed Function in your code repository. These functions can directly access the time series properties in your Ontology. Once you have written a Function, there are two ways to run your new function. You can either test your Function in live preview or publish your Function and start using it in other applications throughout the platform.

Python functions

:::callout{theme="warning"} The FoundryTS library is not compatible with functions. Instead, the Python OSDK offers an alternative for accessing time series data through Ontology properties. :::

Access data

The Python OSDK allows you to read data points from time series properties (TSPs) into pandas or Polars DataFrames.

The DataFrame contains two columns:

Column name Type Description
timestamp pandas.Timestamp polars.datatypes.Datetime
value Union[float, str] Value of the point
@function
def aircraft_altimeter_mean(aircraft: Aircraft) -> float:
    """Get the mean of altimeter readings for a given Ontology aircraft object."""
    df = aircraft.altimeter.to_pandas(all_time=True)
    return df["value"].mean()
@function
def aircraft_altimeter_mean(aircraft: Aircraft) -> float:
    """Get the mean of altimeter readings for a given Ontology aircraft object."""
    df = aircraft.altimeter.to_polars(all_time=True)
    return df.select(pl.col("value").mean()).item()

Return time series

To return time series data from a Python function, import the timeseries_sdk library and use the TimeSeries.serialize method to format your return DataFrame as a list[bytes]. You can then visualize this using the code function time series card in Quiver.

:::callout{theme="warning"} Categorical time series outputs are not supported. :::

import pandas as pd
from timeseries_sdk.functions.types import TimeSeries
from ontology_sdk.ontology.objects import Aircraft

@function
def sample_series() -> list[bytes]:
    df = pandas.DataFrame({
        "timestamp": [
            pd.Timestamp("2025-03-19 08:00:00"),
            pd.Timestamp("2025-03-19 09:00:00"),
            pd.Timestamp("2025-03-19 10:00:00")
        ],
        "value": [1.0, 2.0, 2.5]
    })
    return TimeSeries.serialize(df)

@function
def normalized_altimeter_rate(aircraft: Aircraft) -> list[bytes]:
    """
    Read an aircraft's altimeter TSP, smooth it, compute its rate of change,
    then normalize by the maximum absolute rate.
    """
    df = aircraft.altimeter.to_pandas(all_time=True)
    if df.empty:
        return TimeSeries.serialize(df)

    df["value"] = df["value"].rolling(window=10, min_periods=1).mean()

    time_diff_seconds = df["timestamp"].diff().dt.total_seconds()
    df["value"] = (df["value"].diff() / time_diff_seconds).fillna(0)

    max_abs_rate = df["value"].abs().max()
    if max_abs_rate > 0:
        df["value"] = df["value"] / max_abs_rate

    return TimeSeries.serialize(df)

TypeScript functions

The following sections provide examples of common TypeScript function operations.

Return the first or last point

Since functions do not have a built-in type for time series points, you can instead return the value or the timestamp. For example, the following function reads the latest temperature on a machine:

    @Function()
    public async getLatestTemperature(machine: MachineRoot): Promise<Double | undefined> {
        const latest = await machine.temperatureId?.getLastPointV2();
        return latest?.value;
    }

You can similarly get the first temperature reading with the following function:

    @Function()
    public async getEarliestTemperature(machine: MachineRoot): Promise<Double | undefined> {
        const earliest = await machine.temperatureId?.getFirstPointV2();
        return earliest?.value;
    }

Aggregate over a series

One useful aggregation is to compute the average over a range of points. Consider the following function that gets the average temperature of an example machine:

    @Function()
    public async getAverageTemperature(machine: MachineRoot): Promise<Double | undefined> {
        const aggregation = await machine.temperatureId?.aggregate()
            .overEntireRange()
            .mean()
            .compute();
        return aggregation?.mean!;
    }

Take the derivative

Building on the example above, you can also retrieve the average change in temperature on the same machine by using the following compute function:

    @Function()
    public async getAverageTemperature(machine: MachineRoot): Promise<Double | undefined> {
        const aggregation = await machine.temperatureId?.derivative()
            .aggregate()
            .overEntireRange()
            .mean()
            .compute();
        return aggregation?.mean;
    }

Specify a time range

You can apply other transforms in addition to derivatives to a time series. The following is an example of how you can apply timestamp parameters as a range on a time series:

    @Function()
    public async getAverageTemperatureOverRange(
        machine: MachineRoot,
        start: Timestamp,
        end: Timestamp): Promise<Double | undefined>
    {
        const latest = await machine.temperatureId?.timeRange({min: start, max: end})
            .aggregate()
            .overEntireRange()
            .mean()
            .compute();
        return latest?.mean;
    }

中文翻译

Functions 中的时间序列(Time Series)

Functions 支持对时间序列属性进行操作。本文将介绍如何在 Functions 中设置和使用时间序列属性。

初始设置

要在 Functions 中使用时间序列,您需要先将时间序列存储在本体论(Ontology)中。您可以按照此处概述的步骤开始操作。

将时间序列存储到本体论后,我们需要为时间序列 Functions 创建一个代码仓库(Code Repository)。在此仓库中,我们首先导入本体论类型(Ontology Types),以便引用存储在这些本体论类型中的时间序列。

至此,您已准备好开始在 Functions 中处理时间序列。

在 Functions 中处理时间序列

要在 Functions 中访问时间序列,请先在代码仓库中创建一个基于对象的函数(Object-backed Function)。这些函数可以直接访问本体论中的时间序列属性。编写好函数后,有两种方式可以运行您的新函数:您可以在实时预览中测试函数,也可以发布函数并在平台的其他应用程序中使用它。

Python 函数

:::callout{theme="warning"} FoundryTS 库与 Functions 不兼容。Python OSDK 提供了另一种通过本体论属性访问时间序列数据的方式。 :::

访问数据

Python OSDK 允许您从时间序列属性(TSPs)中读取数据点,并将其导入 pandas 或 Polars 的 DataFrame 中。

DataFrame 包含两列:

列名(Column name) 类型(Type) 描述(Description)
timestamp pandas.Timestamp polars.datatypes.Datetime
value Union[float, str] 数据点的值
@function
def aircraft_altimeter_mean(aircraft: Aircraft) -> float:
    """获取指定本体论飞机对象的高度计读数平均值。"""
    df = aircraft.altimeter.to_pandas(all_time=True)
    return df["value"].mean()
@function
def aircraft_altimeter_mean(aircraft: Aircraft) -> float:
    """获取指定本体论飞机对象的高度计读数平均值。"""
    df = aircraft.altimeter.to_polars(all_time=True)
    return df.select(pl.col("value").mean()).item()

返回时间序列

要从 Python 函数返回时间序列数据,请导入 timeseries_sdk 库,并使用 TimeSeries.serialize 方法将返回的 DataFrame 格式化为 list[bytes]。然后,您可以使用 Quiver 中的代码函数时间序列卡片来可视化该数据。

:::callout{theme="warning"} 不支持分类时间序列(Categorical Time Series)输出。 :::

import pandas as pd
from timeseries_sdk.functions.types import TimeSeries
from ontology_sdk.ontology.objects import Aircraft

@function
def sample_series() -> list[bytes]:
    df = pandas.DataFrame({
        "timestamp": [
            pd.Timestamp("2025-03-19 08:00:00"),
            pd.Timestamp("2025-03-19 09:00:00"),
            pd.Timestamp("2025-03-19 10:00:00")
        ],
        "value": [1.0, 2.0, 2.5]
    })
    return TimeSeries.serialize(df)

@function
def normalized_altimeter_rate(aircraft: Aircraft) -> list[bytes]:
    """
    读取飞机的高度计时间序列属性,进行平滑处理,计算其变化率,
    然后通过最大绝对变化率进行归一化。
    """
    df = aircraft.altimeter.to_pandas(all_time=True)
    if df.empty:
        return TimeSeries.serialize(df)

    df["value"] = df["value"].rolling(window=10, min_periods=1).mean()

    time_diff_seconds = df["timestamp"].diff().dt.total_seconds()
    df["value"] = (df["value"].diff() / time_diff_seconds).fillna(0)

    max_abs_rate = df["value"].abs().max()
    if max_abs_rate > 0:
        df["value"] = df["value"] / max_abs_rate

    return TimeSeries.serialize(df)

TypeScript 函数

以下各节提供了常见 TypeScript 函数操作的示例。

返回第一个或最后一个数据点

由于 Functions 没有内置的时间序列数据点类型,您可以改为返回值或时间戳。例如,以下函数读取机器的最新温度:

    @Function()
    public async getLatestTemperature(machine: MachineRoot): Promise<Double | undefined> {
        const latest = await machine.temperatureId?.getLastPointV2();
        return latest?.value;
    }

您也可以使用以下函数获取第一个温度读数:

    @Function()
    public async getEarliestTemperature(machine: MachineRoot): Promise<Double | undefined> {
        const earliest = await machine.temperatureId?.getFirstPointV2();
        return earliest?.value;
    }

对序列进行聚合

一种常用的聚合操作是计算一系列数据点的平均值。以下函数获取示例机器的平均温度:

    @Function()
    public async getAverageTemperature(machine: MachineRoot): Promise<Double | undefined> {
        const aggregation = await machine.temperatureId?.aggregate()
            .overEntireRange()
            .mean()
            .compute();
        return aggregation?.mean!;
    }

计算导数

基于上述示例,您还可以通过以下计算函数获取同一机器的平均温度变化:

    @Function()
    public async getAverageTemperature(machine: MachineRoot): Promise<Double | undefined> {
        const aggregation = await machine.temperatureId?.derivative()
            .aggregate()
            .overEntireRange()
            .mean()
            .compute();
        return aggregation?.mean;
    }

指定时间范围

除了导数之外,您还可以对时间序列应用其他变换。以下示例展示了如何将时间戳参数作为范围应用于时间序列:

    @Function()
    public async getAverageTemperatureOverRange(
        machine: MachineRoot,
        start: Timestamp,
        end: Timestamp): Promise<Double | undefined>
    {
        const latest = await machine.temperatureId?.timeRange({min: start, max: end})
            .aggregate()
            .overEntireRange()
            .mean()
            .compute();
        return latest?.mean;
    }