C3 AI Documentation Home

Simple Metrics

Simple metrics are instructions for how to produce a Timeseries object from any stream of data that represents a time series.

Simple metrics can produce four categories of Timeseries objects: continuous numeric, continuous enumerated, discontinuous point events, and discontinuous interval events.

Continuous numericContinuous numeric has a standard time interval that can report a data point. The data point could be body temperature, heart rate, electricity consumption, etc.
Continuous enumeratedContinuous enumerated is similar to continuous numeric, except there is an enumerated, or itemized list of values each time is associated with, rather than any kind of specific numeric value. This can be a history of statuses, for example when a device was on or off.
Discontinuous point eventsDiscontinuous point events occur at any point in time and do not have a standard time interval. For example, a light bulb is switched on at 12:05 PM, and it's switched off at 12:15 PM.
Discontinuous interval eventsDiscontinuous interval events can occur at any point in time, but they are time bound. For example, an outage occurred from 3:03 PM to 3:05 PM.

Core fields

Simple metrics are instances of the SimpleMetric Type. Here are the key fields to specify when declaring a SimpleMetric Type:

  • id (string) — id is inherited from the Persistable Type. The naming convention of the Simple Metric id field should adhere to the following format: name_srcType. For example, AveragePower_SmartBulb should be the id of a metric that can output a Timeseries of the average power of a SmartBulb. These should be global and unique.

  • name (string) — The name should be the descriptor of what the metric does. For example, AveragePower could be the name of a metric which can report the average power series of a SmartBulb. The convention here is CamelCase.

  • srcType (string) — Entity for which to analyze data.

  • path (string) — The path field is the path from the srcType to the Type where the metric data gets evaluated. For cases where evaluation needs to happen on the same Type, do not specify the path. This can be timed-dependent if any of the following Types are in it:

  • expression (string) — The expression field is the expression that must be evaluated on the Type the path leads to. This is configured using the Expression Engine Function library.

    • Non-normalizable data
      • Constant function: expression: "identity(1)"
      • Piecewise constant function with aggregation across space: expression: "sum(length)"
      • Binary function: expression: "manufacturer == 'A'"
      • Ternary condition function: expression: "job == 'B' ? 1: 0"
      • String function: expression: "startsWith(model, 'X')"
    • Normalizable data
      • Continuous numeric function with aggregation across space and time: expression: "avg(avg(normalized.data.power))". Normalized keyword is not an actual field, it indicates that the metric engine should use normalized data
    • tsDecl, actionDecl are optional fields that may be used in place of expression

Expression details

Aggregating over time and space

For normalizable data, the expression can look to aggregate across space and time. Consider the expression defined below:

expression: "avg(avg(normalized.data.power))"

In the provided example, you can see two fields: data and power, which are linked together. It is important to note that these fields belong to different Types. In effect, these fields indicate a path saying to the effect of: go to the field data on a certain Type, which can then lead to another Type that has a field named power, that contains the data for analysis.

Assume the data for power is coming from two different sensors on the same light bulb. It is also measured at irregular frequencies, as shown in the image below in step (1).

Simple Metrics Power Data

Next, consider the expression normalizing that data. This is indicated by the keyword normalized (normalized.data.power). For more information about the keyword normalized, see Normalization Engine. In this example, the normalization interval is set to be a quarter hour with default zero interpolation and the default average treatment.

What this means is there should be one value for every 15 min interval which can be calculated using an average aggregation. In step (2) the two readings within the last interval for the first sensor were averaged, producing a value of 29. Additionally, note that in intervals for which there was no value, the normalization process placed a 0 – this is a zero interpolation.

The third step is inner aggregation, aggregation over time. The expression calls for an average as the aggregation method over time: (avg(normalized.data.power)) Assuming the interval for evaluation of the metric is specified as HOUR (meaning 1 value for each hour interval). The resulting data is averaged as shown.

The final fourth step is the outer aggregation, which is aggregation across space (or sources). (avg(avg(normalized.data.power))). This aggregates all separate Timeseries into a single Timeseries by averaging them, as seen in the example above.

Path details

There are three main steps in determining the path:

  1. Choose the source. The source is the Type on which to evaluate the metric. The SmartBulb Type is the source for evaluating the AveragePower metric.
  2. Determine where to locate the data. For AveragePower, it is defined as the field on the Type SmartBulbMeasurement.
  3. Figure out how to get from the source to the field that contains the data you need to analyze.

In determining the path, it is helpful to consider the data model, reproduced for this example below:

SmartBulb Data Model

The SmartBulb Type contains a field called bulbMeasurements, which connects to the SmartBulbMeasurementSeries Type. The SmartBulbMeasurementSeries Type serves as the timeseries header Type.

Inside the SmartBulbMeasurementSeries Type, the data field points to the SmartBulbMeasurement Type. The SmartBulbMeasurement Type is where you can find the power field, which holds the data for analysis.

To locate the power field, follow this path:

SmartBulb → bulbMeasurements → data → power

Here is the step-by-step process:

  1. Start with the bulbMeasurements field in the SmartBulb Type. This field links to the SmartBulbMeasurementSeries Type, which organizes the time series data.
  2. From the SmartBulbMeasurementSeries Type, use the data field to reach the SmartBulbMeasurement Type, which contains individual data points.
  3. Finally, access the power field in the SmartBulbMeasurement Type to retrieve the data needed for analysis.

Important observations to note:

  • The path bulbMeasurements gets you from the source Type to the header Type.
  • The expression data.power gets you from the header Type to the data (field) you want to analyze. The expression also dictates the logic to apply on the data: (avg(avg(normalized.data.power)))

Retracing the path

The path of information from source Type to data can be accessed by looking at the Type documentation. Begin at the source and select the link to the pertinent Type.

Retracing the path

The example Simple Metric definitions for the categories continuous numeric and discontinuous interval events are below:

Continuous numeric

JSON
{
  "id": "AveragePower_SmartBulb",
  "name": "AveragePower",
  "srcType": "SmartBulb",
  "description": "Average power over time of the smart bulb",
  "path": "bulbMeasurements",
  "expression": "avg(avg(normalized.data.power))"
}

Discontinuous interval events

JSON
{
  "id": "ManufacturerNameStartsWithG_SmartBulb",
  "name": "ManufacturerNameStartsWithG",
  "description": "True or false for duration of LightBulb",
  "srcType": "SmartBulb",
  "expression": "startsWith(lowerCase(manufacturer.name), 'g')"
}

See also

Was this page helpful?