Collections Strategy Challenger Lab

Build a Databricks challenger strategy and leadership brief for collections.

Introduction

30 Second Summary

When every overdue account looks equally urgent, a collections team can burn through limited agent time. The strongest next action stays hidden inside the queue.

In this project, you will build a credit collections decisioning lab in Databricks Free Edition. You will carry its challenger recommendation from portfolio diagnosis to IT handoff to a leadership brief in Google Slides.

What You'll Build

Picture running your notebook for a reviewer as account-level evidence becomes an IT-ready challenger recommendation.

By the end of this project, you'll have:

  • A runnable synthetic portfolio diagnosis that reveals score-band differences without exposing customer data.
  • A champion/challenger simulation that shows the recommendation's effect on agent demand. The same table shows its projected portfolio value.
  • A stakeholder-ready package with an IT rule handoff that makes priority explicit. A one-slide leadership recommendation turns the evidence into a controlled-test proposal.
  • Secret Mission: Add a support-needs guardrail that routes every flagged account to specialist review.

Are there any prerequisites?

You should be comfortable with SQL, Python, and credit-risk concepts. You also need access to Databricks and Google Slides through your existing accounts.

No installations are required.

Before We Start

This checkpoint ties the lab to a credit strategy team's real decision. It gives you a clear purpose before any notebook work begins.

Open Your Decisioning Workspace

Your strategy cannot be tested until the analysis workspace can execute a simple query. This first check separates workspace setup problems from the portfolio logic you build next.

Databricks Free Edition gives you a browser notebook backed by serverless compute. A successful readiness query proves the analysis surface can run Spark SQL.

Google Slides will hold the leadership brief you create later. You will prepare that blank presentation now so the final recommendation has a destination.

In this step, get ready to:
  • Create a Python Databricks notebook connected to serverless compute.
  • Prepare a blank Google Slides presentation for the leadership recommendation.
  • Run a readiness cell that confirms Spark SQL is working.
Create the Databricks notebook

The notebook becomes the working surface for every strategy calculation in this lab. Connecting it to compute now gives each later cell somewhere to run.

  • Open your Databricks workspace in the browser using the account that already has access.
  • Complete the sign-in flow shown for that account.

You should reach your Databricks workspace with the left sidebar visible.

  • Click + New in the left sidebar.
  • Select Notebook.

You should see the notebook editor with an empty cell ready for code.

  • Set the notebook name to Collections Strategy Challenger Lab in the title field.

The notebook title now identifies this specific decisioning lab.

  • Set the notebook language to Python if another language is selected.
  • Use the compute control at the top of the notebook to select serverless compute.

Your Python notebook is now open. The compute control should show that it is connected to serverless compute.

Why Databricks for this lab?

A desktop analytics tool would add local setup to this Mac-based project. Databricks Free Edition keeps the analysis in the browser.

Serverless compute handles the execution environment for the notebook. That keeps your attention on the collections strategy.

Notebook not connecting?

  • Refresh the browser page if the compute control remains disconnected.
  • Confirm that you signed in to the Databricks Free Edition account that owns the workspace.

Ask for help with connecting my Databricks notebook to serverless compute.

Prepare the leadership slide

The final recommendation needs a separate surface that a leadership audience can scan quickly. Starting with a blank presentation keeps the later briefing focused on evidence from the notebook.

You should see a new presentation with a blank slide canvas.

  • Click the presentation title field in the top-left corner.
  • Enter Collections Strategy Challenger Recommendation as the presentation title.

The presentation now shows the project title. The slide canvas remains blank.

  • Leave the Google Slides browser tab open for Step 5.

Presentation title not updating?

  • Click the presentation title field again.
  • Re-enter Collections Strategy Challenger Recommendation.

Ask for help with renaming my Google Slides presentation.

Prove the workspace is ready

A tiny readiness query checks the full execution path before you build the portfolio. It uses the notebook's Spark session to create a one-row result.

  • Switch back to the Collections Strategy Challenger Lab notebook.
  • Add the environment check by pasting this code into the first notebook cell:
# Environment check
display(spark.sql("SELECT 'ready' AS status"))

What does this code do?

  • The spark.sql call submits the SQL query to the active Spark session.
  • The returned value is a Spark DataFrame.
  • The query SELECT 'ready' AS status creates one value named status.
  • The display helper renders the returned DataFrame as a table in the notebook.

Before you run the cell, what result do you expect the table to contain?

  • Run the current cell by pressing Shift+Enter.

You should see a result table with a status column. Its single row should contain ready.

Readiness table missing?

  • Confirm that the notebook is connected to serverless compute.
  • Check that your cell matches the environment-check code exactly.
  • Run the cell again with Shift+Enter.

Ask for help with diagnosing my Databricks readiness cell.

✔️ Awesome, I've got everything!

Your notebook contains the environment-check cell. Your other browser tab contains a blank Google Slides presentation titled Collections Strategy Challenger Recommendation.

ⓧ I'd like to double check the full code

Compare your notebook cell with the complete code below. Your Google Slides presentation should be titled Collections Strategy Challenger Recommendation with no slide content added yet.

# Environment check
display(spark.sql("SELECT 'ready' AS status"))

You have proved that the notebook can execute Spark SQL. Next, you will build the synthetic portfolio that gives the strategy review something concrete to diagnose.

Build the Portfolio Baseline

Your Databricks notebook is ready for its first decisioning dataset. The readiness result proved that your browser workspace can execute notebook cells.

A strategy review needs a visible account-level baseline before optimization begins. This step creates a deterministic synthetic portfolio without exposing customer information.

In this step, get ready to:
  • Generate 120 deterministic synthetic accounts without personal information.
  • Display the first ten accounts in account ID order.
  • Inspect the champion treatment's days-past-due-only logic.
Build the synthetic account view

A temporary view gives later notebook cells a named table to query. Deterministic formulas produce the same 120 accounts every time you run the notebook.

The portfolio definition spans two code blocks here, but both belong in one notebook cell. The second block closes the SQL statement before the cell runs.

  • Start the portfolio cell beneath the environment check by pasting this first segment:
# Build a deterministic, synthetic portfolio and its intentionally simple champion strategy.
spark.sql("""
CREATE OR REPLACE TEMP VIEW collections_accounts AS
WITH generated AS (
    SELECT
        id AS account_id,
        500 + pmod(id * 137, 9500) AS balance,
        300 + pmod(id * 29, 551) AS risk_score,
        CASE pmod(id, 5)
            WHEN 0 THEN 10
            WHEN 1 THEN 25
            WHEN 2 THEN 40
            WHEN 3 THEN 55
            ELSE 75
        END AS days_past_due,
        pmod(id, 5) AS prior_contacts,
        CASE
            WHEN pmod(id, 7) = 0 OR pmod(id, 7) = 1 THEN 1
            ELSE 0
        END AS promise_kept,
        CASE
            WHEN pmod(id, 4) > 0 THEN 1
            ELSE 0
        END AS email_consent,

What does the first segment create?

  • The generated rows receive stable account_id values.
  • Repeatable formulas create the balance values.
  • Another formula creates the risk_score values.
  • The remaining expressions create delinquency data plus contact-history data.
  • Complete the same cell by pasting this continuation directly below END AS email_consent,:
        CASE
            WHEN pmod(id, 11) = 0 THEN 1
            ELSE 0
        END AS support_need_flag
    FROM range(1, 121)
)
SELECT
    *,
    CASE
        WHEN days_past_due <= 29 THEN 'DIGITAL_REMINDER'
        WHEN days_past_due <= 59 THEN 'AGENT_CALL'
        ELSE 'INTENSIVE_CALL'
    END AS champion_treatment,
    CASE
        WHEN risk_score >= 680
             AND prior_contacts <= 2
             AND pmod(account_id, 4) > 0 THEN 1
        WHEN promise_kept = 1
             AND pmod(account_id, 3) > 0 THEN 1
        ELSE 0
    END AS observed_cured
FROM generated
""")

What does the completed view contain?

  • The support_need_flag field identifies accounts that may need an additional support path.
  • The champion_treatment field assigns one action from the delinquency value.
  • The observed_cured field supplies a deterministic response signal for later analysis.
  • The temporary view stays available for the current notebook session.

Before you run the completed cell, do you expect the same formulas to create a stable portfolio on every run?

  • Run the completed portfolio cell with Shift+Enter.

The cell should complete without an error. The collections_accounts temporary view is now ready for a query.

Does the portfolio view cell fail?

  • Confirm that both code segments are inside the same Python cell.
  • Check that the continuation starts directly below END AS email_consent,.
  • Match the opening triple quotes with the closing triple quotes.

Ask for help comparing your cell with the target code: help me find the syntax or placement problem in my collections_accounts cell.

Display the ten-row portfolio preview

A DataFrame holds the result of a query in Python. Ordering by account ID keeps this preview stable across repeated runs.

  • Create the portfolio preview in the next Python cell by pasting this code:
portfolio_preview = spark.sql("""
SELECT *
FROM collections_accounts
ORDER BY account_id
LIMIT 10
""")
display(portfolio_preview)

What does the preview cell do?

  • The query reads every field from collections_accounts.
  • The ordering places the lowest account_id values first.
  • The row limit keeps the output focused on ten accounts.
  • The final line renders portfolio_preview as a notebook table.

Before you run the preview, do you think the account IDs will form one continuous sequence?

  • Run the preview cell with Shift+Enter.

You should see ten rows ordered from account 1 through account 10.

The table should contain the account details plus contact history. It should also show consent indicators plus both treatment outcome fields.

That is your first visible decisioning result. Your notebook now produces a safe account-level baseline with repeatable values.

Is the preview missing or incomplete?

  • Run the portfolio view cell before the preview cell.
  • Check that the preview queries collections_accounts with the same spelling used in the view definition.
  • Confirm that the preview cell ends with display(portfolio_preview).

Ask for help checking the cell order: help me fix my ten-row collections portfolio preview.

✔️ Awesome, I've got everything!

Your notebook contains the readiness check plus the synthetic portfolio view. It also displays the ordered ten-row preview.

ⓧ I'd like to double check the full code

Compare your notebook with the complete code for this step. Keep each section in the order shown below.

# Environment check
display(spark.sql("SELECT 'ready' AS status"))

# Build a deterministic, synthetic portfolio and its intentionally simple champion strategy.
spark.sql("""
CREATE OR REPLACE TEMP VIEW collections_accounts AS
WITH generated AS (
    SELECT
        id AS account_id,
        500 + pmod(id * 137, 9500) AS balance,
        300 + pmod(id * 29, 551) AS risk_score,
        CASE pmod(id, 5)
            WHEN 0 THEN 10
            WHEN 1 THEN 25
            WHEN 2 THEN 40
            WHEN 3 THEN 55
            ELSE 75
        END AS days_past_due,
        pmod(id, 5) AS prior_contacts,
        CASE
            WHEN pmod(id, 7) = 0 OR pmod(id, 7) = 1 THEN 1
            ELSE 0
        END AS promise_kept,
        CASE
            WHEN pmod(id, 4) > 0 THEN 1
            ELSE 0
        END AS email_consent,
        CASE
            WHEN pmod(id, 11) = 0 THEN 1
            ELSE 0
        END AS support_need_flag
    FROM range(1, 121)
)
SELECT
    *,
    CASE
        WHEN days_past_due <= 29 THEN 'DIGITAL_REMINDER'
        WHEN days_past_due <= 59 THEN 'AGENT_CALL'
        ELSE 'INTENSIVE_CALL'
    END AS champion_treatment,
    CASE
        WHEN risk_score >= 680
             AND prior_contacts <= 2
             AND pmod(account_id, 4) > 0 THEN 1
        WHEN promise_kept = 1
             AND pmod(account_id, 3) > 0 THEN 1
        ELSE 0
    END AS observed_cured
FROM generated
""")

portfolio_preview = spark.sql("""
SELECT *
FROM collections_accounts
ORDER BY account_id
LIMIT 10
""")
display(portfolio_preview)
Inspect the champion treatment

A champion strategy is the current rule set used as a comparison point. This baseline deliberately assigns treatments from days past due alone.

Before you compare the rows, do you think two accounts with the same delinquency value can receive different champion treatments?

  • Compare days_past_due with champion_treatment across all ten rows.
  • Check whether balance changes any champion treatment.
  • Check whether risk_score changes any champion treatment.
  • Check whether prior_contacts changes any champion treatment.
  • Check whether promise_kept changes any champion treatment.
  • Explain aloud why the champion treatment is intentionally simple.

Rows at 10 or 25 days receive DIGITAL_REMINDER. Rows at 40 or 55 days receive AGENT_CALL.

Rows at 75 days receive INTENSIVE_CALL. Accounts with the same delinquency value always receive the same champion treatment.

Balance never changes the assignment. Risk score never changes the assignment.

Contact history also leaves the assignment unchanged. This deliberate simplicity creates a clear baseline for measuring the champion's capacity problem.

Your portfolio baseline is live. Next, you'll measure where the champion consumes assisted-contact capacity.

Diagnose the Champion Workload

Your synthetic portfolio is running in Databricks. Now you need to test what its current champion strategy asks the collections team to do.

The champion groups accounts only by days past due. This sends broad groups into assisted collections despite different synthetic risk patterns.

You will use SQL summaries to expose that workload shortfall before changing the strategy.

In this step, get ready to:
  • Summarize account volumes and assisted contacts by champion treatment.
  • Split each treatment into score bands to reveal hidden performance differences.
  • Record the capacity diagnosis in a Markdown cell.
Summarize assisted contacts by treatment

A treatment-level summary shows how much of the portfolio enters each champion path. It also makes the demand for assisted contact visible.

  • Return to the Collections Strategy Challenger Lab notebook tab from earlier.
  • Add a Python cell below the displayed portfolio_preview output by using the notebook's add-cell control.
  • Paste the following query into the new cell:
# Monitor the champion strategy.
champion_summary = spark.sql("""
SELECT
    champion_treatment,
    count(*) AS accounts,
    round(avg(observed_cured) * 100, 1) AS observed_cure_rate_pct,
    round(sum(balance), 2) AS balance_managed,
    sum(
        CASE
            WHEN champion_treatment = 'AGENT_CALL' THEN 1
            WHEN champion_treatment = 'INTENSIVE_CALL' THEN 1
            ELSE 0
        END
    ) AS assisted_contacts
FROM collections_accounts
GROUP BY champion_treatment
ORDER BY champion_treatment
""")
display(champion_summary)

What Does This Query Measure?

  • The treatment grouping reveals how the champion distributes all 120 accounts.
  • The observed cure rate summarizes the synthetic response recorded inside each treatment.
  • The managed balance measures the account value assigned to each path.
  • The assisted-contact calculation counts accounts assigned to AGENT_CALL or INTENSIVE_CALL.

Before you run the cell, which champion treatments do you expect to consume assisted-contact capacity?

  • Run the cell by pressing Shift+Enter.

You will see three treatment rows. AGENT_CALL has 48 assisted contacts.

The INTENSIVE_CALL row adds 24 assisted contacts. You have exposed the first pressure point across 72 accounts.

Champion Summary Missing or Empty?

If the query cannot find collections_accounts, rerun the earlier portfolio creation cell before rerunning this cell.

If assisted_contacts is missing, compare the treatment names in your CASE expression with the code above.

Help me troubleshoot the champion workload summary.

Split champion treatments into score bands

A score band groups accounts into interpretable risk ranges. Splitting each champion treatment by these ranges reveals variation hidden by the first summary.

  • Add another Python cell below the champion_summary output by using the notebook's add-cell control.
  • Paste the following score-band query into the new cell:
score_band_diagnosis = spark.sql("""
SELECT
    champion_treatment,
    CASE
        WHEN risk_score >= 700 THEN 'LOWER_RISK'
        WHEN risk_score >= 620 THEN 'MEDIUM_RISK'
        ELSE 'HIGHER_RISK'
    END AS score_band,
    count(*) AS accounts,
    round(avg(observed_cured) * 100, 1) AS observed_cure_rate_pct,
    round(sum(balance), 2) AS balance_managed
FROM collections_accounts
GROUP BY
    champion_treatment,
    CASE
        WHEN risk_score >= 700 THEN 'LOWER_RISK'
        WHEN risk_score >= 620 THEN 'MEDIUM_RISK'
        ELSE 'HIGHER_RISK'
    END
ORDER BY champion_treatment, score_band
""")
display(score_band_diagnosis)

How Do the Score Bands Work?

  • Scores of 700 or higher enter the LOWER_RISK band.
  • Scores from 620 to 699 enter the MEDIUM_RISK band.
  • Scores below 620 enter the HIGHER_RISK band.
  • Grouping by treatment plus score band exposes response differences inside the champion's broad assignments.

Before you run the cell, do you expect accounts inside the same champion treatment to show identical cure rates?

  • Run the score-band cell by pressing Shift+Enter.

You will see the champion treatments split across LOWER_RISK, MEDIUM_RISK, and HIGHER_RISK rows.

The observed cure rates differ inside the same treatment. That split gives you evidence that days past due alone hides meaningful variation.

Score Bands Missing or Duplicated?

Check that the same score-band CASE expression appears in both the SELECT section and the GROUP BY section.

Check that the query reads from collections_accounts before rerunning the cell.

Help me fix the score-band diagnosis.

✔️ Awesome, I've got everything!

Your two diagnostic cells now display the champion workload summary and its score-band breakdown.

ⓧ I'd like to double check the full code

Compare your cumulative notebook code with this complete version. Keep each displayed result directly below the cell that creates it.

# Environment check
display(spark.sql("SELECT 'ready' AS status"))

# Build a deterministic, synthetic portfolio and its intentionally simple champion strategy.
spark.sql("""
CREATE OR REPLACE TEMP VIEW collections_accounts AS
WITH generated AS (
    SELECT
        id AS account_id,
        500 + pmod(id * 137, 9500) AS balance,
        300 + pmod(id * 29, 551) AS risk_score,
        CASE pmod(id, 5)
            WHEN 0 THEN 10
            WHEN 1 THEN 25
            WHEN 2 THEN 40
            WHEN 3 THEN 55
            ELSE 75
        END AS days_past_due,
        pmod(id, 5) AS prior_contacts,
        CASE
            WHEN pmod(id, 7) = 0 OR pmod(id, 7) = 1 THEN 1
            ELSE 0
        END AS promise_kept,
        CASE
            WHEN pmod(id, 4) > 0 THEN 1
            ELSE 0
        END AS email_consent,
        CASE
            WHEN pmod(id, 11) = 0 THEN 1
            ELSE 0
        END AS support_need_flag
    FROM range(1, 121)
)
SELECT
    *,
    CASE
        WHEN days_past_due <= 29 THEN 'DIGITAL_REMINDER'
        WHEN days_past_due <= 59 THEN 'AGENT_CALL'
        ELSE 'INTENSIVE_CALL'
    END AS champion_treatment,
    CASE
        WHEN risk_score >= 680
             AND prior_contacts <= 2
             AND pmod(account_id, 4) > 0 THEN 1
        WHEN promise_kept = 1
             AND pmod(account_id, 3) > 0 THEN 1
        ELSE 0
    END AS observed_cured
FROM generated
""")

portfolio_preview = spark.sql("""
SELECT *
FROM collections_accounts
ORDER BY account_id
LIMIT 10
""")
display(portfolio_preview)

# Monitor the champion strategy.
champion_summary = spark.sql("""
SELECT
    champion_treatment,
    count(*) AS accounts,
    round(avg(observed_cured) * 100, 1) AS observed_cure_rate_pct,
    round(sum(balance), 2) AS balance_managed,
    sum(
        CASE
            WHEN champion_treatment = 'AGENT_CALL' THEN 1
            WHEN champion_treatment = 'INTENSIVE_CALL' THEN 1
            ELSE 0
        END
    ) AS assisted_contacts
FROM collections_accounts
GROUP BY champion_treatment
ORDER BY champion_treatment
""")
display(champion_summary)

score_band_diagnosis = spark.sql("""
SELECT
    champion_treatment,
    CASE
        WHEN risk_score >= 700 THEN 'LOWER_RISK'
        WHEN risk_score >= 620 THEN 'MEDIUM_RISK'
        ELSE 'HIGHER_RISK'
    END AS score_band,
    count(*) AS accounts,
    round(avg(observed_cured) * 100, 1) AS observed_cure_rate_pct,
    round(sum(balance), 2) AS balance_managed
FROM collections_accounts
GROUP BY
    champion_treatment,
    CASE
        WHEN risk_score >= 700 THEN 'LOWER_RISK'
        WHEN risk_score >= 620 THEN 'MEDIUM_RISK'
        ELSE 'HIGHER_RISK'
    END
ORDER BY champion_treatment, score_band
""")
display(score_band_diagnosis)
Record the diagnosis in Markdown

A Markdown note keeps your analytical conclusion beside the evidence. This turns two result tables into a diagnosis another reviewer can follow.

  • Add a cell below the score-band output by using the notebook's add-cell control.
  • Set the new cell type to Markdown.
  • Enter this diagnosis in the cell: The champion applies the same action to accounts with materially different synthetic risk and response patterns. This creates avoidable assisted-contact capacity pressure.
  • Render the diagnosis by pressing Shift+Enter.

Before the final check, which output should reveal the variation hidden inside the broad champion treatments?

  • Rerun the champion_summary cell by pressing Shift+Enter.
  • Rerun the score_band_diagnosis cell by pressing Shift+Enter.

The first output reports assisted contacts by champion treatment. The second splits those same treatments into score bands with different cure rates.

Your rendered diagnosis now connects those results to avoidable capacity pressure. The champion's workload problem is visible and documented.

You have proved that the champion treats different account profiles too broadly. Next, you will turn that diagnosis into ordered challenger rules that reserve assisted capacity for selected accounts.

Build the Challenger Strategy

Your champion diagnosis in Databricks Free Edition showed that broad delinquency bands send accounts with different risk patterns into the same treatment. That creates avoidable assisted-contact pressure.

An ordered challenger strategy makes every priority reviewable. Python rules reserve agent capacity for selected accounts.

Other accounts move to digital reminders. Promise follow-up and self-service cover the remaining paths.

In this step, get ready to:
  • Define four ordered rules plus a fallback treatment.
  • Apply a generated SQL CASE expression to every account.
  • Trace a champion treatment into its challenger outcomes through the reclassification table.
Define the ordered challenger rules

The list records rule priority. The first matching condition controls the treatment.

  • Add a new Python cell immediately below the cell that ends with display(score_band_diagnosis).
  • Create the ordered rule sequence by pasting this code into the new cell:
# Define the challenger as ordered, explainable Python rules.
strategy_rules = [
    ("promise_kept = 1", "PROMISE_FOLLOW_UP"),
    (
        "risk_score >= 700 AND days_past_due <= 29 AND email_consent = 1",
        "DIGITAL_REMINDER",
    ),
    (
        "balance >= 5000 AND (risk_score < 620 OR days_past_due >= 60)",
        "AGENT_CALL",
    ),
    ("email_consent = 1", "EMAIL_NUDGE"),
]

How does rule priority work?

  • The PROMISE_FOLLOW_UP rule has first priority when promise_kept = 1.
  • The DIGITAL_REMINDER rule selects lower-risk accounts with early delinquency plus email consent.
  • The AGENT_CALL rule reserves assisted contact for selected high-balance accounts.
  • The EMAIL_NUDGE rule catches remaining accounts with email consent.
  • Accounts that match no listed condition later receive the SELF_SERVICE_PLAN fallback.
  • Run the rule cell by pressing Shift+Enter.

The cell finishes without an error. Your notebook now holds the ordered strategy_rules list.

Rule cell not completing?

Check that every condition and treatment remains inside matching quotation marks. Confirm that each tuple ends with a comma.

Compare the indentation around the multi-line tuples with the code above.

Help me check the syntax in my ordered challenger rules.

Generate the challenger assignments

The analyst-friendly rules now need an executable decision expression. The generated CASE keeps the Python rule order intact.

  • Add a new Python cell immediately below the strategy_rules cell.
  • Generate the CASE expression by pasting this code into the new cell:
case_lines = [
    f"WHEN {condition} THEN '{treatment}'"
    for condition, treatment in strategy_rules
]
challenger_case = (
    "CASE\n    "
    + "\n    ".join(case_lines)
    + "\n    ELSE 'SELF_SERVICE_PLAN'\nEND"
)

What does this code generate?

  • The case_lines list turns each condition and treatment into one SQL WHEN clause.
  • The challenger_case value joins those clauses in their original priority order.
  • The final ELSE clause assigns SELF_SERVICE_PLAN when no earlier rule matches.
  • Run the CASE-generation cell by pressing Shift+Enter.

The cell finishes without an error. The generated SQL is now stored in challenger_case.

CASE generation not completing?

Check the indentation inside the list comprehension. Confirm that the single quotation marks around the generated treatment remain inside the f-string.

Help me debug the generated challenger CASE expression.

The generated expression becomes useful when it is applied to the portfolio. A temporary view keeps the challenger result available to later notebook cells.

  • Add a new Python cell immediately below the CASE-generation cell.
  • Create the challenger assignment view by pasting this code into the new cell:
spark.sql(f"""
CREATE OR REPLACE TEMP VIEW challenger_assignments AS
SELECT
    *,
    {challenger_case} AS challenger_treatment
FROM collections_accounts
""")

What does this code create?

  • The f-string inserts challenger_case into the SQL statement.
  • The query preserves every column from collections_accounts.
  • The generated decision becomes the new challenger_treatment column.
  • The result is stored as the challenger_assignments temporary view.
  • Run the view-creation cell by pressing Shift+Enter.

The cell finishes without an error. Every synthetic account now has a challenger treatment available in challenger_assignments.

Challenger view not created?

Confirm that the cells defining strategy_rules and challenger_case completed first.

Check that the braces around challenger_case remain inside the SQL f-string.

Help me create the challenger assignment view.

Inspect the reclassification table

A reclassification table groups accounts by their old treatment and their new treatment. This reveals how each broad champion segment splits under the ordered rules.

  • Add a new Python cell immediately below the view-creation cell.
  • Build and display the reclassification table by pasting this code into the new cell:
reclassification = spark.sql("""
SELECT
    champion_treatment,
    challenger_treatment,
    count(*) AS accounts
FROM challenger_assignments
GROUP BY champion_treatment, challenger_treatment
ORDER BY champion_treatment, challenger_treatment
""")
display(reclassification)

What does this query show?

  • The two treatment columns identify each champion-to-challenger path.
  • The accounts column counts how many synthetic accounts follow that path.
  • The grouping produces one row for every treatment pair present in the assignments.
  • The ordering keeps related champion treatments together for review.

Before you run this cell, which champion treatment do you think splits across the most challenger treatments?

  • Run the reclassification cell by pressing Shift+Enter.

You will see champion_treatment, challenger_treatment, and accounts in the result table. Every displayed account count is greater than zero.

That is the strategy shift made visible. Each broad champion treatment now redistributes accounts across more selective challenger actions.

  • Find a displayed row where challenger_treatment is PROMISE_FOLLOW_UP.
  • Switch back to the strategy_rules cell from earlier.
  • Locate the first tuple containing promise_kept = 1.
  • Trace that condition to its assigned PROMISE_FOLLOW_UP action.

This path is evaluated first because it appears first in strategy_rules. An account that matches it receives PROMISE_FOLLOW_UP before lower-priority rules are considered.

Reclassification table missing?

Confirm that the challenger_assignments cell completed during the current notebook session. Run the preceding challenger cells again if the temporary view is unavailable.

Check that both treatment names in the query match the columns created earlier.

Help me restore the reclassification table.

✔️ Awesome, I've got everything!

Your notebook now contains the ordered challenger rules. It also displays every nonzero champion-to-challenger treatment path.

ⓧ I'd like to double check the full code

Compare your notebook with the complete collections_strategy_lab.py code through this step.

# Environment check
display(spark.sql("SELECT 'ready' AS status"))

# Build a deterministic, synthetic portfolio and its intentionally simple champion strategy.
spark.sql("""
CREATE OR REPLACE TEMP VIEW collections_accounts AS
WITH generated AS (
    SELECT
        id AS account_id,
        500 + pmod(id * 137, 9500) AS balance,
        300 + pmod(id * 29, 551) AS risk_score,
        CASE pmod(id, 5)
            WHEN 0 THEN 10
            WHEN 1 THEN 25
            WHEN 2 THEN 40
            WHEN 3 THEN 55
            ELSE 75
        END AS days_past_due,
        pmod(id, 5) AS prior_contacts,
        CASE
            WHEN pmod(id, 7) = 0 OR pmod(id, 7) = 1 THEN 1
            ELSE 0
        END AS promise_kept,
        CASE
            WHEN pmod(id, 4) > 0 THEN 1
            ELSE 0
        END AS email_consent,
        CASE
            WHEN pmod(id, 11) = 0 THEN 1
            ELSE 0
        END AS support_need_flag
    FROM range(1, 121)
)
SELECT
    *,
    CASE
        WHEN days_past_due <= 29 THEN 'DIGITAL_REMINDER'
        WHEN days_past_due <= 59 THEN 'AGENT_CALL'
        ELSE 'INTENSIVE_CALL'
    END AS champion_treatment,
    CASE
        WHEN risk_score >= 680
             AND prior_contacts <= 2
             AND pmod(account_id, 4) > 0 THEN 1
        WHEN promise_kept = 1
             AND pmod(account_id, 3) > 0 THEN 1
        ELSE 0
    END AS observed_cured
FROM generated
""")

portfolio_preview = spark.sql("""
SELECT *
FROM collections_accounts
ORDER BY account_id
LIMIT 10
""")
display(portfolio_preview)

# Monitor the champion strategy.
champion_summary = spark.sql("""
SELECT
    champion_treatment,
    count(*) AS accounts,
    round(avg(observed_cured) * 100, 1) AS observed_cure_rate_pct,
    round(sum(balance), 2) AS balance_managed,
    sum(
        CASE
            WHEN champion_treatment = 'AGENT_CALL' THEN 1
            WHEN champion_treatment = 'INTENSIVE_CALL' THEN 1
            ELSE 0
        END
    ) AS assisted_contacts
FROM collections_accounts
GROUP BY champion_treatment
ORDER BY champion_treatment
""")
display(champion_summary)

score_band_diagnosis = spark.sql("""
SELECT
    champion_treatment,
    CASE
        WHEN risk_score >= 700 THEN 'LOWER_RISK'
        WHEN risk_score >= 620 THEN 'MEDIUM_RISK'
        ELSE 'HIGHER_RISK'
    END AS score_band,
    count(*) AS accounts,
    round(avg(observed_cured) * 100, 1) AS observed_cure_rate_pct,
    round(sum(balance), 2) AS balance_managed
FROM collections_accounts
GROUP BY
    champion_treatment,
    CASE
        WHEN risk_score >= 700 THEN 'LOWER_RISK'
        WHEN risk_score >= 620 THEN 'MEDIUM_RISK'
        ELSE 'HIGHER_RISK'
    END
ORDER BY champion_treatment, score_band
""")
display(score_band_diagnosis)

# Define the challenger as ordered, explainable Python rules.
strategy_rules = [
    ("promise_kept = 1", "PROMISE_FOLLOW_UP"),
    (
        "risk_score >= 700 AND days_past_due <= 29 AND email_consent = 1",
        "DIGITAL_REMINDER",
    ),
    (
        "balance >= 5000 AND (risk_score < 620 OR days_past_due >= 60)",
        "AGENT_CALL",
    ),
    ("email_consent = 1", "EMAIL_NUDGE"),
]

case_lines = [
    f"WHEN {condition} THEN '{treatment}'"
    for condition, treatment in strategy_rules
]
challenger_case = (
    "CASE\n    "
    + "\n    ".join(case_lines)
    + "\n    ELSE 'SELF_SERVICE_PLAN'\nEND"
)

spark.sql(f"""
CREATE OR REPLACE TEMP VIEW challenger_assignments AS
SELECT
    *,
    {challenger_case} AS challenger_treatment
FROM collections_accounts
""")

reclassification = spark.sql("""
SELECT
    champion_treatment,
    challenger_treatment,
    count(*) AS accounts
FROM challenger_assignments
GROUP BY champion_treatment, challenger_treatment
ORDER BY champion_treatment, challenger_treatment
""")
display(reclassification)

Your challenger now turns a workload diagnosis into explicit account-level decisions. Next, you will simulate its projected impact and package the strategy for technical and leadership review.

Simulate Impact and Brief Leadership

Your Databricks Free Edition notebook now assigns every synthetic account to an explainable challenger treatment. This final step turns those assignments into a decision-ready case.

Technical partners need an implementation-ready rule table. Leadership needs projected impact with its assumptions made explicit.

You will package both views. You will also propose a controlled sample test with success criteria and capacity monitoring.

In this step, get ready to:
  • Compare champion and challenger outcomes through a what-if simulation.
  • Package the five ordered challenger actions for an IT implementation conversation.
  • Create a one-slide leadership recommendation supported by three notebook KPI comparisons.
Simulate the strategy impact

A what-if simulation applies explicit assumptions to both strategies. The shared portfolio creates a consistent comparison across value and operational capacity.

This simulation uses SQL to create one scenario row per account for each strategy. The final summary keeps the projected results separate from the observed cure field in your synthetic portfolio.

  • Use the notebook's add-cell control to create a Python cell directly below display(reclassification).
  • Paste the scenario simulation into the new cell using this code:
# Compare projected outcomes under explicit teaching assumptions.
spark.sql("""
CREATE OR REPLACE TEMP VIEW strategy_comparison AS
WITH scored AS (
    SELECT
        *,
        least(0.70, 0.10 + (risk_score - 300) / 1000.0) AS base_cure_probability,
        CASE
            WHEN champion_treatment = 'DIGITAL_REMINDER' THEN 0.04
            WHEN champion_treatment = 'AGENT_CALL' THEN 0.08
            ELSE 0.10
        END AS champion_treatment_uplift,
        CASE
            WHEN challenger_treatment = 'PROMISE_FOLLOW_UP' THEN 0.12
            WHEN challenger_treatment = 'DIGITAL_REMINDER' THEN 0.06
            WHEN challenger_treatment = 'AGENT_CALL' THEN 0.14
            WHEN challenger_treatment = 'EMAIL_NUDGE' THEN 0.07
            ELSE 0.08
        END AS challenger_treatment_uplift
    FROM challenger_assignments
),
scenario_rows AS (
    SELECT
        account_id,
        'Champion' AS strategy,
        least(
            0.85,
            base_cure_probability + champion_treatment_uplift
        ) AS projected_cure_probability,
        balance * 0.30 * least(
            0.85,
            base_cure_probability + champion_treatment_uplift
        ) AS projected_recovery,
        CASE
            WHEN champion_treatment = 'AGENT_CALL' THEN 1
            WHEN champion_treatment = 'INTENSIVE_CALL' THEN 1
            ELSE 0
        END AS assisted_contact,
        CASE
            WHEN champion_treatment = 'DIGITAL_REMINDER' THEN 0.20
            WHEN champion_treatment = 'AGENT_CALL' THEN 1.00
            ELSE 1.25
        END AS workload_units
    FROM scored

    UNION ALL

    SELECT
        account_id,
        'Challenger' AS strategy,
        least(
            0.85,
            base_cure_probability + challenger_treatment_uplift
        ) AS projected_cure_probability,
        balance * 0.30 * least(
            0.85,
            base_cure_probability + challenger_treatment_uplift
        ) AS projected_recovery,
        CASE
            WHEN challenger_treatment = 'AGENT_CALL' THEN 1
            ELSE 0
        END AS assisted_contact,
        CASE
            WHEN challenger_treatment = 'AGENT_CALL' THEN 1.00
            WHEN challenger_treatment = 'PROMISE_FOLLOW_UP' THEN 0.25
            WHEN challenger_treatment = 'DIGITAL_REMINDER' THEN 0.15
            WHEN challenger_treatment = 'EMAIL_NUDGE' THEN 0.15
            ELSE 0.10
        END AS workload_units
    FROM scored
)
SELECT
    strategy,
    count(*) AS accounts,
    sum(assisted_contact) AS assisted_contacts,
    round(sum(workload_units), 1) AS workload_units,
    round(avg(projected_cure_probability) * 100, 1) AS projected_cure_rate_pct,
    round(sum(projected_recovery), 2) AS projected_recovery
FROM scenario_rows
GROUP BY strategy
ORDER BY strategy
""")

comparison_df = spark.sql("SELECT * FROM strategy_comparison ORDER BY strategy")
display(comparison_df)

What does this simulation do?

  • The scored section calculates a shared base cure probability. It adds separate treatment uplifts for the champion and challenger.
  • The scenario_rows section creates one Champion row and one Challenger row for every account.
  • The final query summarizes assisted contacts and workload units. It also summarizes projected cure rate and projected recovery.
  • The comparison_df result renders the comparison as a two-row table.

Treat These Values as Assumptions

The cure uplifts are teaching assumptions. The 0.30 payment fraction is also a teaching assumption.

The workload-unit weights approximate relative operational effort for this project. Label every resulting value as projected until a controlled test produces observed evidence.

Before you run the cell, form a prediction about which strategy uses less assisted capacity.

  • Run the simulation cell with Shift+Enter.

You will see one Champion row and one Challenger row. Both rows cover the same 120 accounts.

The Challenger row uses fewer assisted contacts. It also uses fewer workload units.

  • Record the assisted-contact comparison for your slide: Champion ... vs Challenger ....
  • Record the workload-unit comparison for your slide: Champion ... vs Challenger ....
  • Record the projected cure-rate comparison for your slide: Champion ...% vs Challenger ...%.

That is the analytical case assembled. Your challenger now has a visible capacity impact and a projected outcome comparison.

Comparison Table Missing or Incomplete?

  • Rerun the earlier challenger cells if the notebook cannot read challenger_assignments.
  • Compare the closing parentheses around scored and scenario_rows with the supplied code.
  • Confirm the final two lines create and display comparison_df.

Help me diagnose my strategy comparison cell.

Package the IT handoff

A simulation explains the expected impact. An IT handoff defines the exact rule order and system action needed to implement the strategy.

You will package the rules as Python rows with four fields. The priority column preserves the same first-match sequence used by your challenger.

  • Use the notebook's add-cell control to create a Python cell below display(comparison_df).
  • Add the IT handoff table to the new cell using this code:
# Package the ordered rules for an IT implementation conversation.
handoff_rows = [
    (
        1,
        "PROMISE_FOLLOW_UP",
        "promise_kept = 1",
        "Retain the promise cadence and suppress competing treatments",
    ),
    (
        2,
        "DIGITAL_REMINDER",
        "risk_score >= 700 AND days_past_due <= 29 AND email_consent = 1",
        "Send the consented low-touch reminder",
    ),
    (
        3,
        "AGENT_CALL",
        "balance >= 5000 AND (risk_score < 620 OR days_past_due >= 60)",
        "Create a prioritized agent work item",
    ),
    (
        4,
        "EMAIL_NUDGE",
        "email_consent = 1",
        "Send the consented email treatment",
    ),
    (
        5,
        "SELF_SERVICE_PLAN",
        "No earlier rule matched",
        "Offer the self-service repayment path",
    ),
]

handoff_df = spark.createDataFrame(
    handoff_rows,
    schema=(
        "rule_priority INT, treatment STRING, entry_condition STRING, "
        "system_action STRING"
    ),
)
display(handoff_df)

What does this handoff contain?

  • The rule_priority field preserves the first-match evaluation order.
  • The treatment field names the action assigned by each rule.
  • The entry_condition field gives an IT partner the condition to translate into a decisioning system.
  • The system_action field describes the operational outcome that follows a match.

Before you run the cell, predict which treatment appears at priority five.

  • Run the handoff cell with Shift+Enter.

You will see five rows ordered from priority 1 through 5. The final row assigns SELF_SERVICE_PLAN when no earlier rule matches.

You now have the bridge from analytical logic to implementation. Each challenger action has a condition and a system outcome.

Handoff Table Missing a Rule?

  • Confirm handoff_rows contains five numbered tuples.
  • Check that the schema contains all four field definitions.
  • Rerun the cell containing handoff_df after correcting any missing comma or quote.

Help me repair my IT handoff DataFrame.

✔️ Awesome, I've got everything!

Your notebook now displays the two-row strategy comparison and the five-row IT handoff. Keep both results available for your leadership brief.

ⓧ I'd like to double check the full code

Compare your cumulative notebook with this complete collections_strategy_lab.py reference.

# Environment check
display(spark.sql("SELECT 'ready' AS status"))

# Build a deterministic, synthetic portfolio and its intentionally simple champion strategy.
spark.sql("""
CREATE OR REPLACE TEMP VIEW collections_accounts AS
WITH generated AS (
    SELECT
        id AS account_id,
        500 + pmod(id * 137, 9500) AS balance,
        300 + pmod(id * 29, 551) AS risk_score,
        CASE pmod(id, 5)
            WHEN 0 THEN 10
            WHEN 1 THEN 25
            WHEN 2 THEN 40
            WHEN 3 THEN 55
            ELSE 75
        END AS days_past_due,
        pmod(id, 5) AS prior_contacts,
        CASE
            WHEN pmod(id, 7) = 0 OR pmod(id, 7) = 1 THEN 1
            ELSE 0
        END AS promise_kept,
        CASE
            WHEN pmod(id, 4) > 0 THEN 1
            ELSE 0
        END AS email_consent,
        CASE
            WHEN pmod(id, 11) = 0 THEN 1
            ELSE 0
        END AS support_need_flag
    FROM range(1, 121)
)
SELECT
    *,
    CASE
        WHEN days_past_due <= 29 THEN 'DIGITAL_REMINDER'
        WHEN days_past_due <= 59 THEN 'AGENT_CALL'
        ELSE 'INTENSIVE_CALL'
    END AS champion_treatment,
    CASE
        WHEN risk_score >= 680
             AND prior_contacts <= 2
             AND pmod(account_id, 4) > 0 THEN 1
        WHEN promise_kept = 1
             AND pmod(account_id, 3) > 0 THEN 1
        ELSE 0
    END AS observed_cured
FROM generated
""")

portfolio_preview = spark.sql("""
SELECT *
FROM collections_accounts
ORDER BY account_id
LIMIT 10
""")
display(portfolio_preview)

# Monitor the champion strategy.
champion_summary = spark.sql("""
SELECT
    champion_treatment,
    count(*) AS accounts,
    round(avg(observed_cured) * 100, 1) AS observed_cure_rate_pct,
    round(sum(balance), 2) AS balance_managed,
    sum(
        CASE
            WHEN champion_treatment = 'AGENT_CALL' THEN 1
            WHEN champion_treatment = 'INTENSIVE_CALL' THEN 1
            ELSE 0
        END
    ) AS assisted_contacts
FROM collections_accounts
GROUP BY champion_treatment
ORDER BY champion_treatment
""")
display(champion_summary)

score_band_diagnosis = spark.sql("""
SELECT
    champion_treatment,
    CASE
        WHEN risk_score >= 700 THEN 'LOWER_RISK'
        WHEN risk_score >= 620 THEN 'MEDIUM_RISK'
        ELSE 'HIGHER_RISK'
    END AS score_band,
    count(*) AS accounts,
    round(avg(observed_cured) * 100, 1) AS observed_cure_rate_pct,
    round(sum(balance), 2) AS balance_managed
FROM collections_accounts
GROUP BY
    champion_treatment,
    CASE
        WHEN risk_score >= 700 THEN 'LOWER_RISK'
        WHEN risk_score >= 620 THEN 'MEDIUM_RISK'
        ELSE 'HIGHER_RISK'
    END
ORDER BY champion_treatment, score_band
""")
display(score_band_diagnosis)

# Define the challenger as ordered, explainable Python rules.
strategy_rules = [
    ("promise_kept = 1", "PROMISE_FOLLOW_UP"),
    (
        "risk_score >= 700 AND days_past_due <= 29 AND email_consent = 1",
        "DIGITAL_REMINDER",
    ),
    (
        "balance >= 5000 AND (risk_score < 620 OR days_past_due >= 60)",
        "AGENT_CALL",
    ),
    ("email_consent = 1", "EMAIL_NUDGE"),
]

case_lines = [
    f"WHEN {condition} THEN '{treatment}'"
    for condition, treatment in strategy_rules
]
challenger_case = (
    "CASE\n    "
    + "\n    ".join(case_lines)
    + "\n    ELSE 'SELF_SERVICE_PLAN'\nEND"
)

spark.sql(f"""
CREATE OR REPLACE TEMP VIEW challenger_assignments AS
SELECT
    *,
    {challenger_case} AS challenger_treatment
FROM collections_accounts
""")

reclassification = spark.sql("""
SELECT
    champion_treatment,
    challenger_treatment,
    count(*) AS accounts
FROM challenger_assignments
GROUP BY champion_treatment, challenger_treatment
ORDER BY champion_treatment, challenger_treatment
""")
display(reclassification)

# Compare projected outcomes under explicit teaching assumptions.
spark.sql("""
CREATE OR REPLACE TEMP VIEW strategy_comparison AS
WITH scored AS (
    SELECT
        *,
        least(0.70, 0.10 + (risk_score - 300) / 1000.0) AS base_cure_probability,
        CASE
            WHEN champion_treatment = 'DIGITAL_REMINDER' THEN 0.04
            WHEN champion_treatment = 'AGENT_CALL' THEN 0.08
            ELSE 0.10
        END AS champion_treatment_uplift,
        CASE
            WHEN challenger_treatment = 'PROMISE_FOLLOW_UP' THEN 0.12
            WHEN challenger_treatment = 'DIGITAL_REMINDER' THEN 0.06
            WHEN challenger_treatment = 'AGENT_CALL' THEN 0.14
            WHEN challenger_treatment = 'EMAIL_NUDGE' THEN 0.07
            ELSE 0.08
        END AS challenger_treatment_uplift
    FROM challenger_assignments
),
scenario_rows AS (
    SELECT
        account_id,
        'Champion' AS strategy,
        least(
            0.85,
            base_cure_probability + champion_treatment_uplift
        ) AS projected_cure_probability,
        balance * 0.30 * least(
            0.85,
            base_cure_probability + champion_treatment_uplift
        ) AS projected_recovery,
        CASE
            WHEN champion_treatment = 'AGENT_CALL' THEN 1
            WHEN champion_treatment = 'INTENSIVE_CALL' THEN 1
            ELSE 0
        END AS assisted_contact,
        CASE
            WHEN champion_treatment = 'DIGITAL_REMINDER' THEN 0.20
            WHEN champion_treatment = 'AGENT_CALL' THEN 1.00
            ELSE 1.25
        END AS workload_units
    FROM scored

    UNION ALL

    SELECT
        account_id,
        'Challenger' AS strategy,
        least(
            0.85,
            base_cure_probability + challenger_treatment_uplift
        ) AS projected_cure_probability,
        balance * 0.30 * least(
            0.85,
            base_cure_probability + challenger_treatment_uplift
        ) AS projected_recovery,
        CASE
            WHEN challenger_treatment = 'AGENT_CALL' THEN 1
            ELSE 0
        END AS assisted_contact,
        CASE
            WHEN challenger_treatment = 'AGENT_CALL' THEN 1.00
            WHEN challenger_treatment = 'PROMISE_FOLLOW_UP' THEN 0.25
            WHEN challenger_treatment = 'DIGITAL_REMINDER' THEN 0.15
            WHEN challenger_treatment = 'EMAIL_NUDGE' THEN 0.15
            ELSE 0.10
        END AS workload_units
    FROM scored
)
SELECT
    strategy,
    count(*) AS accounts,
    sum(assisted_contact) AS assisted_contacts,
    round(sum(workload_units), 1) AS workload_units,
    round(avg(projected_cure_probability) * 100, 1) AS projected_cure_rate_pct,
    round(sum(projected_recovery), 2) AS projected_recovery
FROM scenario_rows
GROUP BY strategy
ORDER BY strategy
""")

comparison_df = spark.sql("SELECT * FROM strategy_comparison ORDER BY strategy")
display(comparison_df)

# Package the ordered rules for an IT implementation conversation.
handoff_rows = [
    (
        1,
        "PROMISE_FOLLOW_UP",
        "promise_kept = 1",
        "Retain the promise cadence and suppress competing treatments",
    ),
    (
        2,
        "DIGITAL_REMINDER",
        "risk_score >= 700 AND days_past_due <= 29 AND email_consent = 1",
        "Send the consented low-touch reminder",
    ),
    (
        3,
        "AGENT_CALL",
        "balance >= 5000 AND (risk_score < 620 OR days_past_due >= 60)",
        "Create a prioritized agent work item",
    ),
    (
        4,
        "EMAIL_NUDGE",
        "email_consent = 1",
        "Send the consented email treatment",
    ),
    (
        5,
        "SELF_SERVICE_PLAN",
        "No earlier rule matched",
        "Offer the self-service repayment path",
    ),
]

handoff_df = spark.createDataFrame(
    handoff_rows,
    schema=(
        "rule_priority INT, treatment STRING, entry_condition STRING, "
        "system_action STRING"
    ),
)
display(handoff_df)

The final two sections add the projected scenario comparison and the ordered implementation handoff. The cumulative reference matches the completed core notebook.

Create the leadership brief

A leadership brief turns the notebook outputs into a decision. It separates the recommendation from its assumptions and proposed test.

You will use the Google Slides presentation from earlier. The single slide will answer what to test and why the test deserves approval.

  • Switch back to the open Google Slides presentation named Collections Strategy Challenger Recommendation.
  • Select Insert from the top menu.
  • Select Text box.
  • Click near the top of the existing slide.
  • Type Collections Strategy Challenger Recommendation as the slide title.

What Should the Slide Communicate?

The title establishes the decision under review. The body should connect the challenger recommendation to capacity and projected outcomes.

Assumptions define the limits of the claim. The controlled sample test defines how the team gathers real evidence.

  • Select Insert from the top menu.
  • Select Text box.
  • Click beneath the title to create the body text area.
  • Type Pilot the challenger on a controlled sample because it reduces projected assisted workload while preserving a measurable outcome test. as the recommendation.
  • Add Assisted contacts: followed by Champion ... vs Challenger ....
  • Add Workload units: followed by Champion ... vs Challenger ....
  • Add Projected cure rate: followed by Champion ...% vs Challenger ...%.

Keep the Evidence Traceable

Each KPI comparison comes directly from strategy_comparison. This lets a reviewer trace the slide back to the notebook output.

  • Add Simulation assumptions as a label below the KPI comparisons.
  • Add Cure uplifts are teaching assumptions. below the assumptions label.
  • Add Projected recovery uses a 30% payment fraction. below the cure-uplift statement.
  • Add Workload units are teaching assumptions. below the payment-fraction statement.
  • Add Next step: Run a controlled sample test with agreed cure and recovery success criteria. below the assumptions.
  • Add Monitor assisted contacts and workload units against available capacity. as the final line.

Before you review the finished slide, consider which claim could be misunderstood without the assumptions.

  • Review the slide from top to bottom.
  • Confirm the slide contains one recommendation.
  • Confirm the slide contains three notebook KPI comparisons.
  • Confirm the slide states all three simulation assumptions.
  • Confirm the slide proposes a controlled sample test with success criteria.
  • Confirm the slide includes capacity monitoring.

You should see one leadership slide supported by the Champion and Challenger notebook rows. The recommendation is ready for a technical review and an executive decision conversation.

Secret mission

Add a Support-Needs Guardrail

Your challenger improves workload allocation, but a production strategy also needs a customer-treatment control. In this mission, you will give every support-needs account a specialist path before any optimization rule can assign a lower-touch treatment.

Clean Up Your Resources

Clean Up Your Resources

This project has no incremental cost. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • The Collections Strategy Challenger Lab Python notebook in Databricks Free Edition.
  • The Collections Strategy Challenger Recommendation presentation in Google Slides.

Keep everything running

No action needed. Choose this if you want to keep reviewing the strategy outputs or refining your recommendation.

  • Leave the Collections Strategy Challenger Lab notebook saved in your Databricks workspace.
  • Leave the Collections Strategy Challenger Recommendation presentation saved in your Google account.
  • Run the notebook again whenever you want to recreate its temporary views or refresh its displayed results.

Pause - I'll come back to this later

Close your browser tabs to pause the work. Your saved artifacts remain available for your next session.

  • Close the browser tab containing the Collections Strategy Challenger Lab notebook.
  • Close the browser tab containing the Collections Strategy Challenger Recommendation presentation.
  • Reopen the notebook from your Databricks workspace when you return.
  • Click Run All to recreate the temporary views and displayed outputs.
  • Reopen the presentation from Google Slides when you are ready to revise the leadership brief.

Delete - I don't want to use this again

Remove both saved project artifacts to start fresh. The notebook contains synthetic data, so no customer records are affected.

  • Return to the Workspace browser in Databricks.
  • Right-click the Collections Strategy Challenger Lab notebook.
  • Select Move to Trash.
  • Switch back to the open Collections Strategy Challenger Recommendation presentation.
  • Select File from the Google Slides menu.
  • Select Move to trash.

Nice Work!

Nice Work!

Excellent work! Your Databricks Free Edition lab now carries a collections strategy change from portfolio diagnosis through challenger simulation to control testing.

You've learned how to:

  • Build a deterministic synthetic portfolio with SQL. Use score-band diagnostics to expose the champion strategy's assisted-contact capacity problem.
  • Build an explainable challenger strategy from ordered Python rules. Use champion/challenger analysis to compare projected operational performance under explicit teaching assumptions.
  • Package five ordered treatments into an IT-ready rule handoff. Turn the recommendation into a one-slide Google Slides brief. Support the decision with notebook KPIs. Propose a controlled sample test with success criteria. Include capacity monitoring.
  • Secret Mission: Added a support-needs guardrail that routes every flagged account to SPECIALIST_REVIEW before the optimization rules. Verified the control with guardrail_audit. The output reported 0 guardrail exceptions.

Ready to quiz yourself?