Skip to main content

Explainability

KumoRFM explanations provide insights into how predictions are made by offering two complementary perspectives. The global view (cohorts) reveals broad patterns across all in-context examples, showing which data characteristics drive predictions. The local view (subgraphs) highlights specific values in an individual entity’s data neighborhood that influenced its prediction. Together, these views answer: “What patterns does the model see overall?” and “Why did this specific entity get this prediction?” Use explanations to understand model reasoning, debug unexpected results, communicate insights to stakeholders, or validate that the model is learning meaningful patterns rather than spurious correlations.

Getting Started

Enable explanations by passing an ExplainConfig object to the predict method:
from kumoai.rfm import ExplainConfig

# Basic explanation
explanation = model.predict(
    query="PREDICT COUNT(returns.*, 0, 30, days) > 0 FOR orders.order_id=333",
    explain=ExplainConfig()
)

# With natural language summary
explanation = model.predict(
    query="PREDICT COUNT(returns.*, 0, 30, days) > 0 FOR orders.order_id=333",
    explain=ExplainConfig(with_summary=True)
)
The return type changes from a DataFrame to an Explanation object with three components:
# Access the prediction scores
prediction_df = explanation.prediction

# Access global column analysis
cohorts = explanation.details.cohorts

# Access local subgraph with attribution scores
subgraph = explanation.details.subgraphs[0]

# Access natural language summary (if generate_summary=True)
summary = explanation.summary

Global View: Column Analysis (Cohorts)

The cohort analysis reveals how different column values correlate with prediction outcomes across all in-context examples used by the model. This global perspective helps identify which features are generally predictive.

Structure

Each cohort object contains:
  • table_name: The table being analyzed (e.g., 'orders', 'users')
  • column_name: The column or aggregate (e.g., 'age', 'COUNT(*)')
  • hop: Distance from entity table (0 = entity itself, 1 = directly linked, 2 = second-degree neighbors)
  • stype: Semantic type numerical, categorical, timestamp, None for aggregates)
  • cohorts: List of value ranges (numerical/timestamp) or categories
  • populations: Proportion of in-context examples in each cohort (sums to 1.0)
  • targets: Average prediction score for examples in each cohort
High-impact columns show large variance in targets across different cohorts. Compare cohort values to identify risk factors and protective factors. Example: Predicting order returns
 # orders.date at hop 0 (the order being predicted) 
{     
	'table_name': 'orders',     
	'column_name': 'date',     
	'hop': 0,     
	'cohorts': ['[0 months, 2 months)', '[2 months, 24 months+)'],     
	'populations': [0.098, 0.902],     
	'targets': [0.020, 0.000],
}
What this means: Recent orders (0-2 months old) have a 2% return rate, while older orders have 0% return rate. Order age is highly predictive—the model learned that return policies don’t apply to old purchases.

Calculating Column importance

To quantify which columns matter most, compute weighted variance of targets:
import numpy as np

def calculate_importance(cohort):
    populations = np.array(cohort.populations)
    targets = np.array(cohort.targets)
    weighted_mean = np.sum(populations * targets)
    weighted_variance = np.sum(populations * (targets - weighted_mean) ** 2)
    return weighted_variance

# Calculate for all cohorts and sort
importance_scores = [
    {'column': f"{c.table_name}.{c.column_name}", 'score': calculate_importance(c)}
    for c in explanation.details.cohorts
]
importance_scores.sort(key=lambda x: x['score'], reverse=True)
Note on hops: The same column may appear at different hops. For example, orders.price at hop 0 is the price of the order being predicted, while orders.price at hop 2 represents prices of other orders connected through items or users. These are treated as distinct features in the global analysis.

Local View: Subgraph Attribution

The subgraph shows the actual data neighborhood around the specific entity being predicted, with attribution scores indicating which values influenced the prediction.

Structure

A subgraph contains:
  • seed_id: Always 0 (the entity being predicted)
  • seed_table: The entity’s table (e.g., 'orders')
  • seed_time: The anchor time for prediction
  • tables: Dictionary mapping table names to nodes
Each node represents a row from a table and contains:
  • cells: Dictionary of column names to cell objects
  • value: The actual data value (can be None if missing)
  • score: Attribution score from 0 to 1 (higher = more influential)
  • links: Dictionary mapping relationship names to sets of connected node IDs
Important: Node IDs are internal indices (0 to N), not primary keys. The entity being predicted always has ID 0.

Score Interpretation

Attribution scores indicate how much changing a value would change the prediction:
  • 0.00 - 0.05: Negligible influence
  • 0.05 - 0.15: Moderate influence
  • 0.15 - 0.30: Strong influence
  • 0.30+: Critical influence
Example: Order return prediction for order_id=333
# Node 0 in 'orders' table (the seed entity) 
{     
	'cells': {         
		'date': {'value': '2018-09-20', 'score': 1.0},
	    'price': {'value': 8.98, 'score': 0.291},
		'sales_channel_id': {'value': 2, 'score': 0.222}
		},     
	'links': {         
		'item_id->items': {5},         
		'user_id->users': {17}	     
	}
}
What this means: The order date (from 2018) is the strongest signal (score=1.0), indicating age is decisive. Price and sales channel show moderate influence. The order connects to 1 item and 1 user, forming a larger subgraph. Key insight: Null values can have non-zero scores, meaning missingness itself is informative. For example, a missing email address might correlate with higher churn risk.

Natural Language Summaries

For quick interpretation or stakeholder communication, generate natural language explanations:
explanation = model.predict(     
		query="PREDICT COUNT(returns.*, 0, 30, days) > 0 FOR orders.order_id=333",     
		explain=ExplainConfig(generate_summary=True)
)
print(explanation.summary)
Example output:
The model predicts this order is very unlikely to be returned (probability near 0%). The primary driver is the order’s age—it was placed over 18 months ago, well beyond typical return windows. Historical patterns show that orders older than 2 months have a 0% return rate, while recent orders (0-2 months) have a 2% return rate. The order’s low price ($8.98) and sales channel also contribute moderately to the low return probability.
Summaries are powered by an LLM that interprets both cohort and subgraph data, making them ideal for reports, dashboards, or explaining decisions to non-technical audiences. But in any case, all the raw data which is summarized recovered as shown above and parsed into a desired format as needed.
To learn more about explainability see this example notebook