C3 AI Documentation Home

Bindings and Variables in Metrics

Bindings is a key concept embedded within metrics. bindings are a field in the EvalMetricsSpec Type that provide values to variables in a metric expression at runtime.

Definition

Bindings are often used to parametrize metrics with values specified by the end user, and to test compound metrics with simple metrics at runtime. The API definition is shown below:

bindings (anyof(map<string, any>, [map<string, any>]))

Example of bindings of type map<string, any>

Begin by defining a simple metric on SmartBulb. Note there is an additional field that has two variables listed alongside their data types.

The expression refers to variable1 and variable2 defined in the array variables of the metric definition. At runtime, the values of variable1 and variable2 must be specified and passed in the field bindings of the EvalMetricsSpec.

In this example, the resulting Timeseries is equal to identity(7):

JavaScript
var m = SimpleMetric.make({
  id: "MyParametricMetric_SmartBulb",
  name: "MyParametricMetric",
  srcType: "SmartBulb",
  expression: "identity(2) * variable1 + variable2",
  variables: [{
    name: "variable1",
    dataType: "double"
  },{
    name: "variable2",
    dataType: "double"
  }]
});
JavaScript
var spec = EvalMetricsSpec.make({
  ids: ["SMBLB1"],
  expressions: ["MyParametricMetric"],
  start: "2018-01-01",
  end: "2018-02-01",
  interval: "DAY",
  bindings: {
    "variable1": 2,
    "variable2": 3
  }
});
SmartBulb.evalMetricsWithMetadata(spec, [m]);

Example of bindings of type [map<string, any>]

Using the same simple metric declaration, it is also possible to provide multiple values. Note there are two possible combination sets of variable1 and variable2. This means the result can be two Timeseries. One Timeseries is the same as identity(7), or a Timeseries that simply repeats the value 7, and the other Timeseries is the same as identity(14), or a Timeseries that repeats 14.

The advantage of using bindings is that it is only required to define one foundational metric to then be able to have a few permutations of that metric.

JavaScript
var spec = EvalMetricsSpec.make({
  ids: ["SMBLB1"],
  expressions: ["MyParametricMetric"],
  start: "2018-01-01",
  end: "2018-02-01",
  interval: "DAY",
  bindings: [{
    "variable1": 2,
    "variable2": 3
  },{
    "variable1": 4,
    "variable2": 6
  }]
});
SmartBulb.evalMetricsWithMetadata(spec, [m]);

Example of bindings cartesian product

One other way to combine bindings is with bindingsCartesianProduct. This generates a cartesian product of all the binding parameters passed. For example, by setting the variable to be true, there are now 4 timelines with the combinations (2,4), (2,6), (3,4), (3,6).

JavaScript
var spec = EvalMetricsSpec.make({
  ids: ["SMBLB1"],
  expressions: ["MyParametricMetric"],
  start: "2018-01-01",
  end: "2018-02-01",
  interval: "DAY",
  bindings: [{
    "variable1": 2,
    "variable2": 3
  },{
    "variable1": 4,
    "variable2": 6
  }],
  bindingsCartesianProduct: true
});
SmartBulb.evalMetricsWithMetadata(spec, [m]);

Bindings in compound metrics and variables

Bindings also play a crucial role in compound metrics, especially when dealing with variables. Consider the following compound metric example:

JSON
{
  "id": "CompoundMetricInner",
  "name": "CompoundMetricInner",
  "expression": "identity(2) * timeperiod",
  "variables": [{
    "name": "timeperiod",
    "dataType": "int"
  }]
}

You can use both implicit and explicit bindings for compound metrics:

Implicit bindings

Implicit bindings occur when a compound metric references another metric with variables without re-declaring those variables. For example:

JSON
{
  "id": "CompoundMetricOuterImplicit",
  "name": "CompoundMetricOuterImplicit",
  "expression": "CompoundMetricInner"
}

Explicit bindings

Explicit bindings require re-declaring the variables in the compound metric:

JSON
{
  "id": "CompoundMetricOuterExplicit",
  "name": "CompoundMetricOuterExplicit",
  "expression": "CompoundMetricInner",
  "variables": [{
    "name": "timeperiod",
    "dataType": "int"
  }]
}

Both implicit and explicit bindings evaluate to the same result. The choice between them depends on clarity and the specific use case.

JavaScript
var spec = EvalMetricsSpec.make({
  ids: ['TestId'],
  expressions: ['CompoundMetricOuterImplicit', 'CompoundMetricOuterExplicit'],
  start: '2018-01-01',
  end: '2018-01-11',
  interval: 'DAY',
  bindings: {
    "timeperiod": 10
  }
});
TestAsset.evalMetrics(spec);

For the explicit metric, you are re-declaring that there is a metric variable called timeperiod without any value to it. The value is being substituted with what is passed at runtime in the EvalMetricsSpec.

Best practices and warnings

  • Always ensure that variable names do not shadow existing field names to avoid conflicts.
  • Use explicit bindings for clarity when necessary, but implicit bindings can simplify the metric definitions.
  • Efficiently manage multiple bindings to avoid redundant computations.
Was this page helpful?