> This page is for Structured Data Models Client.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/sdgm/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/sdgm/_mcp/server.

# Understand Prediction Results

> Interpret the PredictionResult and DataFrame returned by a Kumo Tabular prediction

`client.predict()` and `FittedModel.predict()` always return a `PredictionResult` — never a DataFrame sometimes and an explanation object other times.

```python
result = client.predict(request, model=KumoTabular())

result.to_pandas()   # always a frame
result.metadata      # whatever the endpoint reported
result.explanations  # gradients and column cohorts when requested and supported
```

For upcoming explanation support and its deployment requirements, see [Explain Kumo Tabular Predictions](/sdgm/kumo-tabular/user-guide/explain-predictions/explain-tabular-predictions).
Explanations are separate from `to_pandas()`: use `.explanations.gradients.to_pandas()` or `.explanations.columns.to_pandas()` after checking that column analysis is available.

## How many rows come back

The shape depends on the **task**, not the model:

| Task           | Rows           |
| -------------- | -------------- |
| classification | One per entity |
| regression     | One per entity |

Kumo Tabular does not produce ranked or multi-step results, so every prediction is one row per scored row.

## Joining

Every row carries `row_index`. It also carries `id` — the entity's key — when the schema declared one; a single-table request with no primary key has none.

> **Warning**
>
> **Join on the key when it is there, otherwise on `row_index`. Never on row order.** The endpoint is not required to return rows in the order they were sent.

```python
answers = result.to_pandas()
joined = (
    to_score.reset_index(drop=True)
    .reset_index(names="row_index")
    .merge(answers, on="row_index")
)
```

`row_index` in the response is the zero-based **position** of each row within the frame you sent — not whatever index labels `to_score` happened to carry. Drop `to_score`'s own index first (`reset_index(drop=True)`) before turning the position into a `row_index` column; skipping that step silently mismatches predictions to rows whenever `to_score` was filtered, sorted, or sliced beforehand.

## Columns

| Field           | Column(s)                            |
| --------------- | ------------------------------------ |
| `prediction`    | `prediction`                         |
| `probabilities` | `probability_<class>`, one per class |
| `quantiles`     | `quantile_<level>`, one per level    |

A column appears only when at least one returned row carries that field, so a classification result has `probability_*` columns and a regression one has `quantile_*` columns without either inventing the other.

> **Note**
>
> A regression on Kumo Tabular always reports the **mean** of its predicted distribution under `prediction`, and does not take `prediction_statistic` — that option exists only for the relational model, which defaults to the median instead. See [Kumo Relational: What a regression reports](/sdgm/kumo-relational/user-guide/work-with-prediction-results/prediction-results#what-a-regression-reports).

## Which fields the answer actually carries

`outputs=[...]` on `predict()` asks for fields; it does not guarantee they come back. Check what you asked for against what arrived:

```python
result = client.predict(request, model=KumoTabular(), outputs=["prediction", "quantiles"])
missing = result.absent(["prediction", "quantiles"])
```

sdm-client also warns when a requested field is absent from the answer, naming it.

## Illustrative shapes

Classification:

```text
   row_index  prediction  probability_0  probability_1
0          0           0           0.79           0.21
1          1           1           0.12           0.88
```

Regression:

```text
   row_index  prediction
0          0      128.40
1          1      301.75
```

These examples show only the column structure. The values depend on the query and the deployed release.

## An empty result still has columns

Scoring an empty `predict` frame is legal — it is what a filter that matched nothing produces — and `to_pandas()` returns a frame with `row_index` and the requested output columns, not a frame with zero columns.