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:
In this tutorial, you create metrics and methods to further process the time series data in your object model for downstream use.
Use the C3 AI VS Code Extension to implement metrics and methods to your object model. To learn how to access the C3 AI VS Code Extension, see Access Developer Tools tutorial.
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:
- Create a metric
.jsonfile in themetadata/SimpleMetricfolder of your application: This file defines the parameters and evaluation expression for the metric. - 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:
// 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
srcTypefor the metric should beCity - The
pathshould be the dot notation path from thesrcTypeType to the header Type- Review the Model Data as Time Series tutorial to review the header Type used in this object model.
- The
expressionfield contains an expression engine expression that evaluates thetemperaturefield across time and space.- The expression should return the
avgvalue fortemperatureacross time andCityinstances.
- The expression should return the
The following code block displays the completed AvgTemperature_City.json file to define the new simple metric:
// 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:
// 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
evalfunction alongside therollingDifffunction to return a time series of how theAvgTemperaturemetric changes over time, segmented byDay. - Use the
absfunction to calculate the absolute value of temperature changes.
The following code block displays the completed TemperatureChange.json file to define the new compound metric:
// 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:
- Declare a method on an existing Type in the MyType.c3typ file:
- Use the
functionkeyword. - Specify the method type.
- Specify the input parameters.
- Specify the runtime execution options.
- Use the
- Define the method's logic in a code file:
- 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
.jsfile.
- 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
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:
/**
* 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
doubledata type. - Is implemented using JavaScript and uses the
serverruntime.
/**
* 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:
// 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;
}