--- title: "Register a Feature Aggregate" description: "Register a Feature Aggregate to roll up feature values across records." --- import { Tabs, TabItem } from '@astrojs/starlight/components';
TL;DR
Roll up many rows from a lower entity level into one value at this Feature's entity (like each Customer's max_account_balance across their Accounts). Tick Is Aggregated, write the logic in Python, Pandas, or Spark, and create. This page covers the aggregated case; for a transformation at the same entity level, see Feature.
## What is it? Every registered object has an **entity**, the level it is computed at, such as Application, Account, or Customer. A regular [Feature](/user-guide/how-tos/register-a-feature/) works with data already at its own entity, one row at a time. A Feature Aggregate works across levels: it takes many rows from a lower entity and rolls them up into a single value at the Feature's entity. For example, a Customer has many Accounts, and a Feature Aggregate can turn those Account rows into one number per Customer, like their highest balance. A Feature Aggregate is not a separate object type. It is simply a [Feature](/user-guide/how-tos/register-a-feature/) with **Is Aggregated** ticked, which switches the Formula section into aggregation mode. Reach for it whenever your output needs to summarize child rows, like totaling a Customer's balances across all their Accounts.
Feature Feature Aggregate (this page)
Computes from Inputs already at the Feature's entity level Multiple rows at a lower entity level, rolled up to this Feature's entity
Use when The output depends only on inputs already at the same entity You need to summarize across child rows
Definition language Python only Python, Pandas, or Spark
Example risk_tier from an Application's fico max_account_balance for each Customer
## How to Register Follow these steps to register a Feature Aggregate.
#### Open the Create Form On the **Feature Engineering → Feature** page, click **+ New Feature**. A name prompt opens first. Enter a descriptive name for the Feature (for example, `Max Account Balance`) and confirm to land on the full Feature form.
#### Fill in Attributes Tick **Is Aggregated** first. This is what turns the Formula section into aggregation mode. Set the **Type** \* - the **output type of your aggregation**, not the source column type (summing or taking a max → `Numerical`, returning a list → an `Array` type, a flag or label → `String` or `Boolean`). Set the **Entity** \* to the entity you are **rolling up to** (for example, `Customer`); inputs must come from this entity or a lower one. Fill in the **Alias**. See [Common Registration Info](/user-guide/how-tos/common-registration-info/).
#### Fill in Formula Under **Input**, pick the inputs the aggregation reads, from this Feature's entity or a lower one. With **Entity = Customer**, you can pick Account-level inputs (for example, `account`); each becomes a variable in the editor, referenced by its alias. Then write the aggregation in **Python**, **Pandas**, or **Spark**: - **Python and Pandas** receive **one entity's rows**. The platform groups the table by your Feature's entity and hands your code only the rows for the current entity (one Customer's Accounts). You compute and `return` a **single value** for that entity. No `groupBy` needed. - **Spark** receives the **whole, ungrouped table** across every entity. You do the `groupBy` yourself and `return` a **DataFrame keyed by the entity**, plus an **Output Dataframe Key**. :::note[Output Dataframe Key] **Spark only.** The column in your returned DataFrame that holds the entity key (for example, `customer_id`). The platform uses it to join each aggregated value back to the right Customer. ::: The example below computes each Customer's maximum balance across their open Accounts (**Entity = Customer**, input = `account`). Each input table is exposed as a **dictionary of lists**, sliced to the current entity (one Customer's Accounts). Return a single value. ```python # `account` is a data dictionary of one Customer's Accounts open_balances = [ bal for bal, status in zip( account['current_balance'], account['status'] ) if status == 'open' and bal is not None ] if not open_balances: return None return max(open_balances) ``` Each input table is exposed as a **pandas DataFrame**, sliced to the current entity (one Customer's Accounts). Return a single value. ```python # `account` is a pandas DataFrame of one Customer's Accounts open_accounts = account[account['status'] == 'open'] if open_accounts.empty: return None return open_accounts['current_balance'].max() ``` The input is the **full, ungrouped** Account table across every Customer. Do the `groupBy('customer_id')` yourself, return a 2-column DataFrame (the entity key + the value), and set the **Output Dataframe Key** to `customer_id`. ```python import pyspark.sql.functions as F # `account` is the full Account table across all Customers open_accounts = account.filter(F.col('status') == 'open') return open_accounts.groupBy('customer_id').agg( F.max('current_balance').alias('max_account_balance') ) ```
#### Fill in Properties and Create Fill in the [Properties](/user-guide/how-tos/common-registration-info/#properties) (Description, Permissible Purpose, Group, and Keywords), then click **Create** at the bottom right. The Feature is saved as a draft and you land on its details page.
## What's Next Once registered, the Feature Aggregate can be used downstream in [Features](/user-guide/how-tos/register-a-feature/), [Models](/user-guide/how-tos/register-a-model-definition/), and [Policies](/user-guide/how-tos/register-policy-decision-workflow/), just like any Feature. You can also [run a simulation](/user-guide/how-tos/run-a-simulation/) on it and trace its lineage to every downstream object. Next steps: - Build a [Model](/user-guide/how-tos/register-a-model-definition/) or [Policy](/user-guide/how-tos/register-policy-decision-workflow/) that uses this Feature Aggregate. - Send it for [approval](/user-guide/how-tos/approve-an-object/) so it can be used outside your draft workspace. - Computing at the same entity level instead? Register a regular [Feature](/user-guide/how-tos/register-a-feature/).