C3 AI Documentation Home

Create Metrics and Methods

In the Create an Entity tutorial, you created a simple persistable Type to represent entities in a global data tracking application's object model.

In the Model Data as Time Series tutorial, you expanded the Types in your object model to model time series data created by City entities in the object model.

Metrics and methods provide additional functionality to data in your object model for downstream applications. For example, metrics can be used as inputs for machine learning or can be displayed in the application user interface.

This tutorial assumes you have completed the following tutorials:

  1. Access Developer Tools
  2. Create an Entity
  3. Create Entity Relationships
  4. Model Time Series Data

In this tutorial, you create metrics and methods to further process the time series data in your object model for downstream use.

Metrics

Metrics are the basic building blocks for using the data stored in Types in your object model. There are two types of metrics: simple metrics and compound metrics:

  • Simple metrics: A simple metric defines a way to access data by transforming raw or normalized data into a Timeseries object. Simple metrics are tied to a single source Type. To learn more about simple metrics, see Simple Metrics.
  • Compound metrics: Defines a way to manipulate existing time series objects into new metrics with logical and mathematical operators. A compound metric can have both simple and compound metrics as inputs. To learn more about compound metrics, see Compound Metrics.

In this section, you create simple and compound metrics to make use of the data stored in the Types you created in previous tutorials.

Create a Simple Metric

To create a simple metric on a Type, you must perform the following:

  1. Create a metric .json file in the metadata/SimpleMetric folder of your application: This file defines the parameters and evaluation expression for the metric.
  2. Mixin the FeatureEvaluatable Type for the source Type: This allows the source type to support the evaluation of metrics.

The following code block displays the syntax to create a new simple metric in your application:

JavaScript
// MySimpleMetric_MyType.json
// An example simple metric
{
    // The unique id
    "id": "MySimpleMetric_MyType",
    // The unique name for this simple metric
    "name":"MySimpleMetric",
    // A description for what this metric does
    "description": "Calculates a metric for a given MyType",
    // The source Type this metric runs against
    "srcType": "MyType",
    // The path to the time series header field to use during evaluation
    "path": "pathToData",
    // The evaluation expression for the metric
    "expression": "expressionEngineFunction"
}

Create a new simple metric called AvgTemperature to evaluate the average temperature for a City over time. The metric should have the following characteristics:

  • The srcType for the metric should be City
  • The path should be the dot notation path from the srcType Type to the header Type
  • The expression field contains an expression engine expression that evaluates the temperature field across time and space.
    • The expression should return the avg value for temperature across time and City instances.

The following code block displays the completed AvgTemperature_City.json file to define the new simple metric:

JavaScript
// Simple metric to evaluate the average temperature of a city over time
{
  "id": "AvgTemperature_City",
  "name":"AvgTemperature",
  "description": "Average temperature in degrees Celsius for a city over time",
  "srcType": "City",
  "path": "measurementSeries",
  "expression": "avg(avg(normalized.data.temperature))"
}

This simple metric returns a time series of the average temperature of a City for a given time range and period.

Create a Compound Metric

Compound metrics further define ways to manipulate time series objects into new metrics. Unlike simple metrics, compound metrics are not associated with a source Type and can take simple or other compound metrics as inputs.

To create a compound metric, you must create a metric .json file in the metadata/CompoundMetric folder of your application: This file defines the parameters and evaluation expression for the metric.

The syntax to declare a compound metric is similar to that of a simple metric, without the need to define a srcType or path.

The following code block displays the syntax to create a new compound metric in your application:

JavaScript
// MyCompoundMetric.json
// An example compound metric
{
    // The unique id
    "id": "MyCompoundMetricName",
    // The unique name for this simple metric
    "name": "MyCompoundMetric",
    // A description for what this metric does
    "description": "Description of the metric",
    // The evaluation expression for the metric
    "expression": "ExpressionEngineFunction"
}

The main distinction between a compound and a simple metric is its inputs and expression functions.

Since compound metrics take other metrics as inputs, their expression engine functions are typically more complex. To learn more about expression engine functions in the C3 Agentic AI Platform, review Expression Engine Functions.

Create a new compound metric called TemperatureChange to evaluate the rate of temperature change for a City in a time period. The expression engine function should have the following characteristics:

  • Use the eval function alongside the rollingDiff function to return a time series of how the AvgTemperature metric changes over time, segmented by Day.
  • Use the abs function to calculate the absolute value of temperature changes.

The following code block displays the completed TemperatureChange.json file to define the new compound metric:

JavaScript
// Compound metric to evaluate the rate of temperature change for a city
{
    "name": "TemperatureChange",
    "id": "TemperatureChange",
    "description": "Absolute temperature change in degrees Celsius per day for a city",
    "expression": "eval('DAY',abs(rollingDiff(AvgTemperature)))"
}

This metric returns a time series of the absolute temperature change per day for a given time range and interval.

Methods

Methods are used to define custom behaviors on Types in your object model. Methods are declared like fields in Types and can be called from anywhere in the application.

Some examples of actions that methods can do include:

  • Calculate and return statistics about Types in the object model.
  • Run complex analytical operations and write the results in a Type.

Create methods by implementing the following components in the object model:

  1. Declare a method on an existing Type in the MyType.c3typ file:
    1. Use the function keyword.
    2. Specify the method type.
    3. Specify the input parameters.
    4. Specify the runtime execution options.
  2. Define the method's logic in a code file:
    1. The code file extension must match the runtime execution specified in the MyType.c3typ file. For example, a method executed in JavaScript should be defined in a .js file.

In this section, you create a member method in your object model. A member method runs on a specific instance of a Type.

To learn more about the different types of methods available in the C3 Agentic AI Platform, see Methods topic.

Declare a method

Member methods are declared using the member keyword. The following code block shows an example of the declaration of a member method with no input parameters and using the server runtime execution:

Type
/**
* MyType.c3typ
* An example Type
*/
entity type MyType {
  // An example of some fields with common data types
  myMethod: member function (): double js-server
}

Declare a new getMaxTemperature member method in the City Type. This method will have the following characteristics:

  • Does not require any input parameters.
  • Returns a double data type.
  • Is implemented using JavaScript and uses the server runtime.
Type
/**
 * City.c3typ
 * A single city within a country.
 */
entity type City mixes FeatureEvaluatable {
    
    /**
     * The name of this city.
     */
    name: !string
    
    /**
     * The country in which this city is located.
     */
    country: !Country
    
    /**
     * The collection of CountryCapitalHistory for this city.
     */
    @db(order='descending(start)')
    capitalHistory: [CountryCapitalHistory](to)
    
    /**
     * The country for which this city currently serves as capital (if any).
     */
    currentCountry: Country stored calc capitalHistory[0].(end == null).from
    
    /**
     * The collection of CityMeasurementHeader for this city.
     */
    @db(order='descending(start)')
    measurementSeries: [CityMeasurementHeader](city)
    
    /**
     * A method to calculate the maximum recorded temperature.
     */
    getMaxTemperature: member function (): double js-server
}

After completing this step, you have successfully declared a new method in the City Type. However, there is no logic associated with this method yet.

In the next section, you create a JavaScript file to define the logic for the getMaxTemperature method.

Implement a method

After declaring one or more methods in a Type, create a file to implement the logic for all declared methods.

The implementation file extension must match the implementation in the original method declaration. For example, to implement the getMaxTemperature method, you must create a City.js file since the method declaration specifies JavaScript.

Implement the getMaxTemperature method in a new City.js file. The method takes no inputs. It returns the maximum temperature ever recorded for an individual City.

The following code block displays the implementation of the getMaxTemperature method:

JavaScript
// City.js

function getMaxTemperature() {
    // Declare variables for later
    var maxTemp;
    
    // Retrieve all temperature measurements for this city
    var filter = "parent.city.id=='" + this.id + "'";
    var measurements = CityMeasurementDataPoint.fetch({filter: filter});
    
    // Find the maximum temperature value
    if (measurements.objs.length > 0) {
        maxTemp = measurements.objs[0].temperature;
        for (var i = 1; i < measurements.objs.length; i++) {
            if (measurements.objs[i].temperature > maxTemp) {
                maxTemp = measurements.objs[i].temperature;
            }
        }
        return maxTemp;
    }
    
    return null;
}

See also

Was this page helpful?