Skip to content

Connect a Python function ​

See how a Python function becomes a Method, receives selected data and returns a result.

Write a function → register a Method → select inputs → query data → inspect the result

A product revenue ranking shows the basic flow. OLS regression then shows how to connect several variables. Saved results appear below the code using prepared example data.

Example data setup
python
import sys
from pathlib import Path

import pandas as pd

# examples/ is outside the installed decision_layer package.
repo_root = next(
    path
    for path in (Path.cwd(), *Path.cwd().parents)
    if (path / "examples/methods").is_dir()
)
sys.path.insert(0, str(repo_root))

from decision_layer.methods import Column, DataInput, Scope, python_method
from importlib import reload
from examples.methods.authoring import customer_analysis

# Read updated example code when rerunning this setup cell.
practice_session = reload(customer_analysis).practice_session

1. Rank products by revenue ​

A small ranking function shows how the function and its data connect.

Calculation function ​

Rank the value column and return it alongside group. The function does not need to know about Cube or Decision Layer.

python
def rank_values(data, descending=True):
    """Rank observed values, retaining ties."""
    return data[["group", "value"]].assign(
        rank=data["value"].rank(method="min", ascending=not descending).astype(int)
    )


frame = pd.DataFrame({"group": ["A", "B", "C"], "value": [30, 20, 20]})
standalone = rank_values(frame)
standalone

Output

text
  group  value  rank
0     A     30     1
1     B     20     2
2     C     20     2

Register the Method ​

python_method(...) gives the function a name and declares its inputs.

  • name, version: the Method's identity and version
  • DataInput: the data passed to the function
  • Column: a required column. Use measure for a metric and dimension for a dimension.

Registration declares a value metric and a group dimension. The actual revenue metric and product dimension are selected at execution time.

practice_session() creates a local session with prepared example data. session.register(...) adds the Method to that session.

python
rank_method = python_method(
    rank_values,
    name="my_team.rank",
    version="1.0.0",
    inputs={
        "data": DataInput(
            columns={
                "value": Column(source="measure"),
                "group": Column(source="dimension"),
            },
        ),
    },
)

session = await practice_session()
session.register(rank_method)
print("Registered Methods:", [method.name for method in session.list_methods()])

Output

text
등록된 Method: ['my_team.rank']

Select inputs and execute ​

Connect the actual data for a product revenue ranking.

SettingSelection in this example
bindingsRevenue for value, product for group
paramsdescending=True: highest revenue first
scopeDate range and date dimension

Decision Layer queries the selected data, builds a DataFrame with value and group columns, and passes it to rank_values.

An AI request follows the same flow. AI selects a registered Method and semantic inputs, asks about ambiguous metrics or dates, and explains the result after Decision Layer queries and calculates it.

python
trial = await session.run(
    "my_team.rank",
    bindings={
        "value": "cube://shop/sales/revenue",
        "group": "cube://shop/sales/product",
    },
    params={"descending": True},
    scope=Scope(
        date_range=("2026-09-01", "2026-09-30"),
        time_dimension="cube://shop/sales/date",
    ),
)

trial.result.primary.data

Output

text
[{'group': 'A', 'value': 30.0, 'rank': 1},
 {'group': 'B', 'value': 20.0, 'rank': 2},
 {'group': 'C', 'value': 20.0, 'rank': 2}]

2. OLS with several variables ​

The ranking introduced how a function connects to data. Reusing the same method matters more when code handles several variables and analytical conditions.

  • A data scientist registers reviewed code once.
  • Users and AI connect the required data to reuse that analysis.
  • Decision Layer records server inputs and results in a Run.

Next, OLS examines the relationship between customer ad exposures and purchase amounts.

Calculation function ​

Examine ad exposures and purchases while also accounting for age and prior purchases.

  • data: the DataFrame to analyze
  • outcome: the purchase amount column
  • treatment: the ad exposure column
  • confounders: the age and prior purchase amount columns

Column names are arguments, so the function can be reused with different data. This example uses numeric synthetic data. The result describes association; it does not establish causation.

python
def ols(data, outcome, treatment, confounders):
    """Use OLS to examine a variable and outcome while accounting for other variables."""
    import numpy as np
    import pandas as pd
    import statsmodels.api as sm

    columns = [outcome, treatment, *confounders]
    if len(set(columns)) != len(columns):
        raise ValueError("The same column was selected more than once.")

    values = data[columns]
    if not all(pd.api.types.is_numeric_dtype(values[col]) for col in columns):
        raise ValueError("Select numeric columns only.")
    if not np.isfinite(values.to_numpy(dtype=float)).all():
        raise ValueError("Handle missing or infinite values first.")
    if values[outcome].nunique() < 2:
        raise ValueError("The outcome has no variation.")

    design = sm.add_constant(values[[treatment, *confounders]], has_constant="add")
    if len(data) <= design.shape[1]:
        raise ValueError("Add more observations or select fewer variables.")
    if np.linalg.matrix_rank(design) < design.shape[1]:
        raise ValueError(
            "Some predictors are exact linear combinations of others. Review the selected variables."
        )

    fit = sm.OLS(values[outcome], design).fit()
    return {
        "value": float(fit.params[treatment]),
        "n_observations": int(fit.nobs),
        "r_squared": float(fit.rsquared),
        "coefficients": {key: float(value) for key, value in fit.params.items()},
        "causal_claim": "not_established",
        "uncertainty": "not_reported",
    }
python
from examples.methods.authoring.customer_analysis import customer_sample

customers = customer_sample()

direct_ols = ols(
    customers,
    outcome="purchase_amount",
    treatment="ad_exposures",
    confounders=["age", "prior_purchase_amount"],
)

direct_ols

Output

text
{'value': 50.00000000000206,
 'n_observations': 200,
 'r_squared': 1.0,
 'coefficients': {'const': 999.999999999618,
  'ad_exposures': 50.00000000000206,
  'age': 9.999999999999813,
  'prior_purchase_amount': 0.20000000000000567},
 'causal_claim': 'not_established',
 'uncertainty': 'not_reported'}

Register the Method ​

entity_key identifies each customer row. multiple=True lets confounders accept several variables.

python
my_ols = python_method(
    ols,
    name="my_team.ols",
    version="1.0.0",
    inputs={
        "data": DataInput(
            columns={
                "entity_id": Column(source="dimension", role="entity_key"),
                "outcome": Column(source="measure", dtype="number"),
                "treatment": Column(
                    source=["dimension", "measure"],
                    dtype="number",
                ),
                "confounders": Column(
                    source=["dimension", "measure"],
                    dtype="number",
                    multiple=True,
                ),
            },
            minimum_rows=10,
        )
    },
    output="estimate",
    requires_packages={"statsmodels": ">=0.14,<0.16"},
)

session.register(my_ols)
print("Registered Methods:", [method.name for method in session.list_methods()])

Output

text
등록된 Method: ['my_team.rank', 'my_team.ols']

Select inputs and execute ​

Connect the customer ID, purchases and ad exposures, then select age and prior purchases as additional variables.

python
# Examine ad exposures and purchases, accounting for age and prior purchases.
ols_trial = await session.run(
    "my_team.ols",
    bindings={
        "entity_id": "cube://shop/customers/id",
        "outcome": "cube://shop/customers/purchase_amount",
        "treatment": "cube://shop/customers/ad_exposures",
        "confounders": [
            "cube://shop/customers/age",
            "cube://shop/customers/prior_purchase_amount",
        ],
    },
    scope=Scope(
        date_range=("2026-02-01", "2026-02-28"),
        time_dimension="cube://shop/customers/date",
    ),
)

ols_trial.result.primary.data

Output

text
{'value': 50.00000000000206,
 'n_observations': 200,
 'r_squared': 1.0,
 'coefficients': {'const': 999.999999999618,
  'treatment': 50.00000000000206,
  'confounders_0': 9.999999999999813,
  'confounders_1': 0.20000000000000567},
 'causal_claim': 'not_established',
 'uncertainty': 'not_reported'}

How selected inputs reach the function ​

1. Check the inputs.

Decision Layer checks that the selected metrics and dimensions exist in Cube and satisfy the Method's input requirements.

InputSelected data
entity_idCustomer ID
outcomePurchase amount
treatmentAd exposures
confoundersAge, prior purchase amount

2. Query customer-level data from Cube.

The selected metrics, dimensions and dates become a Cube query. Cube returns customer-level data using the aggregations and joins defined in its model.

View the Cube request

This JSON is sent to /cubejs-api/v1/load.

json
{
  "query": {
    "dimensions": ["customers.id", "customers.age"],
    "measures": [
      "customers.purchase_amount",
      "customers.ad_exposures",
      "customers.prior_purchase_amount"
    ],
    "timeDimensions": [
      {
        "dimension": "customers.date",
        "dateRange": ["2026-02-01", "2026-02-28"]
      }
    ],
    "order": [["customers.id", "asc"]],
    "limit": 50000,
    "offset": 0
  }
}

3. Rename the columns and pass them to ols.

Purchase amount becomes outcome, and ad exposures become treatment. Age and prior purchase amount become confounders_0 and confounders_1, in the order selected.

entity_idoutcometreatmentconfounders_0confounders_1
C0018340104032200
C0021011066940600

Decision Layer calls the function with this table and its column names.

python
ols(
    data=queried_dataframe,
    outcome="outcome",
    treatment="treatment",
    confounders=["confounders_0", "confounders_1"],
)

Use it in a project ​

The example registered two Methods in a local session. To use them in the admin console and through AI, save the functions and registrations in your project's method.py files.

Continue with Register in a project for file layout and server startup.