---
title: "Understand Job Output"
description: "What a job's result table looks like - the columns each object type produces (id, inputs, output, transforms, dependent, explainer, block/segment/rule) and how to read them."
---

<div style="padding: 14px 16px; border: 0.5px solid #AFA9EC; border-radius: 12px; background: #EEEDFE; margin-bottom: 1.5rem;">
  <div style="display: flex; align-items: center; gap: 7px; margin-bottom: 8px;">
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
      <path d="M9 2L4 9h4l-1 5 5-7H8l1-5z" stroke="#7f77dd" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
    </svg>
    <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.05em; text-transform: uppercase; color: #534AB7;">TL;DR</span>
  </div>
  <div style="font-size: 13px; color: #3C3489; line-height: 1.65;">
    When you run a job (a Simulation, Monitoring run, and so on), the platform produces a <strong>result table</strong>: one row per record, keyed by <code>id</code>, with each input under its own alias and the result in <code>output</code>. The columns follow a consistent pattern per object type - a Model adds <code>dependent</code>, transform, and <code>explainer</code> columns; a Policy adds a column for every block, segment, and rule. This page is the reference for that pattern.
  </div>
</div>

## What is it?

Every job runs a registered object (a Data Element, Feature, Model, or Policy) over a sample of records and returns a **result table**. Each row is one record (one instance of the object's entity, such as an application or an account), `id` is the key, the inputs sit under their own aliases, and the result lands in `output`.

You meet this table in a few places:

* The **Job Result** section of a completed job, as a sample preview.
* As the `data` PySpark DataFrame in a [Report Definition](/user-guide/how-tos/register-reports/) or a Figure.
* As the data you pull into the [Notebook](/user-guide/how-tos/use-the-notebook/).

Two conventions hold across every object type:

* **`id`** is always the key column. Each row is one record; the values come from that record's platform key (an application ID, a loan ID, and so on), but the column itself is always called `id`.
* **`output`** is always the object's own result: the Data Element's value, the Feature's value, the model score, or the policy decision.

What sits *between* `id` and `output` is what differs by object type. The rest of this page walks each one. The examples share a running theme, a FICO-based `credit_tier`, though each object shows its own columns.

## Data Element

A Data Element exposes one column from a registered table. Its output is a straight passthrough (or a light cast) of that source column.

Columns:

* `id` - the key.
* the **source column** - the raw table column the Data Element reads, named after that column (not after the Data Element's alias).
* `output` - the Data Element's value.

For a Data Element `de_fico` that reads `fico` from the applications table, the source column and `output` carry the same value:

<table class="attr-table lowercase-headers">
  <thead>
    <tr><th><code>id</code></th><th><code>fico</code></th><th><code>output</code></th></tr>
  </thead>
  <tbody>
    <tr><td>A1001</td><td>742</td><td>742</td></tr>
    <tr><td>A1002</td><td>605</td><td>605</td></tr>
  </tbody>
</table>

## Data Aggregate

A [Data Aggregate](/user-guide/how-tos/register-a-data-aggregate/) groups many rows by a join key and rolls them up to one row per key (for example, aggregating an applicant's accounts up to one row per application). The column set is the same shape - `id`, the inputs, `output` - but the **grain and values change**:

* `id` is the **target** key: the value you grouped by. Each row is one group, not one source row.
* Each input appears by its alias, but its values are **collapsed** to the target grain: inputs already at the target grain stay single values, while inputs at the finer grain become a list of the grouped values.
* `output` is the single aggregated result per row.

For an aggregate `max_account_balance` that groups accounts by application and takes the maximum `account_balance` per application. It also reads `applicant_state`, which is already at the application grain, to show the contrast:

<table class="attr-table lowercase-headers">
  <thead>
    <tr><th><code>id</code></th><th><code>account_balance</code></th><th><code>applicant_state</code></th><th><code>output</code></th></tr>
  </thead>
  <tbody>
    <tr><td>A1001</td><td>[1200, 340, 9800]</td><td>CA</td><td>9800</td></tr>
    <tr><td>A1002</td><td>[500, 500]</td><td>TX</td><td>500</td></tr>
  </tbody>
</table>

`account_balance` sits at the finer grain (one value per account), so it collapses to a **list** of the grouped values; `applicant_state` is already at the target grain (one value per application), so it stays a **single value**. The join key you grouped by does not appear as a separate column, and the individual source rows are not shown - they are collapsed into the lists you see.

## Feature

A [Feature](/user-guide/how-tos/register-a-feature/) is a derived value computed from one or more inputs (Data Elements or other Features).

Columns:

* `id` - the key.
* **one column per input** - each named by its alias.
* `output` - the Feature's value.

For the `credit_tier` Feature (one input, `de_fico`, bucketed into `A` / `B` / `C`):

<table class="attr-table lowercase-headers">
  <thead>
    <tr><th><code>id</code></th><th><code>de_fico</code></th><th><code>output</code></th></tr>
  </thead>
  <tbody>
    <tr><td>A1001</td><td>742</td><td>A</td></tr>
    <tr><td>A1002</td><td>605</td><td>C</td></tr>
  </tbody>
</table>

A multi-input Feature simply adds a column per input. A `credit_tier` that also read `dti` would show `id`, `de_fico`, `dti`, `output`.

## Feature Aggregate

A [Feature Aggregate](/user-guide/how-tos/register-a-feature-aggregate/) behaves like a Data Aggregate: same `id` + inputs + `output` shape, but rolled up to **one row per join key**, with inputs collapsed to that grain and one `output` per row. See [Data Aggregate](#data-aggregate) above for how the values collapse.

## Model

A Model adds a few columns to the Feature pattern: its transforms, the target it was trained against, and (optionally) its explainer.

Columns:

* `id` - the key.
* **one column per input** - each named by its alias. Inputs can be Data Elements, Features, or other Models.
* **one column per transform** - if the Model defines any [Transforms](/user-guide/how-tos/register-a-model-definition/), each named by the transform's alias.
* `dependent` - the target the Model predicts. This column is literally named `dependent`, and appears only when a dependent is configured.
* `output` - the model score. This column is always named `output`, regardless of the alias you gave the score.
* `explainer` - a single column, present only when a [Model Explainer](/user-guide/how-tos/register-a-model-explainer/) is enabled. It holds a **map of per-input contributions** (feature importance) or a **list of reason codes**, depending on the explainer, packed into that one column.

For a binary-classification Model that takes `credit_tier` and `dti` as inputs, with an explainer enabled:

<table class="attr-table lowercase-headers">
  <thead>
    <tr><th><code>id</code></th><th><code>credit_tier</code></th><th><code>dti</code></th><th><code>dependent</code></th><th><code>output</code></th><th><code>explainer</code></th></tr>
  </thead>
  <tbody>
    <tr><td>A1001</td><td>A</td><td>0.31</td><td>0</td><td>0.08</td><td><code>&#123;"credit_tier": -0.12, "dti": 0.03&#125;</code></td></tr>
    <tr><td>A1002</td><td>C</td><td>0.52</td><td>1</td><td>0.63</td><td><code>&#123;"credit_tier": 0.21, "dti": 0.09&#125;</code></td></tr>
  </tbody>
</table>

:::note[Model type does not change the shape]
Every model type - Binary Classification, Regression, Multi-class, Time Series - produces exactly **one** `output` column. A multi-class or array-output model returns its structured value (a list, say) into that single column; it is not spread into one column per class.
:::

## Policy

A Policy is a [Decision Workflow](/user-guide/how-tos/register-policy-decision-workflow/) built from blocks. Its output table carries a column for **every part of the workflow** - each block, each segment, each rule - so you can trace exactly how each record was decided.

To make the columns concrete, here is a small cross-sell policy that decides which members to make a credit-card offer to. It chains an **Eligibility Criteria** block, a **Credit Card Selection Strategy** block, a **Member NPV** derived variable, then an **Offer Creation** block and an **Offer Filtering** block. Each decision block carries a rule or two; neither uses segments, so their rules use the shorter two-part naming.

<div class="flow-diagram flow-diagram--td">
  <div class="flow-tier">
    <div class="flow-box box-policy">Block 1 - Eligibility Criteria<small>rule-1.1: no active card &middot; rule-1.2: tenure &gt;= 6 months</small></div>
  </div>
  <div class="flow-tier-arrow"><span class="arrow-label">met, else Not Eligible</span></div>
  <div class="flow-tier">
    <div class="flow-box box-policy">Block 2 - Credit Card Selection Strategy<small>rule-2.1: credit_score &gt;= 660 &middot; rule-2.2: income &gt;= 30000</small></div>
  </div>
  <div class="flow-tier-arrow"><span class="arrow-label">met, else Not Assigned</span></div>
  <div class="flow-tier">
    <div class="flow-box box-variable">Block 3 - Member NPV (Derived Variable)<small>member_npv = expected_revenue - expected_cost</small></div>
  </div>
  <div class="flow-tier-arrow">↓</div>
  <div class="flow-tier">
    <div class="flow-box box-global">Block 4 - Offer Creation<small>build candidate card offers</small></div>
  </div>
  <div class="flow-tier-arrow">↓</div>
  <div class="flow-tier">
    <div class="flow-box box-policy">Block 5 - Offer Filtering<small>rule-5.1: member_npv &gt; 0</small></div>
  </div>
  <div class="flow-tier-arrow"><span class="arrow-label">rolls up to</span></div>
  <div class="flow-tier">
    <div class="flow-box box-decision">output</div>
  </div>
</div>

Columns:

* `id` - the key.
* **one column per input** - each named by its alias (`tenure_months`, `active_card`, `credit_score`, `income`).
* **`<alias>`** - one column per **derived variable** (its computed value), **offer-config**, and **framework** output. Here that is `member_npv`.
* **`block-<n>`** - one per block, where `n` is the block's position (1-based) in the workflow. Holds the block's outcome. A Derived Variable block contributes **two** columns: its value under `<alias>`, and its flow decision under `block-<n>`. A Derived Variable block has no decision outcome, so its `block-<n>` reads `Continue` to show flow proceeded.
* **`segment-<s>.<n>`** - one per segment, where `s` is its block and `n` is the segment's position. Holds whether the record fell into that segment. This policy's blocks have no segments, so no `segment-` columns appear.
* **`rule-<s>.<r>`** - one per rule in a block with no segments, where `s` is the block and `r` is the rule's position. Holds whether the rule passed. For example, Rule 2.1 in the diagram becomes `rule-2.1`: block 2, rule 1. (A block that **does** use segments names its rules `rule-<s>.<n>.<r>`, adding the segment index.)
* `output` - the policy's overall decision (the outcome of the last block the record reached).

The values follow a consistent convention:

* **Rule and segment columns are booleans**: `True` (rule passed / in this segment), `False`, or `None` (the record never reached this point, because an earlier block decided it).
* **`block-<n>` and `output` are outcome strings**, title-cased. The built-in outcomes are `Continue` (flow moves to the next block), `Auto Approval`, `Auto Denial`, and `Manual Review`. A workflow can also define **custom end nodes** with their own names - this policy uses `Not Eligible`, `Not Assigned`, and `Offer Generated`. `output` is always a terminal outcome, never `Continue`.

For the workflow above, one member who clears every block and gets an offer, and one who fails eligibility at the first block:

<div style="overflow-x: auto;">

<table class="attr-table lowercase-headers" style="min-width: 860px; white-space: nowrap;">
  <thead>
    <tr><th><code>id</code></th><th><code>credit_score</code></th><th><code>member_npv</code></th><th><code>block-1</code></th><th><code>block-2</code></th><th><code>block-3</code></th><th><code>block-4</code></th><th><code>block-5</code></th><th><code>rule-1.1</code></th><th><code>rule-2.1</code></th><th><code>rule-5.1</code></th><th><code>output</code></th></tr>
  </thead>
  <tbody>
    <tr><td>M1001</td><td>712</td><td>184.50</td><td>Continue</td><td>Continue</td><td>Continue</td><td>Continue</td><td>Offer Generated</td><td>True</td><td>True</td><td>True</td><td>Offer Generated</td></tr>
    <tr><td>M1002</td><td>648</td><td>None</td><td>Not Eligible</td><td>None</td><td>None</td><td>None</td><td>None</td><td>False</td><td>None</td><td>None</td><td>Not Eligible</td></tr>
  </tbody>
</table>

</div>

Reading the rows: member `M1001` passes `rule-1.1` at Eligibility so `block-1` is `Continue`, and keeps clearing each block until Offer Filtering emits `Offer Generated`, which becomes `output`. Member `M1002` fails `rule-1.1`, so `block-1` is the custom end node `Not Eligible`; every later block is `None` because the record was already decided, and `output` carries that same `Not Eligible`. (The table is trimmed to one rule per block; a real policy shows a column for every rule.)

:::note[Segments add an index to rule names]
This policy's blocks have no segments, so rules are named `rule-<s>.<r>` (two parts). When a block uses segments, its segment columns appear as `segment-<s>.<n>` and its rules gain the segment index: `rule-<s>.<n>.<r>` (three parts).
:::

## Column order

Within the table, columns come in a fixed order: `id` first, then inputs, then the object-specific columns (transforms, blocks, segments, rules, derived variables, and so on), then `output` last. Within each of those groups, columns are sorted alphabetically by name. Run a job for your object and open its **Job Result** to see the exact columns and order for your case.

## What's Next

* [Run a Simulation](/user-guide/how-tos/run-a-simulation/) to produce a job output and open its **Job Result**.
* [Register Reports](/user-guide/how-tos/register-reports/) that read this data as `data` and summarize it.
* [Use the Notebook](/user-guide/how-tos/use-the-notebook/) to pull job output into your own analysis.
