All Data Labs

Machine learning

Retention Offer Uplift Modeling

hard4–5 hours3 datasetsPremium

The whole brief is open to read. This lab's datasets come with Premium.

Company
AT&T
Job positions
Data ScientistProduct AnalystData Analyst
Topics
Uplift modelingIncrementalityRandomized campaignTargetingscikit-learn

The scenario

AT&T: Measure how much a retention offer changes each customer's chance of churning, then decide who should get the next one.

AT&T sends a discount to customers whose contracts are ending, and last June it went to a random half of them instead of the usual list of those most likely to churn. A customer who would have stayed anyway, or who churns whatever you offer, is money spent for nothing. You're a data scientist on the retention team, and the next group of contracts comes up for renewal in October.

Your task

Estimate how much the offer changes each customer's chance of churning, find the customers it does not help, and choose who to send it to within a fixed budget.

Instructions

  1. 1Load the campaign and customer data. Check that the two groups look alike across the customer attributes, since everything that follows depends on the split being random.
  2. 2Clean the data. Handle repeated rows, customers in the campaign missing from the customer table, and offers that were never delivered. Say what you did with each.
  3. 3Measure the overall effect first: the difference between the two groups in the share who churned within sixty days, with a confidence interval around it.
  4. 4Now model the difference the offer makes to a customer rather than whether they churn. One model per group, subtracting their predictions, is one way; putting the group in as an input and taking the difference is another.
  5. 5Look for where that difference changes, and in particular for customers the offer does not help. Compare the two groups inside those slices on their own.
  6. 6Score your ranking on campaign data you held back, using an uplift curve, also called a Qini curve: for each slice from the top of the ranking, the gap between the two groups inside it. Compare that against ranking customers by how likely they are to churn.
  7. 7Choose who in renewals_due.csv to send the offer to. It costs $180 for each customer who accepts, about 41 percent accept, and the budget is $150,000. Save targets.csv with the columns customer_id, predicted_uplift and targeted.

Datasets

All files come in one download.

campaign.csv

The June 2025 campaign, with 14,000 renewing customers split at random between getting the offer and not, and whether each left in the sixty days after.

14,016 rows · 5 columns · 427 KB

ColumnTypeDescription
customer_idtextThe customer.
assigned_grouptextoffer or control, assigned at random.
offered_ondateDate the offer was sent.
offer_acceptedtextyes or no for customers who received the offer. Empty for the control group, and also empty where the offer was never delivered.
churned_within_60dinteger1 if the customer left within sixty days of the offer date, otherwise 0.
Preview the first 5 rows
customer_idassigned_groupoffered_onoffer_acceptedchurned_within_60d
C1004000offer2025-06-14no0
C1004001control2025-06-29empty0
C1004002control2025-06-14empty0
C1004003control2025-06-05empty0
C1004004offer2025-06-21no0

customers.csv

Account attributes for the campaign customers and the October renewals, as they stood when the offer was sent.

18,938 rows · 15 columns · 1.5 MB

ColumnTypeDescription
customer_idtextUnique ID of the customer.
tenure_monthsintegerMonths the customer has been with the company.
plan_typetextunlimited_premium, unlimited_standard, shared_data or prepaid.
monthly_revenue_usddecimalWhat the account bills each month.
lines_on_accountintegerPhone lines on the account.
device_financing_activetextWhether the customer is still paying off a phone: yes or no.
autopay_enrolledtextWhether the account pays automatically: yes or no.
data_gb_last_monthdecimalData used last month, in gigabytes.
support_tickets_90dintegerSupport tickets the customer opened in the last ninety days.
dropped_calls_90dintegerDropped calls recorded on the customer's lines in the last ninety days.
international_plantextWhether an international plan is attached: yes or no.
regiontextOperating region the account belongs to.
fiber_bundledtextWhether home internet is bundled on the account: yes or no.
competitor_fiber_availabletextWhether a rival sells fiber at the customer's address: yes or no.
contract_ends_ondateDate the current contract ends.
Preview the first 5 rows
customer_idtenure_monthsplan_typemonthly_revenue_usdlines_on_accountdevice_financing_activeautopay_enrolleddata_gb_last_monthsupport_tickets_90ddropped_calls_90dinternational_planregionfiber_bundledcompetitor_fiber_availablecontract_ends_on
C10040013prepaid75.781yesyes0.201noWestyesno2025-07-29
C100400220shared_data86.593nono11.812yesWestnono2025-07-28
C100400348unlimited_standard28.681nono30.401noSouthwestnono2025-07-04
C100400530shared_data27.151noyes27.600noSoutheastnono2025-07-16
C100400648shared_data85.21noyes34.702noSoutheastyesno2025-08-10

renewals_due.csv

The 5,000 customers whose contracts end in October 2025, which is who you are choosing from.

5,000 rows · 2 columns · 98 KB

ColumnTypeDescription
customer_idtextThe customer.
contract_ends_ondateDate their contract ends.
Preview the first 5 rows
customer_idcontract_ends_on
C11040002025-10-28
C11040012025-10-15
C11040022025-10-12
C11040032025-10-04
C11040042025-10-21

Hints

No customer is both sent the offer and not sent it, so a predicted difference cannot be checked against a true value for anybody. An uplift curve works around that by comparing the two groups inside each slice of your ranking.

top = ranked.head(2000)
top[top.assigned_group == "control"].churned_within_60d.mean() - top[top.assigned_group == "offer"].churned_within_60d.mean()

An offer that was never delivered still belongs to the group it was assigned to, which is what analysis by intention to treat means. Moving those customers across, or dropping them, breaks the random split the whole analysis rests on.

Deliverable

A public GitHub repo with your analysis (a notebook or scripts), targets.csv for the October renewals, and a README with your randomization check, the overall effect, your uplift curve against a risk-ranked baseline, and who you would target.

When you're done, post your repo in the Solutions tab to share it with other learners.

What grading checks

Use this checklist to review your own work before you post and share it.

  • Submitted GitHub repo is public and reachable.
  • Repo contains at least one notebook or script file.
  • targets.csv has a predicted difference and a targeting decision for every October renewal, inside the budget.
  • The targeted customers would stop at least 90 customers churning, against 78 for the same number of highest risk customers.
  • Fewer than 5 percent of the targeted customers are ones the offer makes more likely to churn.
  • The random split is checked across customer attributes before any modeling, and undelivered offers stay in the group they were assigned to.
  • The customers the offer does not help are identified and kept out of the targets, and the ranking is scored with an uplift curve against a risk-ranked baseline.