RFM analysis for WooCommerce customer segmentation

I have watched clients spend thousands on “marketing automation” plugins that mostly bloat the database and slow the checkout down. The question they wanted answered was simple: who are our best customers. They were trying to answer it with behavioral segment math running inside a WordPress install that was never built for heavy data work.

The default answer to customer segmentation has become “install a reporting plugin,” and that answer costs you page speed. The calculation does not have to live in the dashboard at all. RFM Analysis for WooCommerce runs perfectly well in Pandas, on exported data, with the store none the wiser.

Anyone who has cleaned up broken data after a migration or export knows raw numbers on their own do not tell you much. You need something that turns transactions into behavior you can act on.

The RFM framework: recency, frequency, monetary

RFM is the standard approach in retail analytics. Its job is to stop you treating every customer the same way, which is how shops end up discounting to people who were going to buy anyway while quietly losing the regulars who have stopped showing up.

  • Recency (R) counts the days since the customer’s last order, so it tells you whether they are still around.
  • Frequency (F) counts how many separate orders they have placed, which separates one-off buyers from regulars.
  • Monetary (M) is what they have spent in total, and it is the number the business will ask about first.

Building the metrics in Pandas

Rather than running heavy SQL against a live production server, export the orders to CSV or pull them through the WooCommerce REST API into a Pandas dataframe. From there, going from order rows to per-customer metrics is a short bit of aggregation.

import pandas as pd
import datetime as dt

# Assume df is your WooCommerce order export
# snapshot_date is the reference point (e.g., today + 1)
snapshot_date = df['InvoiceDate'].max() + dt.timedelta(days=1)

# Group by CustomerID and aggregate
rfm = df.groupby('CustomerID').agg({
    'InvoiceDate': lambda x: (snapshot_date - x.max()).days,
    'InvoiceNo': 'nunique',
    'Revenue': 'sum'
})

# Clean up column names
rfm.rename(columns={
    'InvoiceDate': 'Recency',
    'InvoiceNo': 'Frequency',
    'Revenue': 'Monetary'
}, inplace=True)

The snapshot date is where people get burned. I have seen devs hard-code a date, or reach for the current timestamp, and then wonder why last month’s numbers will not reproduce. Base it on the latest date in the dataset and the analysis stays repeatable.

The scoring logic: ranking your customers

Raw numbers on their own are hard to read. Is a frequency of 3 good? On a small shop it probably is, and on a high-volume store it is noise. Quantile binning fixes that by ranking customers relative to each other. Most segmentation plugins never do this. They ship static thresholds that stop making sense the moment your order volume changes.

def bbioon_rfm_score(series, ascending=True, n_bins=5):
    # Rank values to ensure uniqueness and avoid duplicate bin edge errors
    ranked = series.rank(method='first', ascending=ascending)
    return pd.qcut(ranked, q=n_bins, labels=range(1, n_bins+1)).astype(int)

# Apply scores
rfm['R_Score'] = bbioon_rfm_score(rfm['Recency'], ascending=False)
rfm['F_Score'] = bbioon_rfm_score(rfm['Frequency'])
rfm['M_Score'] = bbioon_rfm_score(rfm['Monetary'])

Turning the scores into decisions

With the three scores in hand you can map combinations onto segments. A customer scoring 555 is a “Champion.” A customer at 111 is “Lost.” That is already something the marketing team can work with.

The segment worth your attention is “At-Risk”: low recency, but high frequency and monetary scores. These are people who used to buy regularly and then stopped a few months ago. A single win-back email written for them specifically is often enough.

If wiring RFM analysis into a WooCommerce store is eating hours you do not have, that is work I can take on. I have been doing this since the 4.x days.

What I would actually do

WordPress is not an analytics engine and it does not need to be. Exploratory data analysis belongs somewhere built for it, which in practice means Python and Pandas. Run the numbers outside the store and the store stays fast. Then re-run them: scores drift as customers buy or go quiet, so a segment is only as accurate as the last refresh.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.