The business question
Picture the risk team at a card issuer. They have a book of open accounts and a limited amount of attention: collections calls, credit line reviews, hardship offers. The question they actually care about is which of these customers is about to stop paying, and how early can we tell? Everything else, including whatever model eventually gets built, is in service of pointing that attention at the right accounts.
So before building anything predictive, I wanted to answer a simpler version: which attributes separate the people who default from the people who don't at all? If it's mostly behavior, the model and the operations should be built around behavior. If it's mostly who the customer is, that's a very different (and much more legally fraught) conversation.
Why this matters
The data is the UCI "Default of Credit Card Clients" dataset: real accounts from a bank in Taiwan, covering April through September 2005. Each row has the client's credit limit, six months of repayment status, statement balances, payments, a few demographic fields, and whether they defaulted on the following month's payment.
The timing makes it more interesting than a typical teaching dataset. In 2005 and 2006 Taiwan went through a credit card debt crisis after banks issued cards aggressively and a lot of cardholders ran up balances they couldn't carry. That's why 22.1% of these clients defaulted, several times what a healthy US portfolio would show. It's effectively a portfolio under stress, which is exactly when you find out which warning signs are real.
The approach
I grouped every candidate signal into three buckets and compared default rates across each:
- Behavior: most recent repayment status, how many of the last six months were late, and utilization (balance divided by limit).
- What the bank already decided: the credit limit.
- Who the customer is: age, education, marital status, and sex.
The measure is simple on purpose: default rate within each group, and how far that sits above or below the portfolio's 22.1%. No model yet. If a grouped default rate can't tell the difference between two segments, a model probably won't find much there either.
The build
Python: decoding the data, and one thing that didn't add up
The raw file is all numeric codes: education is 1 through 6, marital status is 0 through 3, and repayment status runs from -2 to 8. The first job was turning those into labels a risk manager would recognize, then building bands for age, credit limit and utilization.
import numpy as np
import pandas as pd
d = pd.read_csv("credit.csv")
pay = d["PAY_1"] # most recent month's repayment status
d["repayment_status"] = np.select(
[pay == -2, pay == -1, pay == 0, pay == 1, pay == 2],
["No balance", "Paid in full", "Revolving, current",
"1 month late", "2 months late"],
"3+ months late",
)
# How many of the last six months were late, not just the latest one
d["months_late"] = (d[[f"PAY_{i}" for i in range(1, 7)]] >= 1).sum(axis=1)
# Codes 0, 4, 5 and 6 aren't documented; group them rather than guess
d["education"] = d["EDUCATION"].map(
{1: "Graduate School", 2: "University", 3: "High School"}
).fillna("Other / Unknown")
The part worth flagging: the documented code definitions don't fully match the data. About two thirds of the clients with no statement balance are coded "1 month late," which is hard to square with having nothing to pay. I kept the codes as published and called it out rather than quietly recoding it, because a silent fix would make the "1 month late" group look cleaner than it is.
Tableau: two views for two different conversations
The Portfolio Overview is the "where does risk live" page: headline numbers, then default rate by repayment status, months late, credit limit, utilization and age. The Segment Detail page is the drill-down: a heatmap of repayment status against credit limit, and how far each customer segment sits from the portfolio rate. The core calculations are short:
// Default Rate
SUM([Defaulted]) / COUNT([Client ID])
// Portfolio Default Rate: fixed at the whole book, so it doesn't move with filters
MIN({ FIXED : SUM([Defaulted]) }) / MIN({ FIXED : COUNT([Client ID]) })
// Default Rate vs Portfolio, in percentage points
[Default Rate] - [Portfolio Default Rate]
Findings
1. The most recent payment is the strongest signal by a wide margin
Clients who are revolving a balance but current default at 12.8%. One month late, that jumps to 33.8%. Two months late, it's 69.6%. The step from one missed payment to two is the cliff: the default rate roughly doubles, and at that point most of these clients are already on their way out.
2. History stacks up in a staircase
Counting how many of the last six months were late gives a remarkably clean progression: 11.8% for clients with no late months, 29.6% with one, 50.8% with three, and 70.4% for clients who were late in all six. No single month tells the whole story, but the count almost does.
3. Risk is concentrated, which is good news operationally
The 22.5% of clients who are currently behind on a payment account for 51% of all defaults. Widen that to anyone who was late at any point in the last six months, about a third of the book, and you've covered roughly 65% of defaults. A collections team that focused on that third would be looking at most of the problem.
4. Low credit limits look risky, but the limit isn't the cause
Clients with limits under NT$50K default at 35.8%; clients at NT$300K and above default at 13.8%. It would be easy to read that as "lower limits cause default." They don't. The limit is the bank's own earlier risk judgment showing up again: riskier applicants were given smaller limits in the first place. Cutting someone's limit doesn't make them a safer borrower.
5. Demographics barely move the needle
Compared with a nearly 60-point spread across repayment status, the customer-profile fields are almost flat. Men default at 24.3% and women at 20.7%. Married clients at 23.5%, single at 20.9%. Education ranges from 19.3% for graduate school to 24.9% for high school. Age is a gentle U-shape, highest under 25 and over 55.
That matters for a second reason. In a US lending context, the Equal Credit Opportunity Act bars using sex or marital status in credit decisions. They're in the dashboard as filters so you can see for yourself how little they explain, not as anything a lender should act on.
Default risk in this portfolio is overwhelmingly a payment-history story. The practical version: treat the first missed payment as an early-warning trigger and the second as an emergency, and spend attention on the third of the book with any recent late payment rather than on customer profiles.
What I'd do next
The obvious next step is an actual probability-of-default model: a logistic regression first, because it's explainable, then a gradient-boosted model to see how much accuracy the extra complexity buys. I'd pick the approval cutoff based on what each kind of mistake costs, since missing a defaulter is far more expensive than wrongly flagging a good customer. Multiplying each client's default probability by their balance gives expected loss, which is the number that actually ranks where to spend attention. Clients who defaulted here were carrying NT$258 million, about 21% of all balances on the book.
I'd also rerun everything on the full 30,000-row file (this copy has 24,000), and dig into that repayment-code inconsistency before trusting the "1 month late" group in a model.