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:

Powered by Blogger.