RFM Analysis Part 1: Introduction
Most organizations are sitting on years of transactional data, yet still struggle to answer some very basic questions such as:
§ Who are our best donors
or customers?
§ Who is drifting
away?
§ Where should we
focus our limited time, budget, and attention?
RFM analysis is one of the simplest and
most effective ways to begin answering those questions. RFM stands for Recency,
Frequency, and Monetary value, a framework used to evaluate how individuals
have interacted with an organization over time. Recency measures how
recently someone has taken an action, such as making a donation or purchase. Frequency
captures how often they take that action, while Monetary reflects the
total or average amount of value they contribute. When combined, these three
dimensions provide a compact but powerful summary of behavioral patterns.
At its core, RFM analysis is not
about complex algorithms or black-box models. Instead, it offers a structured
way to organize real-world behavior into meaningful segments that reveal who is
highly engaged, who was engaged in the past but is beginning to lapse, who
contributes significant value but interacts infrequently, and who may no longer
warrant continued outreach. Rather than treating an entire audience as a
single, homogeneous group, RFM allows organizations to acknowledge that
different people are at very different points in their lifecycle.
The RFM framework is incredibly value
for fundraising and nonprofit organizations, allowing them to identify
high-value donors who merit personal stewardship, surface donors who are at
risk of lapsing before they disappear entirely, prioritize campaign targeting,
improve retention, and allocate staff time more strategically. These insights
directly support both short-term fundraising performance and long-term donor
sustainability.
But, the same framework extends well
beyond non-profits. The underlying questions—how recently someone engaged, how
often they engage, and how much value they generate—apply just as naturally to
customers, subscribers, members, or users. In an e-commerce or subscription
business, for example, RFM can highlight loyal repeat purchasers, one-time
buyers who never returned, and high-spending customers whose activity has
recently declined. The domain changes, but the analytical logic remains the
same.
I hadn’t encountered RFM until recently
when it was mentioned by a friend and colleague of mine, DT Oliver. As DT
explained the basics of how it works, I immediately saw how powerful it could
be for segmenting donors, customers, etc. This led to DT and I collaborating
together on how to segment constituents and visualize that data in order to best
aid non-profit fundraisers in enhancing their data-informed fundraising
strategy.
During this process, we learned a lot
about what works well and what doesn’t. So, in this blog post, the first in a three-part
series, I’m going to talk a bit more about RFM, how it works, and some of the
best ways to segment the data. I’m going to be using non-profit donors for most
of my examples, but just remember that this type of analysis works in many
other areas as well.
Let’s get started!!
The Basics of RFM
RFM analysis works by converting raw
transactional data into relative rankings along three dimensions: Recency,
Frequency, and Monetary.
For donors, Recency is
typically calculated as the number of days since a their most recent gift; Frequency
is the number of gifts they’ve made; and Monetary represents the total
amount they’ve given.
Note: These are somewhat simplistic
in nature and you may wish to inject a bit more nuance into your analysis. For
example, simply measuring the total number of gifts or the total amount will
tend to create higher rankings for donors you’ve had for a long time, so it may
be useful to include other things such as the average number of gifts or
average amount per month since the donor’s first gift. This helps to balance and
normalize your results.
Once we have the raw numbers for each
of the key metrics, we typically group donors into a small number of ordered
“buckets” for each metric—most commonly 1-5. For each dimension, donors are
ranked relative to one another and then divided into quintiles. Donors in the
top quintile receive a score of 5, the next quintile a 4, and so on down to 1.
Because the interpretation differs
slightly by metric, the direction of the scoring is important. For Recency,
donors who have given most recently (smaller numbers) receive higher scores.
For Frequency and Monetary, donors who give more often or contribute higher
amounts (larger numbers) receive higher scores.
The result of this process is three
separate scores for each donor, each on a scale from 1 to 5, with 5 being the highest.
These three scores are then combined to form a donor’s overall RFM score. While
the implementations vary, the most common method is to concatenate them into a
single three-digit score. For example, R=5, F=4, M=3 becomes 543. This provides
us with one compact score for each donor. Your champion donors will be 555,
being highly ranked in all three categories.
Once donors have RFM scores, those
scores can be translated into descriptive segments—such as “Champions,”
“Loyal,” “At-Risk,” or “Lapsed”—that make the results easier to interpret and
act upon.
Segmenting the Donor Data
For these demos, I’ve created a
sample data set that contains over 380,000 donation records for 4,000 unique
donors, covering a period of time from January 1, 2015 to February 3, 2025.
So let’s put that data into Tableau
and start segmenting it!!
Note: I learned how to create many of
these calcs from a great blog from Hevo, How to Perform
RFM Analysis in Tableau.
We’ll break this into five steps:
1.
Calculate
the key metrics that will drive the scores.
2.
Calculate
a rank percentile for each donor’s scores.
3.
Group
those resulting percentiles into quintiles.
4.
Combine
the scores.
5.
Translate
into descriptive segments.
1. Calculate the Key Metrics
Our key metrics will be Days Since
Last Gift (Recency), Total Gifts (Frequency), and Total Amount
(Monetary).
Days Since Last Gift
// Number of days
since the last gift.
DATEDIFF('day', {FIXED [Donor ID]: MAX([Gift Date])}, TODAY())
Total Gifts
// Total number of
gifts
{FIXED [Donor ID]: COUNTD([Gift ID])}
Total Amount
// Total gift amount
{FIXED [Donor ID]: SUM([Gift Amount])}
Note: I’m using FIXED LODs here in an
attempt to avoid certain aggregation issues down the road, but you could use
Window Calcs as well.
2. Calculate Rank Percentile
Now that we have our key metrics, we
need to calculate percentiles for each, being sure that we sort properly since
lower numbers are better for recency, while higher numbers are better for
frequency and monetary.
Percentile Recency
// Percentile (0 to
1) based on recency.
// Lower numbers
should be higher recency values.
// For this reason,
we sort descending.
RANK_PERCENTILE(MAX([Days Since Last
Gift]), 'desc')
Percentile Frequency
// Percentile (0 to
1) based on frequency.
// Higher counts
should be higher values.
// For this reason,
we sort ascending.
RANK_PERCENTILE(MAX([Total Gifts]),
'asc')
Percentile Monetary
// Percentile (0 to
1) based on monetary.
// Higher amounts
should be higher values.
// For this reason,
we sort ascending.
RANK_PERCENTILE(MAX([Total Amount]),
'asc')
Note: In my testing, I found that
RANK_PERCENTILE works slightly differently in Tableau Desktop than in Tableau
Prep (yeah, I was surprised too!!). While the differences aren’t huge, they can
be significant as they often result in different quintiles (the next step). In
a later post in this series, we’re going to bring Prep into play, but for now,
I just want to provide alternative versions of these calculations that will
work the way Prep works.
Percentile Recency
// Adjusted calc to
work the same way as Prep.
// (Rank - 1) / (N -
1) instead of Desktop’s (Rank / N)
(RANK(MAX([Days Since Last Gift]), 'desc') - 1)/(MAX([Total Donors])-1)
Percentile Frequency
// Adjusted calc to
work the same way as Prep.
// (Rank - 1) / (N -
1) instead of Desktop’s (Rank / N)
(RANK(MAX([Total Gifts]), 'asc') - 1)/(MAX([Total Donors])-1)
Percentile Monetary
// Adjusted calc to
work the same way as Prep.
// (Rank - 1) / (N -
1) instead of Desktop’s (Rank / N)
(RANK(MAX([Total Amount]), 'asc') - 1)/(MAX([Total Donors])-1)
3. Group into Quintiles
We now have scores of 0 to 1 for each
score for each donor. Next, we need to group these into quintiles (values of
1-5).
Quintile Recency
// Group this into
quintiles.
IF [Percentile
Recency] <= 0.20 THEN
1
ELSEIF [Percentile Recency] <= 0.40 THEN
2
ELSEIF [Percentile Recency] <= 0.60 THEN
3
ELSEIF [Percentile Recency] <= 0.80 THEN
4
ELSE
5
END
As you’d expect, the same basic
structure is also used for Frequency and Monetary so I won’t write all three of
them out.
4. Combine the Scores
OK, we now have scores of 1-5 for
each donor for each of our metrics, Recency, Frequency, and Monetary. We can
now concatenate them together to create a single consolidated RFM Score.
RFM Score
// Combine into RFM
value.
STR([Quintile
Recency]) + STR([Quintile Frequency]) + STR([Quintile Monetary])
5. Translate Into Descriptive Segments
Now we’ll look at each individual
score and group the donors into descriptive segments. I’m providing a
relatively simple example, so you’ll want to think through your own groupings.
For simplicity, I’ve created separate calculated fields, R, F, and M that refer
back to the Quintiles from step 3.
Segment
IF [R] = 5 AND [F]
= 5 AND [M] = 5 THEN
// Best of the best
'Champions'
ELSEIF [R] >= 4 AND [F]
>= 4 AND [M] >= 4 THEN
// Nearly elite
'Potential Champions'
ELSEIF [R] >= 4 AND [F]
>= 4 THEN
// Consistent supporters
'Loyal'
ELSEIF [M] = 5 AND [R]
>= 3 THEN
// High monetary but not necessarily
frequent
'Big Spenders'
ELSEIF [R] = 5 AND [F]
= 1 THEN
// Brand new donors
'New'
ELSEIF [R] >= 4 AND [F]
<= 2 THEN
// Recent but not yet frequent
'Promising'
ELSEIF [R] = 2 AND ([F]
>= 3 or [M] >= 3) THEN
// Previously strong but not recent
'At Risk'
ELSEIF [R] = 3 AND [F]
<= 2 THEN
// Showing signs of disengagement
'About to Sleep'
ELSEIF [R] = 1 AND [F]
<= 2 THEN
// Long gone / very cold
'Hibernating'
ELSE
// Everyone else
'Other'
END
We’re now ready to visualize our data
in Tableau…
Uh Oh, a Problem
You mean I’ve taken you all this way
and you’ve created all these calculated fields just for me to tell you there’s
a problem? Yep—sorry about that. While these calculations will work, they
present us with several challenges.
The primary issue is that the
calculations are complex, including both LODs and nested (layered) table calcs.
That always makes me nervous because it means you have to be very careful about
their usage. If, for example, you forget to compute one of those nested table
calcs properly, everything falls apart.
These complex calculations are also
not very performant. Imagine if my data set had millions of rows and tens of
thousands of donors. We will almost certainly see a big performance hit.
The table calcs also limit how we can
use the resulting information. Let’s say, for example, that you wanted to
create a bar chart showing the number of donors in each segment. While this is
typically a simple thing to do, it becomes nearly impossible when the Segment
field is a table calc as that table calc needs all donors in the level of
detail in order to compute properly. To create this one simple chart, you’ll be
forced to try all kinds of hacks and, even then, you may not be able to get it
to work.
The last problem these table calcs
create is the Order of
Operations.
Table calculations are one of the last things to compute. But, as noted above,
every donor needs to be in the level of detail in order for us to compute these
scores. So that means the only filter we can use is table calc filters—no
context, dimension, or measure filters. Again, we’re in hack land if we’re
going to work around this problem.
With all of these issues, I’m of the
opinion that this method simply is not sustainable. The table calculations
create too many limitations which will limit our ability to visualize this data
and do so in a way that is performant. So, what’s the solution?
Push the calculations to the data
layer!
In my opinion, any time you can push
this type of thing to the data layer, the better you’ll be. Instead of complex
calcs, we’ll have simple dimensions and measures we can use in Tableau. And
that will save us so much pain.
Unfortunately, this post is already
really long, so you’re going to have to wait until the second post in this
series to see how we can address this in the data layer. In that post, I’ll
share code I’ve written in Python as well as a Tableau Prep flow that performs
the RFM scoring segmentation. I’ll also share some alternative scoring methods
that I think work better than the 1-5 scoring shared here.
Thanks for reading and please be sure
to come back once part 2 is ready to go. If you have any questions, feel free
to drop them in the comments section below.
Ken Flerlage, February
9, 2026
Twitter | LinkedIn | GitHub | Tableau Public











No comments: