Where we start
RFM looks like a scoring exercise and is really a chain of small decisions: what counts as an order, which id is the customer, what "spend" means, what date the clock stops at, and where the knife goes between high and low. Each decision is quick, each one changes who lands in which group, and most tutorials make all of them silently. These six sessions make them out loud, on a real marketplace where 97 percent of customers bought exactly once, because that is the shape of most retail data and it is the shape that breaks the defaults.
★ What you walk out with tonight. A working RFM table on 93,358 real customers with the snapshot date written into the code, the eight-group taxonomy you can read as a mechanism instead of a poster, and the one sentence that stops the first argument every RFM project has: "as of which date?"
One customer becomes three numbers
6 min liveTake one real customer from the Olist marketplace. Three delivered orders between October 2017 and August 2018, R$ 515 in total. Measured from 1 September 2018 she is R 11, F 3, M 515. Every one of those three carries a choice you have to make before the number exists.
LiveWhat each letter measures, and the choice hiding inside each4 min▶
R, recency: days between the customer's last order and the snapshot date. Smaller is better, which is why R is the one dimension scored in reverse later on. Two choices: which date is "last order" (placed, approved, or delivered) and which date is "now". On Olist the placed timestamp is the honest one, because delivered dates depend on the carrier rather than on the customer.
F, frequency: the number of orders. Choices: all time or a window (the last 12 months is common, and it changes who counts as frequent), and whether cancelled, unavailable and undelivered orders count. Here only delivered orders count, which drops 2,963 of 99,441.
M, monetary: what the customer is worth in money. Choices: total spend or average basket (total rewards frequency twice, since F is already counted; average basket does not), and which column is "spend". Olist has both an items table (price plus freight per line) and a payments table (what was actually charged, including vouchers and instalments). The payments table is used here because it is the money that moved; 2,961 orders were paid with more than one payment and they are summed.
None of these is the right answer everywhere. What matters is that each is written down in the code, once, so that when a second analyst gets a different table the difference is a diff and not an argument.
Self-studyWhere RFM came from, and the date that has no source3 min▶
RFM is direct-mail technology. The version most people mean is Arthur Hughes's from Strategic Database Marketing (1994): sort the customer file by recency into fifths, sort each fifth by frequency into fifths, each of those by monetary, and you have 125 cells; then mail a random tenth of every cell, read the response rate per cell, and mail the rest of only the cells that paid. The scoring was never the point. The test mailing was, and session 5 is about the modern version of that step.
Many tutorials say RFM has been "in use since 1961". That date travels from page to page without a citation, and a search for its origin during this build found none. The direct-mail roots are real and usually dated to the 1960s catalogue business, but there is no founding paper and no single year to defend. Hughes 1994 is the citable anchor. When a fact has been copied more often than it has been checked, teach the uncertainty rather than the number.
The same shop, two tables. An analyst counts frequency over all time; the marketing platform the shop uses counts the trailing twelve months. A customer who bought monthly in 2023 and never since is "frequent" in one table and "once" in the other, and the win-back campaign built on the first table mails people the second table would call lost. Neither is wrong. Not writing the window down is what was wrong.
The frozen clock
5 min liveRecency is a distance, and a distance needs two ends. If "now" is whatever day the notebook happens to run, every customer's R changes every day, the groups drift, and a segment table from March cannot be compared with one from May because they were measured from different places. The fix is one line: a snapshot date, chosen deliberately and written into the code.
LiveChoosing the date: the end of the last complete month, not the last row3 min▶
The Olist export's last timestamp is 17 October 2018, so the obvious snapshot is df["order_purchase_timestamp"].max(). It is wrong by 47 days. Only 20 orders are dated September or October and not one of them was delivered: the export stopped at the end of August and a handful of stragglers leaked in. Measured from 17 October, every customer looks a month and a half staler than the data can support, and the whole recency column shifts.
The rule that survives: plot orders per month, find the last complete one, snapshot on the first day after it. Here that is 1 September 2018. Write it as a literal in the code, not as a computed max, so that re-running next quarter is a deliberate edit rather than a silent drift.
This is the same frozen-history discipline the retention course applies to cohort tables, and it matters here for the same reason: a segment view recomputed against a moving "today" quietly restates the past every time it runs.
Self-studyWhich id is the customer, and why the answer costs 3 percent of the rows2 min▶
Olist's orders table carries customer_id, and there are 99,441 of them for 99,441 orders: it is an order-side key, one per purchase. The customers table maps it to customer_unique_id, of which there are 96,096. Group by the first and every customer bought exactly once, by construction, and frequency is meaningless. Group by the second and 2,801 customers turn out to have bought more than once.
Every order system has this pair somewhere: a session or checkout id and a person id. Frequency is only defined against the person, and checking which one you have is the first line of any RFM build, before any date is chosen.
Eight corners of one box
4 min liveThe simplest RFM model splits each of the three columns into high and low, which gives every customer a three-digit code of ones and zeros and puts them in one of eight corners of a box. Each corner has a name and a first action. It is a mechanism, and it is worth being able to read a code without the poster.
| Code | Group | Who they are | First action |
|---|---|---|---|
| 111 | High value | Recent, frequent, big spend. The core | Protect. Early access, a human touch at key moments, never a discount they did not need |
| 101 | Key develop | Recent and big, but only once | A second purchase. Category cross-sell, a reason to return before the memory fades |
| 011 | Key win-back | Frequent and big, gone quiet | Win back. A relevant new arrival, not a generic coupon; find out why they left |
| 001 | Key potential | One big order, long ago | Reactivate cheaply. If it fails, let go |
| 110 | General potential | Recent and frequent, small baskets | Raise the basket. Bundles, thresholds for free shipping |
| 100 | General develop | Recent, once, small | Onboarding. Get the second order at low cost |
| 010 | General retain | Frequent once, small, gone quiet | Light touch. Content, not spend |
| 000 | Low value | Quiet, once, small | Deprioritise. Spend the budget elsewhere |
Self-studyWhat "high" turns out to mean on Olist, before session 2 makes it worse3 min▶
Put the knife at the mean of each column, the most common tutorial choice, and read what the eight groups contain on 93,358 real customers. The mean order count is 1.033, so "frequent" means two or more orders and only 3.0 percent of customers clear it. The mean spend is R$ 165 against a median of R$ 108, so the spend column is skewed right and only 29.3 percent are "big". The mean recency is 239.5 days and 54.8 percent are more recent than that.
The result: High value (111) is 1.28 percent of customers and holds 3.1 percent of revenue. The biggest group is General develop (100), recent one-time buyers with a small basket, at 37.9 percent. And the group holding the most revenue is not the corner with three ones at all: Key develop (101), recent single big baskets, is 15.1 percent of customers and 32.3 percent of revenue.
None of this is a flaw in the taxonomy. It is what a marketplace where 97 percent buy once looks like through it, and it is the reason the eight names should be read as coordinates rather than as verdicts. Session 2 moves the knife and watches every one of these shares change.
Raw orders to eight groups, on 93,358 real customers
★ 22 min · everyone buildsSeven steps in pandas on the public Olist dataset. Every number printed below is what the code produces; if yours differs, the difference is one of the six decisions and you should be able to name which.
Load the three tables and keep delivered orders. Two of the six decisions are made here: undelivered orders do not count, and the placed timestamp is the order date.
Attach the person id and the money. Decision three: the customer is customer_unique_id. Decision four: spend is the payments table, summed per order.
Freeze the clock. Decision five, as a literal, with the reason in a comment. Everything dated after it is dropped so the snapshot and the data agree.
One row per customer. Recency from the last order to the snapshot, frequency as a count of distinct orders, monetary as total spend. Decision six: total, not average.
Look at the three columns before cutting them. One bar chart per column, as shares, and a histogram for spend. Thirty seconds of looking tells you where a knife can and cannot go: a column that is one tall bar has nowhere to cut.
The knife, at the mean tonight. Step 5 showed why: the mean is a knife the reader can see on the picture. Above the mean is 1, below is 0, and recency is reversed because fewer days is better.
Name the corners and count them. The mapping is a dictionary, not a chain of ifs, so it can be swapped for another taxonomy in session 5 without touching the scoring.
What step 5 draws, on the real 93,358. Three columns, three completely different shapes, and the shapes decide where a knife can go.
Save the table with the date in the filename. rfm_2018-09-01.csv, not rfm.csv. Session 6 compares two of these against each other, and the comparison is only meaningful because each one says when it was taken.
Before session 2
30 min- Run the six steps on your own order table, or on Olist if you do not have one. Write the six decisions as comments at the top of the script before you write any code below them.
- Move the snapshot forward one month and re-run. Count how many customers change group. That number is the cost of an undated segment table.
- Swap total spend for average basket in step 4 and re-run step 6. Watch Key develop and Key potential trade places with General groups. Neither is wrong; write down which question each answers.
- Bring the table. Session 2 moves the knife five times on it, and session 3 puts a sample of it on a bench where every share is counted live.
Three questions
Clear all three and session 1 gets stamped in your passport.
1 · The Olist export's last order is dated 17 October 2018. Why is 1 September 2018 the better snapshot?
The snapshot goes at the end of the last complete month, found by plotting orders per month, and it is written as a literal so that re-running later is a deliberate edit rather than a silent drift. A is a convention that happens to coincide; the reason is the ragged edge.
2 · Grouping Olist by customer_id gives 99,441 customers who each bought exactly once. What has gone wrong?
Every order system carries a checkout id and a person id. Frequency is only defined against the person, and checking which id you have is the first line of the build, before any date or knife is chosen.
3 · Which corner of the eight holds the most revenue on Olist with the knife at the mean, and why is that not a paradox?
The names are coordinates, not verdicts. High value means all three digits above the knife; on a one-time-buyer marketplace that is a small corner. Where the revenue actually sits depends on the base, and the taxonomy is a way of seeing it, not a promise about it.
What this session teaches, and where it came from
Every fact on this page, and its verification tier, is recorded in the course's source map.
Session 1 cheat sheet · pin this
You have three columns and a knife at the mean. Session 2 moves the knife: to the median, into thirds the way Klaviyo does it, into fifths the way most tutorials do it, and to thresholds a merchant would actually write down. On the same 93,358 customers, the share called "best" goes from 1 percent to 16 percent depending on nothing but the knife, and under two of the cuts nine in ten of the "best" customers turn out to have bought exactly once. Bring your table.