Learn logistic regression through a bank-fraud problem: probability, odds, log-odds, sigmoid curves, classification thresholds, precision, recall, F1 and business decisions.
Author
MLStation
Published
October 3, 2026
🏦 A $2,400 card transaction arrives at 2:13 AM. Is it fraud?
A bank does not need a prediction such as 0.73 dollars of fraud. It needs an estimate such as:
“Given what we know about this transaction, the estimated probability of fraud is 73%.”
Then the bank must make a second decision:
“Is 73% high enough to block the transaction, request verification, or allow it?”
Those are two different problems. Logistic regression estimates probability. A threshold converts probability into an action.
TipWhat you will be able to do
By the end of this lesson you should be able to:
recognize when a problem is classification, not ordinary regression;
explain why a straight-line probability model is problematic;
move between probability → odds → log-odds;
interpret logistic-regression coefficients and odds ratios;
explain why the sigmoid/logistic curve is needed;
fit a logistic-regression model in Python and obtain fraud probabilities;
explain TP, FP, TN and FN in business language;
explain why 0.5 is not automatically the right threshold;
choose a threshold based on false-negative cost, false-positive cost, recall, precision or F1;
explain a logistic-regression result to a non-technical stakeholder.
Every worked chart follows the same pattern: Data Simulator → Plot Code → Visible Result. The two code blocks are folded so you can focus on the visualization and expand the implementation when needed.
1. The Problem: Detect Fraud, Not a Number
Suppose each row is a credit-card transaction.
Our target is
Y =
\begin{cases}
1,&\text{fraud}\\
0,&\text{legitimate}.
\end{cases}
Possible predictors include:
transaction amount;
transaction hour;
distance from the customer’s usual location;
number of transactions in the previous hour;
whether the merchant is new to the customer;
whether the device is new;
historical customer behavior.
For a transaction with feature vector
X=(x_1,x_2,\ldots,x_p),
we want to estimate
P(Y=1\mid X).
Read this as:
The probability that the transaction is fraudulent, given the information in X.
Here:
Y is the outcome we want to predict;
X is the information available when the decision is made;
P(Y=1\mid X) is a number between 0 and 1.
TRANSACTION FEATURES X → MODEL → FRAUD PROBABILITY → BUSINESS THRESHOLD → ACTION
2. Why This Is Classification
Regression and classification answer different kinds of questions.
Problem
Target
Example output
Linear regression
continuous number
expected monthly spend = $214
Logistic classification
category / event
probability of fraud = 0.73
Final business decision
action
block / review / allow
A binary classification problem has two outcomes. We encode them as 0 and 1 because that makes the mathematics convenient.
The 1 class is usually called the positive class. Here:
Y=1 \Rightarrow \text{fraud}.
“Positive” does not mean good. It means the event we are trying to detect.
3. Why Not Just Use Linear Regression?
Linear regression models
\hat y = \beta_0+\beta_1x.
If we encode fraud as 1 and legitimate as 0, we might be tempted to fit a straight line and call \hat y a probability.
But a probability must satisfy
0\le P(Y=1\mid X)\le1.
A straight line has no such restriction.
3.1 See the problem
Data Simulator
View Data Simulator
import numpy as npimport pandas as pdrng = np.random.default_rng(42)n =700amount = rng.gamma(shape=2.0, scale=85, size=n)# Simulated probability of fraud rises with transaction amount.true_logit =-4.2+0.012* amounttrue_probability =1/ (1+ np.exp(-true_logit))fraud = rng.binomial(1, true_probability)linear_vs_logistic = pd.DataFrame({"amount": amount,"fraud": fraud})
Among transactions receiving comparable model probabilities, a well-calibrated model should see roughly the corresponding fraction become fraud.
For example, among many transactions receiving probabilities around 0.80, approximately 80% should be fraudulent if the model is well calibrated.
Probability quantifies uncertainty before the outcome is known.
6. From Probability to Odds
People often use probability and odds interchangeably, but mathematically they are different.
If an event has probability p, its odds are
\text{odds}=\frac{p}{1-p}.
Example: probability = 0.75
\text{odds}
=
\frac{0.75}{0.25}
=
3.
This means:
3 to 1 odds in favor of the event.
For every one expected non-event, there are three expected events under this probability ratio.
Example: probability = 0.20
\text{odds}
=
\frac{0.20}{0.80}
=
0.25.
Equivalent wording:
1 to 4 odds in favor of the event.
7. Probability ↔︎ Odds
Starting with
o=\frac{p}{1-p},
solve for p:
o(1-p)=p
o-op=p
o=p(1+o)
therefore
\boxed{p=\frac{o}{1+o}}.
So probability and odds contain the same information; they simply use different scales.
7.1 Interactive probability-to-odds relationship
Data Simulator
View Data Simulator
import numpy as npimport pandas as pdp_values = np.linspace(.01,.99,199)probability_odds = pd.DataFrame({"probability": p_values})probability_odds["odds"] = ( probability_odds["probability"]/ (1-probability_odds["probability"]))
Plot Code
View Plot Code
import plotly.express as pxfig = px.line( probability_odds, x="probability", y="odds", title="Probability and Odds Are Different Scales")fig.add_vline(x=.5, line_dash="dot")fig.add_hline(y=1, line_dash="dot")fig.update_layout( xaxis_title="Probability p", yaxis_title="Odds = p / (1-p)", height=430)fig.show()
Notice:
p=0.5 corresponds to odds =1;
p>0.5 corresponds to odds greater than 1;
p<0.5 corresponds to odds less than 1;
odds are not restricted to 0–1.
8. Why Log-Odds?
Odds are positive:
0<\text{odds}<\infty.
Taking the logarithm gives
-\infty<\log(\text{odds})<\infty.
This is exactly the range a linear expression can occupy.
import plotly.express as pxfig = px.line( odds_ratio_demo, x="starting_probability", y="new_probability", title="A Constant Odds Ratio Does Not Mean a Constant Probability Increase")fig.add_shape(type="line", x0=0, y0=0, x1=1, y1=1, line={"dash":"dot"})fig.update_layout( xaxis_title="Starting probability", yaxis_title="Probability after doubling the odds", height=440)fig.show()
For example:
starting at p=0.10, doubling odds gives about p=0.182;
starting at p=0.50, doubling odds gives about p=0.667.
That is why logistic-regression coefficients are naturally interpreted on the odds scale.
14. A More Realistic Fraud Model
Now let us simulate transactions with several useful predictors.
We will use:
amount_100: transaction amount measured in hundreds of dollars;
distance_100km: distance from usual location in hundreds of kilometers;
new_device: 1 if the device has not been seen before;
tx_last_hour: number of transactions in the previous hour.
Notice the overlap. Fraud does not occur only above one perfect amount or distance.
This is exactly why probability is useful: the model can represent varying degrees of risk rather than pretending there is a perfect boundary in the raw data.
19. Threshold Animation: Watch the Confusion Matrix Change
Data Simulator
View Data Simulator
import numpy as npimport pandas as pdthreshold_rows = []for t in np.round(np.arange(.05,.96,.05),2): pred = (test_probability >= t).astype(int) tp =int(((pred==1) & (y_test.to_numpy()==1)).sum()) fp =int(((pred==1) & (y_test.to_numpy()==0)).sum()) fn =int(((pred==0) & (y_test.to_numpy()==1)).sum()) tn =int(((pred==0) & (y_test.to_numpy()==0)).sum())for cell,count in {"TP: fraud caught":tp,"FP: legitimate flagged":fp,"FN: fraud missed":fn,"TN: legitimate allowed":tn }.items(): threshold_rows.append({"threshold":f"{t:.2f}","cell":cell,"count":count })threshold_animation = pd.DataFrame(threshold_rows)
Plot Code
View Plot Code
import plotly.express as pxfig = px.bar( threshold_animation, x="cell", y="count", animation_frame="threshold", range_y=[0,threshold_animation["count"].max()*1.12], title="Move the Threshold: Every Decision Count Changes")fig.update_layout( xaxis_title="Outcome", yaxis_title="Number of test transactions", height=480)fig.show()
Press Play.
As the threshold rises, the bank becomes more conservative about labeling something fraud.
Typically:
fewer transactions are flagged;
false positives decrease;
true positives can also decrease;
false negatives increase.
There is no threshold that magically eliminates both types of error when the classes overlap.
20. Recall: When Missing Fraud Is Expensive
Recall asks:
Of all actual fraud transactions, what fraction did we catch?
\boxed{
\text{Recall}
=
\frac{TP}{TP+FN}
}
The denominator contains all actual positives.
High recall means few false negatives.
For fraud detection, medical screening, safety alerts and some security systems, false negatives can be particularly costly. In such cases, the organization may choose a lower threshold to catch more positives.
But that usually creates more false positives.
21. Precision: When False Alarms Are Expensive
Precision asks:
Of everything we flagged as fraud, what fraction was actually fraud?
\boxed{
\text{Precision}
=
\frac{TP}{TP+FP}
}
The denominator contains all predicted positives.
High precision means relatively few false alarms among flagged cases.
Precision may be especially important when:
every alert requires expensive manual investigation;
customers suffer major friction when flagged;
review capacity is limited;
the positive action itself is costly.
22. F1: Balance Precision and Recall
Sometimes both false positives and false negatives matter and we want one summary that balances precision and recall.
F1 is high only when both precision and recall are reasonably high.
But F1 does not know the dollar cost of fraud, customer friction, staffing constraints or legal requirements.
23. Interactive Threshold Tradeoff: Precision, Recall and F1
Data Simulator
View Data Simulator
import numpy as npimport pandas as pdfrom sklearn.metrics import precision_score, recall_score, f1_scoremetric_rows = []for t in np.arange(.02,.96,.01): pred = (test_probability >= t).astype(int) metric_rows.append({"threshold":t,"precision":precision_score( y_test,pred,zero_division=0 ),"recall":recall_score( y_test,pred,zero_division=0 ),"F1":f1_score( y_test,pred,zero_division=0 ) })threshold_metrics = pd.DataFrame(metric_rows)threshold_metrics_long = threshold_metrics.melt( id_vars="threshold", var_name="metric", value_name="score")
Plot Code
View Plot Code
import plotly.express as pxfig = px.line( threshold_metrics_long, x="threshold", y="score", color="metric", title="Threshold Is a Business Lever")fig.update_traces( hovertemplate="Threshold=%{x:.2f}<br>Score=%{y:.3f}")fig.update_layout( yaxis_range=[0,1.02], xaxis_title="Fraud threshold", yaxis_title="Metric value", height=470)fig.show()
Hover over the curves.
A lower threshold generally increases recall because the model flags more transactions.
A higher threshold generally increases the strictness required to call something fraud and can improve precision over useful ranges, while recall falls.
24. How Should a Business Choose the Threshold?
There is no universal “correct” threshold.
Start with the business consequence.
🛡️ Minimize missed fraud
False negatives are very costly.
Emphasize: recall.
Often choose a lower threshold.
🚨 Minimize false alarms
Investigations or customer blocks are expensive.
Emphasize: precision.
Often choose a higher threshold.
⚖️ Balance both
FP and FN both matter without a clear asymmetric cost.
Possible summary: F1.
Still inspect the actual errors.
💵 Known business costs
Translate TP, FP, FN and TN into money or utility.
Choose: threshold maximizing expected business value.
25. Threshold Selection by Business Cost
Suppose, for teaching purposes:
each missed fraud (FN) costs $500;
each false alarm (FP) costs $12;
catching fraud has no additional modeled loss;
correctly allowing a legitimate transaction has no modeled loss.
Then
\text{Cost}(t)
=
500\,FN(t)
+
12\,FP(t).
This is not a universal fraud-cost model. It is an example of converting business consequences into a threshold objective.
Data Simulator
View Data Simulator
import numpy as npimport pandas as pdbusiness_rows = []for t in np.arange(.02,.96,.01): pred = (test_probability >= t).astype(int) truth = y_test.to_numpy() tp = ((pred==1)&(truth==1)).sum() fp = ((pred==1)&(truth==0)).sum() fn = ((pred==0)&(truth==1)).sum() tn = ((pred==0)&(truth==0)).sum() cost =500*fn +12*fp business_rows.append({"threshold":t,"TP":tp,"FP":fp,"FN":fn,"TN":tn,"business_cost":cost })business_threshold = pd.DataFrame(business_rows)best_business_row = business_threshold.loc[ business_threshold["business_cost"].idxmin()]
Plot Code
View Plot Code
import plotly.express as pxfig = px.line( business_threshold, x="threshold", y="business_cost", title="Choose a Threshold from Business Consequences")fig.add_vline( x=float(best_business_row["threshold"]), line_dash="dash", annotation_text=("Lowest simulated cost: "f"{best_business_row['threshold']:.2f}" ))fig.update_layout( xaxis_title="Fraud threshold", yaxis_title="Simulated error cost ($)", height=450)fig.show()
This makes the logic explicit:
The threshold is not chosen because 0.5 “looks mathematically natural.” It is chosen because a particular operating point best supports the decision objective.
ImportantImportant correction to a common simplification
It is not generally true that businesses “mostly maximize recall.”
Some applications strongly prioritize recall; others prioritize precision, capacity, expected cost, safety, customer experience, or a constrained combination.
The metric should follow the consequence of the errors.
26. A Three-Zone Policy Can Be Better Than One Hard Threshold
Real systems do not always need only two actions.
A bank might use:
p < 0.10 → allow automatically
0.10 ≤ p < 0.60 → request verification / review
p ≥ 0.60 → block or escalate
Probability is valuable precisely because it supports graded decisions.
The model can rank risk while the business chooses multiple action regions.
27. What About Accuracy?
Accuracy is
\text{Accuracy}
=
\frac{TP+TN}
{TP+TN+FP+FN}.
It can be misleading for rare events.
Suppose only 1% of transactions are fraud.
A useless model that predicts “legitimate” for every transaction achieves:
99\%\text{ accuracy}.
But:
\text{Recall}=0.
It catches no fraud.
For imbalanced classification, always ask what the metric is rewarding.
28. Precision–Recall Curve: See Many Thresholds at Once
Instead of selecting one threshold immediately, inspect the available precision–recall tradeoff.
So the estimated fraud probability is approximately 66.8%.
Now the business rule acts.
At threshold 0.50:
0.668\ge0.50
\Rightarrow
\text{flag fraud}.
At threshold 0.80:
0.668<0.80
\Rightarrow
\text{do not flag}.
Nothing about the fitted probability changed. Only the decision policy changed.
32. How to Explain a Logistic Regression Result
A good explanation has three levels.
Level 1 — Probability
“The model estimates a 67% probability of fraud for this transaction.”
Level 2 — Feature / odds interpretation
“Holding the other modeled variables fixed, a one-unit increase in this predictor multiplies fraud odds by e^{\beta_j}.”
Level 3 — Decision
“Our current operating threshold is 40%, so this transaction is sent for review.”
Do not collapse these into one statement.
MODEL INTERPRETATION ≠ PROBABILITY INTERPRETATION ≠ BUSINESS DECISION
33. Common Mistakes
Mistake 1: “Logistic regression predicts 0 or 1.”
More precisely, logistic regression estimates probabilities/scores; a decision rule converts them into class labels.
Mistake 2: “A coefficient of 0.7 means probability rises by 70%.”
No. It means log-odds increase by 0.7 per unit, and odds are multiplied by e^{0.7}.
Mistake 3: “Odds ratio 2 means probability doubles.”
No. Probability change depends on the starting probability.
Mistake 4: “0.5 is the correct threshold.”
It is a common default, not a universal business optimum.
Mistake 5: “High accuracy means good fraud detection.”
Not with severe class imbalance.
Mistake 6: “High recall is always best.”
Only when the value of catching positives justifies the false-positive burden and other consequences.
Mistake 7: “Probability 0.8 automatically means 80% of such cases are positive.”
That interpretation requires adequate calibration on the relevant population.
34. Probability Is the Center of the Classification Story
Logistic regression is valuable because it separates uncertainty estimation from action.
X ↓ LINEAR SCORE z = β₀ + βᵀX ↓ LOG-ODDS ↓ exp() ODDS ↓ o/(1+o) PROBABILITY P(Y=1|X) ↓ threshold / policy CLASSIFICATION OR BUSINESS ACTION
Probability enables several different uses:
Ranking
Who is riskier?
0.91 > 0.72 > 0.28
Binary classification
Is probability above a chosen threshold?
Multiple action tiers
Allow, verify, review, block.
Expected value
Combine probability with financial consequences.
Communication
Explain uncertainty rather than pretending every prediction is certain.
35. A Compact Decision Guide
Business question
What to examine
“How likely is fraud?”
predicted probability
“How does x change risk?”
coefficient + odds ratio
“Catch as much fraud as possible”
recall + FN
“Reduce false alarms”
precision + FP
“Balance precision and recall”
F1 + actual error counts
“Choose an operating policy”
threshold curves
“Errors have dollar values”
expected business cost/value
“Can we trust 0.8 as 80%?”
calibration
“Fraud is rare”
precision–recall behavior + class balance
36. Final Mental Model
✨ Logistic regression does not begin with classification. It begins with probability.
The model learns a linear relationship on the log-odds scale, converts it to a probability with the logistic function, and only then does the organization choose how that probability becomes a decision.
Remember the chain:
\boxed{
X
\rightarrow
\beta_0+\beta^TX
\rightarrow
\log\frac{p}{1-p}
\rightarrow
p
\rightarrow
\text{threshold}
\rightarrow
\text{action}
}
Summary
Fraud detection is a binary classification problem because the target is fraud/not fraud.
Linear regression is poorly suited to modeling a probability directly because a line is unbounded.
Logistic regression models
\log\frac{p}{1-p}
=
\beta_0+\beta^TX.
The sigmoid converts any real-valued score into a probability between 0 and 1.
Odds are
\frac{p}{1-p}.
A coefficient \beta_j changes log-odds additively.
e^{\beta_j} is the corresponding odds ratio for a one-unit predictor increase, holding other included predictors fixed.
The predicted probability and the final class decision are different objects.
Thresholds control the balance between TP, FP, TN and FN.
Recall emphasizes catching positives and reducing FN.
Precision emphasizes reliability of positive flags and reducing FP.
F1 balances precision and recall, but does not encode actual business costs.
The best threshold depends on the real consequence of errors.
Calibration asks whether numerical probabilities correspond to observed event frequencies.
Probability is what makes logistic regression useful not only for classification, but for ranking, risk estimation, tiered actions and business decisions.