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
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_session1. 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.
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)
standaloneOutput
group value rank
0 A 30 1
1 B 20 2
2 C 20 2Register the Method
python_method(...) gives the function a name and declares its inputs.
name,version: the Method's identity and versionDataInput: the data passed to the functionColumn: a required column. Usemeasurefor a metric anddimensionfor 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.
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
등록된 Method: ['my_team.rank']Select inputs and execute
Connect the actual data for a product revenue ranking.
| Setting | Selection in this example |
|---|---|
bindings | Revenue for value, product for group |
params | descending=True: highest revenue first |
scope | Date 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.
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.dataOutput
[{'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 analyzeoutcome: the purchase amount columntreatment: the ad exposure columnconfounders: 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.
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",
}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_olsOutput
{'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.
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
등록된 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.
# 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.dataOutput
{'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.
| Input | Selected data |
|---|---|
entity_id | Customer ID |
outcome | Purchase amount |
treatment | Ad exposures |
confounders | Age, 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.
{
"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_id | outcome | treatment | confounders_0 | confounders_1 |
|---|---|---|---|---|
| C001 | 8340 | 10 | 40 | 32200 |
| C002 | 10110 | 6 | 69 | 40600 |
Decision Layer calls the function with this table and its column names.
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.