Segmentacja behawioralna klientów z użyciem metody RFM

grupowanie, segmentacja, rfm, projekt, k-means, k-średnich

Segmentacja wcale nie musi być skomplikowana. Najważniejszy jest jej efekt i korzyści jakie przynosi dla firmy. W tym projekcie chciałbym pokazać jak szybko, łatwo i przyjemnie wykonać segmentacje behawioralną z użyciem relatywnie prostych metod. Zapraszam! 🙂


  1. Wstęp
  2. Cel projektu
  3. Założenia dotyczące projektu
  4. Opis zbioru danych
  5. Wczytanie danych
  6. Przygotowanie danych do grupowania
  7. Grupowanie danych
  8. Budowa modelu
  9. Opis uzyskanych wyników
  10. Podsumowanie

1. Wstęp

Niniejszy projekt koncentruje się na segmentacji behawioralnel. Użyta zostanie metoda RFM, z pomocą której klienci zostaną podzieleni na grupy.

Prócz aspektów technicznych pokażę, w jaki sposób można przekuć wyniki analizy na akcje marketingowe mające na celu zwiększenie sprzedaży.

2. Cel projektu

  • Głównym celem projektu jest przeprowadzenie grupowania klientów sklepu online.
  • Celem szczegółowym jest wykonanie analizy zbioru, grupowania (w rozumieniu SQL) transakcji i wyciąganie na ich podstawie wniosków.

3. Założenia dotyczące projektu

  • Postawię się w roli osoby przeprowadzającej projekt grupowania klientów dla klienta (dużego sklepu online sprzedającego unikalne prezenty, którego klientami są głównie hurtownicy).
  • Zakładam przeprowadzenie grupowania klientów z użyciem metody RFM (requency, frequency, monetary value).
  • Będę oceniać klientów kryteriami, jakie zakłada metoda RFM. Najbardziej wartościowi kliencie, to ci, którzy:
    1. Requency - w ostatnim czasie byli zainteresowani produktami, które oferuje nasz sklep,
    2. Frequency - często wykonują zakupy w naszym sklepie.
    3. Monetary value - wydają na zakupy w naszym sklepie relatywnie duże pieniądze.

Każdorazowo punktem odniesienie dla każdego klienta będą inni klienci naszego sklepu. Zakładam ocenę danego czynnika względem pozostałych klientów, dzieląc klientów z użyciem metody kwantyli.

  • Nie oceniam skuteczności grupowania RFM. Jedynie przedstawiam sposób jej przeprowadzenia dla przykładowego zbioru danych i wybranego przypadku.
  • Jako że mowa o grupowaniu, to ciężko jest ocenić jej wpływ na sprzedaż bez przeprowadzenia przykładowej kampanii marketingowej na wybranym segmencie z RFM i grupie kontrolnej, a następnie wykonaniu np. testów A/B dla różnicy w średniej, mając na uwadze zysk z różnych grup.
  • W skrócie: przeprowadzam proces grupowania klientów sklepu online, z użyciem prostej i intuicyjnej metody RFM, bez oceny i badania jej jakości.

4. Opis zbioru danych

Główne informacje o zbiorze:

  • Link do zbioru
  • Autor: Dr Daqing Chen.
  • Dodano w 2015.11.06.
  • 8 zmiennych, 541 909 obserwacji (przed agregacją).

Zbiór zawiera dane dotyczące transakcji w jednej z Brytyjskich firm sprzedających drobne pomysły na prezent i upominki. Działa ona tylko i wyłącznie online, nie posiadając sklepu stacjonarnego (co traktuję jako plus, gdyż wszystkie transakcje pochodzą z tego samego kanału sprzedaży). Wiele klientów firmy to hurtownicy.

Wszystkie transakcje znajdujące się w pliku zostały wykonane pomiędzy 01/12/2010 i 09/12/2011.

5. Wczytanie danych i niezbędnych bibliotek

Cały projekt wykonuję w Pythonie, korzystając z Jupyter-a. Pierwszym krokiem jest więc zatem wczytanie niezbędnych bibliotek.

In [1]:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import scipy.stats
import statsmodels.formula.api as sm
from sklearn.cluster import KMeans
from mpl_toolkits.mplot3d import Axes3D
from sklearn.neighbors import NearestNeighbors
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import auc, roc_curve

Ustawiam formatowanie zmiennych ciągłych w Pandas z precyzją do trzech miejsc po przecinku.

In [2]:
pd.set_option('float_format', '{:.3f}'.format)

Wczytuję teraz zbiór z pliku csv. To, co robię dodatkowo przy wczytywaniu, to parametrami wskazuję Pandas-owi:

  • brak nagłówka w zbiorze,
  • nazwy poszczególnych zmiennych,
  • typy niektórych zmiennych,
  • znak za pomocą którego oznaczone są braki w danych.
In [3]:
ef = pd.ExcelFile('data/Online Retail.xlsx')
print(ef.sheet_names)
['Online Retail']
In [4]:
df = ef.parse('Online Retail')

Mając wczytany zbiór, sprawdzam jego rozmiar i podglądam, jak wyglądają poszczególne obserwacje, by sprawdzić, czy wszystko wczytało się prawidłowo.

In [5]:
print(str(df.shape[0]) + ' wierszy.')
print(str(df.shape[1]) + ' kolumn.')
541909 wierszy.
8 kolumn.
In [6]:
df.head()
Out[6]:
InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID Country
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6 2010-12-01 08:26:00 2.550 17850.000 United Kingdom
1 536365 71053 WHITE METAL LANTERN 6 2010-12-01 08:26:00 3.390 17850.000 United Kingdom
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8 2010-12-01 08:26:00 2.750 17850.000 United Kingdom
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6 2010-12-01 08:26:00 3.390 17850.000 United Kingdom
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6 2010-12-01 08:26:00 3.390 17850.000 United Kingdom

Wszystko wygląda dobrze. Przechodzę zatem dalej.Sprawdzam typy poszczególnych zmiennych zmiennych.

In [7]:
df.dtypes
Out[7]:
InvoiceNo              object
StockCode              object
Description            object
Quantity                int64
InvoiceDate    datetime64[ns]
UnitPrice             float64
CustomerID            float64
Country                object
dtype: object

Z opisu danych, który jest zawarty na stronie UCI wynika, że wszystkie faktury o identyfikatorze zaczynającym się od litery "c" zostały anulowane - "InvoiceNo" nie mógł zatem zostać potraktowany przez Pandas jako liczba całkowita - typ zmiennej "object" jest tu jak najbardziej na miejscu. Docelowo interesować nas będą jedynie faktury, które nie zostały anulowane. Czemu? Otóż w założeniach metody RFM widnieje monetary value, a ciężko mówić o wartości w przypadku anulowanych faktur (btw. ciekaw jestem, jak dobry klasyfikator udałoby się zbudować do przewidywania czy faktura zostanie anulowana). 🙂

Co więcej, mając na uwadze cel, jaki przyświeca większości firm - czysto zarobkowy - bardziej wartościowi są klienci zamawiający i płacący, niż zamawiający i anulujący. 😉 W rozszerzonej wersji grupowania, można by się pokusić o wychwycenie grup klientów najczęściej anulujących, a rzadko płacących.

6. Przygotowanie danych do grupowania

Buduję prosty data frame z podstawowymi informacjami o zbiorze.

In [8]:
summary = pd.DataFrame(df.dtypes, columns=['Dtype'])
summary['Nulls'] = pd.DataFrame(df.isnull().any())
summary['Sum_of_nulls'] = pd.DataFrame(df.isnull().sum())
summary['Per_of_nulls'] = round((df.apply(pd.isnull).mean()*100),2)
summary.Dtype = summary.Dtype.astype(str)
print(summary)
                      Dtype  Nulls  Sum_of_nulls  Per_of_nulls
InvoiceNo            object  False             0         0.000
StockCode            object  False             0         0.000
Description          object   True          1454         0.270
Quantity              int64  False             0         0.000
InvoiceDate  datetime64[ns]  False             0         0.000
UnitPrice           float64  False             0         0.000
CustomerID          float64   True        135080        24.930
Country              object  False             0         0.000

Sprawdzam braki danych.

In [9]:
print(str(round(df.isnull().any(axis=1).sum()/df.shape[0]*100,2))+'% obserwacji zawiera braki w danych.')
24.93% obserwacji zawiera braki w danych.

Pierwsze wnioski wyglądają następująco:

  • Istnieją klienci bez identyfikatora.
  • Typ zmiennej "CustomerID" budzi pewne wątpliwości, ale prawdopodobnie stoją za tym brakujące wartości (int nie przyjmuje pustych wartości i wtedy Pandas automatycznie traktuje je jako float).
  • A więc jedynie dwie zmienne zawierają braki. Jedna to już wspominana "CustomerID", a druga to "Description". Z biznesowego punktu widzenia braki w zmiennej "Description" są uzasadnione - nie wszystkie produkty muszą mieć opis.
  • Aż 135080 spośród 541909 obserwacji nie ma podanej wartości zmiennej "CustomerID". To niemal 25%. Sporo.

Z opisu zmiennej na UCI można przeczytać, że: "Customer number. Nominal, a 5-digit integral number uniquely assigned to each customer." Skąd zatem wynikają braki? Czy byli to po prostu klienci detaliczni, którym nie był nadawany identyfikator? Nie znalazłem na to pytanie odpowiedzi. Mam zatem dwie możliwości:

  1. Potraktować klientów bez identyfikatora jako jednego klienta "detalicznego".
  2. Usunąć wszystkie transakcje bez identyfikatora klienta (pozostanie nieco ponad 400 tys. transakcji).

Nie muszę Cię chyba przekonywać, że mając na uwadze problem, z którym się mierzymy w tym przypadku i potencjalne oczekiwania biznesowe, zdecydowanie rozsądniejsza jest opcja druga. Opcja pierwsza zachwiałaby i nieco wypaczyła proces grupowania. Powstałby jeden klient odpowiadający za 25% wszystkich transakcji. Pozostali nawet najwięksi klienci wyglądaliby przy nim jak szum.

Poza tym grupowanie ma na celu podział zbioru na grupy będące wsadem do dalszych działań, np. marketingowych. Jakie działania marketingowe można przeprowadzić dla klientów, których nie jesteśmy w stanie zidentyfikować? 🙂

Usuwam brakujące wartości w zmiennej "CustomerID".

In [10]:
df = df[~df.CustomerID.isnull()]
print('Pozostało ' + str(df.shape[0]) + ' obserwacji.')
Pozostało 406829 obserwacji.

Usunięcie anulowanych faktur.

In [11]:
df = df[df.InvoiceNo.astype('str').str.isdigit()]
In [12]:
print('Pozostało ' + str(df.shape[0]) + ' obserwacji.')
Pozostało 397924 obserwacji.
In [13]:
df.head()
Out[13]:
InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID Country
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6 2010-12-01 08:26:00 2.550 17850.000 United Kingdom
1 536365 71053 WHITE METAL LANTERN 6 2010-12-01 08:26:00 3.390 17850.000 United Kingdom
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8 2010-12-01 08:26:00 2.750 17850.000 United Kingdom
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6 2010-12-01 08:26:00 3.390 17850.000 United Kingdom
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6 2010-12-01 08:26:00 3.390 17850.000 United Kingdom
In [14]:
df['CustomerID'] = df.CustomerID.astype(int)
In [15]:
df['InvoiceNo'] = df.InvoiceNo.astype(int)
6.1. Usunięcie zbędnych zmiennych

Usuwam zbędne zmienne, z których już nie będę korzystać.

In [16]:
df.drop(['Description', 'StockCode', 'Country'], axis = 1, inplace = True)
6.2. Usunięcie starych operacji

W grupowaniu metodą RFM zazwyczaj przyjmuje się, że brane pod uwagę są jedynie operacje z ostatnich 12 miesięcy. Przyczyna takiego zabiegu jest prosta: starsze operacje mogą niewiele mówić o obecnej sytuacji sprzedającego, kupującego i o samym produkcie. Oczywiście mowa o przypadku ogólnym. Są branże, w których relacja z klientem i produkt ma zdecydowanie dłuższy cykl życia. Okres analizy należy zatem dobrać stosownie do wymagań i charakterystyki rozpatrywanego przypadku.

W analizowanym przykładzie "przeterminowanych" operacji nie ma wiele. By je wyznaczyć, muszę przyjąć umowną datę przeprowadzania grupowania. Dane, na których operuję, są dosyć wiekowe, dlatego za "dziś" przyjmę dzień do najnowszej operacji w zbiorze.

In [17]:
df.shape
Out[17]:
(397924, 5)
In [18]:
today = df.InvoiceDate.max() + pd.to_timedelta(1, "D")
In [19]:
today
Out[19]:
Timestamp('2011-12-10 12:50:00')
In [20]:
df = df[df.InvoiceDate >= today - pd.to_timedelta(12, "M")]

7. Grupowanie danych

Ponownie sprawdzam rozmiar zbioru.

In [21]:
print(df.shape)
(383419, 5)
7.1. Agregacja danych

Dodaję nową zmienną: "MonetaryValue".

In [22]:
df['MonetaryValue'] = df.Quantity * df.UnitPrice
In [23]:
df.MonetaryValue.describe()
Out[23]:
count   383419.000
mean        22.429
std        314.367
min          0.000
25%          4.950
50%         11.900
75%         19.800
max     168469.600
Name: MonetaryValue, dtype: float64

W tym miejscu jestem ciągle na poziomie pojedynczego produktu. By wykonać grupowanie RFM muszę przejść najpierw do agregacji na poziomie faktury, a później do poziomu klienta. Całość mógłbym wykonać w jednym kroku, lecz konieczne jest dodanie jednej dodatkowej zmiennej do zbioru: liczby dni od "dziś" (zmienna "today", która jest niezbędna do policzenia "Recency").

Kolejna zmienna - "Recency", czyli informacja o tym, jak dawno klient robił zakupy w sklepie.

In [24]:
df['Recency'] = (today - df.InvoiceDate)/np.timedelta64(1,'D')

Ok, resztę ("F" jak Frequency, oraz agregowanaie danych do poziomu klienta) wykonam za pomocą metody groupby. 🙂

In [25]:
abt = df.groupby(['CustomerID']).agg({'Recency':'min', 'MonetaryValue':'sum', 'InvoiceNo':'count'})

Jeszcze szybka zamiana nazw zmiennych...

In [26]:
abt.rename(columns = {'InvoiceNo':'Frequency'}, inplace = True)
abt = abt[['Recency', 'Frequency', 'MonetaryValue']]
In [27]:
abt.head()
Out[27]:
Recency Frequency MonetaryValue
CustomerID
12346 326.117 1 77183.600
12347 2.874 151 3598.210
12348 75.984 31 1797.240
12349 19.124 73 1757.550
12350 310.867 17 334.400
7.2. Transformacja zmiennych

Kolejnym krokiem jest wykonanie transformacji wszystkich trzech zmiennych. Metoda RFM wymaga na tym etapie zmiennych porządkowych. Można to wykonać na kilka sposobów, m.in. na podstawie:

  • percentyli rozkładu danej zmiennej,
  • wiedzy ekspercką.

Ja decyduję się na pierwszą opcję. 🙂

Etykiety będą mieć wartości z 1-4. W tym momencie należy zwrócić uwagę na biznesowe znaczenie poszczególnych zmiennych. Nadając etykiety wartościom zmiennych, trzeba pamiętać, że wyższa etykieta powinna oznaczać lepszą sytuację danej osoby/firmy z punktu widzenia sprzedającego. W związku z tym w przypadku "Recency" - im mniejsza wartość zmiennej, tym wyższa etykieta (preferujemy klientów, którzy są aktywni). Odwrotnie wygląda sytuacja w przypadku "Frequency" - im większa wartość zmiennej, tym wyższa etykieta (preferujemy klientów, którzy kupują częściej).

In [28]:
r = pd.qcut(abt.Recency, 4, labels = list(range(0,4)))
In [29]:
f = pd.qcut(abt.Frequency, 4, labels = list(range(0,4)))
In [30]:
m = pd.qcut(abt.MonetaryValue, 4, labels = list(range(0,4)))
In [31]:
abt_cutted = pd.DataFrame({'Recency' : r, 'Frequency' : f, 'MonetaryValue' : m})
In [32]:
abt_raw = abt_cutted.values

8. Budowa modelu.

Przygotowanie funkcji do podsumowania grupowania (by wielokrotnie nie pisać tego samego kodu podczas testów).

In [355]:
def summarize_data():    
    fig = plt.figure(figsize=(8, 6))
    ax = fig.add_subplot(111, projection='3d')
    
    for x in abt.groups.unique():        
        xs = abt[abt.groups == x]['Recency']
        zs = abt[abt.groups == x]['Frequency']
        ys = abt[abt.groups == x]['MonetaryValue']
        ax.scatter(xs, ys, zs, s=50, alpha=0.6, edgecolors='w', label = x)

    ax.set_xlabel('Recency')
    ax.set_zlabel('Frequency')
    ax.set_ylabel('MonetaryValue')
    plt.title('Wizualizacja utworzonych grup')
    plt.legend()
    plt.show()
8.1. Wybór odpowiedniej liczby grup.
In [234]:
res = []
for n in range(2, 20):
    kmeans = KMeans(n_clusters=n)
    kmeans.fit(abt_raw)
    res.append([n, kmeans.inertia_])
res = pd.DataFrame(res, columns = ['liczba_grup', 'inercja'])
In [235]:
res
Out[235]:
liczba_grup inercja
0 2 7573.523
1 3 5742.997
2 4 4335.623
3 5 3798.387
4 6 3293.955
5 7 3009.949
6 8 2553.710
7 9 2372.371
8 10 2149.214
9 11 2063.098
10 12 1862.381
11 13 1741.266
12 14 1652.364
13 15 1541.208
14 16 1432.537
15 17 1308.548
16 18 1283.064
17 19 1178.617
In [332]:
plt.figure(figsize=(10,7))
sns.set(font_scale=1.4, style="whitegrid")
sns.lineplot(data = res, x = 'liczba_grup', y = 'inercja').set(title = "Miara odmienności grup vs liczba grup")
plt.axvline(x = 4, linestyle = '--')
plt.axvline(x = 8, linestyle = '--')
plt.show()

Mam dwa typy: 4 i 8 grup. Zweryfikuję oba, ponieważ jak pisałem w jednym z ostatnich wpisów, to analiza wybranych statystyk jest rzeczą najwazniejszą przy ocenie jakości grupowania.

8.2. Pierwszy model - 8 grup.
In [367]:
model_1 = KMeans(n_clusters=8)
groups = model_1.fit_predict(abt_raw)
abt_cutted['groups'] = groups
abt['groups'] = groups
In [368]:
summarize_data()

Rozkład liczności poszczególnych grup:

In [369]:
print((abt.groups.value_counts(normalize = True, sort = True) * 100).to_string())
2   17.773
6   13.904
0   13.763
4   13.365
7   13.200
3   11.841
5    8.464
1    7.691

Statystyki dla całego zbioru:

In [371]:
abt.agg(['mean'])
Out[371]:
Recency Frequency MonetaryValue groups
mean 88.214 89.899 2016.302 3.504

Statystyki dla poszczególnych grup:

In [372]:
abt.groupby('groups').agg(['mean'])
Out[372]:
Recency Frequency MonetaryValue
mean mean mean
groups
0 20.893 61.939 1353.739
1 109.524 20.012 1608.860
2 205.382 11.033 198.082
3 111.193 101.176 1807.542
4 23.870 21.865 328.288
5 30.327 188.640 3834.839
6 7.756 304.039 7434.284
7 154.639 35.849 415.974

Statystyki wyglądają nieźle. Grupy są różnorodne, ładnie różnicują grupy.

8.3. Drugi model - 4 grupy.
In [373]:
model_1 = KMeans(n_clusters=4)
groups = model_1.fit_predict(abt_raw)
abt['groups'] = groups
In [374]:
summarize_data()

Rozkład liczności poszczególnych grup:

In [375]:
print((abt.groups.value_counts(normalize = True, sort = True) * 100).to_string())
1   31.864
0   26.940
2   23.634
3   17.562

Statystyki dla całego zbioru:

In [376]:
abt.agg(['mean'])
Out[376]:
Recency Frequency MonetaryValue groups
mean 88.214 89.899 2016.302 1.318

Statystyki dla poszczególnych grup:

In [377]:
abt.groupby('groups').agg(['mean'])
Out[377]:
Recency Frequency MonetaryValue
mean mean mean
groups
0 15.496 228.969 5245.274
1 178.873 17.592 311.827
2 97.477 74.098 1646.137
3 22.811 29.019 653.712

Segmentacja dla czterech grup w mojej ocenie wygląda lepiej, dlatego decyduję się na nią.

9. Opis uzyskanych wyników.

9.1. Interpretacja powstałych grup.
  • Grupa 0 - klienci, którzy:
    • robili niedawno zakupy,
    • kupują często,
    • robią zakupy za duże kwoty.
  • Grupa 1 - klienci, którzy:
    • od dłuższego czasu nic nie kupowali,
    • kupują rzadko,
    • robią zakupy za relatywnie niewielkie kwoty.
  • Grupa 2 - klienci, którzy:
    • robili dawno zakupy,
    • kupują umiarkowanie często,
    • robią zakupy za wysokie kwoty.
  • Grupa 3 - klienci, którzy:
    • robili niedawno zakupy,
    • kupują rzadko,
    • robią zakupy za relatywnie niewielkie kwoty.
9.2. Nadanie nazw grupom.
  • Grupa 0 - Klienci VIP.
  • Grupa 1 - Przypadkowi klienci detaliczni - starzy.
  • Grupa 2 - Byli klienci VIP.
  • Grupa 3 - Przypadkowi klienci detaliczni - nowi.
9.3. Przykładowe akcje i wnioski.

Sedno analizy segmentacyjnej, czyli akcje mające się przełożyć na zysk dla firmy. W omawianym przykładzie zdecydowałem się na 4 grupy, ponieważ chciałem, by analiza wyników była dla Ciebie przejrzysta. Dla czterech grup liczba akcji będzie oczywiście znacznie mniejsza niż dla ośmiu grup. Warto o tym pamiętać rozważając liczby już na etapie budowania modelu.

Szalenie istotne jest, by w całym procesie segmentacji behawioralnej zaangażowany był biznes. Mowa tu o dziale sprzedaży, który powinien mieć oko na wyniki analizy. Nieoceniona jest też rola marketingu w całym procesie.

Poniżej przedstawiam przykładowe akcje dla poszczególnych grup uzyskanych w procesie segmentacji:

  • Grupa 0 - Klienci VIP.
    • Dla najlepszych klientów warto pamiętać o "pielęgnacji" relacji. Jeśli prowadzimy biznes oparty o sprzedaż poprzez internet, przykładowymi akcją moga być:
      • specjalne oferty, z rabatami przy odpowiednio dużych zakupach,
      • upominek przy najbliższych zamówieniu,
      • karty klienta VIP uprawniające do benefitów (zbieranie punktów wymienianych na nagrody, etc.).
    • Jeśli prowadzimy biznes oparty o sprzedaż bezpośrednią, przykładowymi akcją mogą być:
      • częstsze wizyty przedstawicieli handlowych u klienta,
      • złożenie specjalnej oferty skierowanej do klientów VIP,
      • zostawienie upominku przy ważnych okazjach.
  • Grupa 1 - Przypadkowi klienci detaliczni - starzy.
    • Zakładając, że zależy nam na zwiększaniu sprzedaży i zysku netto, grupa ta nie wydaje się specjalnie rokująca. Sugerowałbym "obsłużyć" ich w ostatniej kolejności (o ile w ogóle powinni oni zostać obsłużeni - dział marketingu powinien zdecydować, mając na uwadze spodziewaną stopę zwrotu z inwestycji).
  • Grupa 2 - Byli klienci VIP.
    • W tej grupie znajdują się duzi klienci, którzy z jakiegoś powodu przestali kupować nasze produkty (zamknięcie działalności, przejście do konkurencji). Niezależnie od powodu warto spróbować odnowić relacje z nimi poprzez sprawnie przeprowadzona akcję marketingową.
  • Grupa 3 - Przypadkowi klienci detaliczni - nowi.
    • Klienci, którzy wcześniej nie kupowali w naszym sklepie. W ich przypadku warto powalczyć o przekształcenie ich w klientów VIP. Przykładowe akcje:
      • e-mail marketing z ofertami "2+1", przedstawiającymi zalety naszych produktów,
      • upsell, cross-sell, połączony z case study naszych zadowolonych klientów (uproduktawianie klientów połączone ze "społecznym dowodem słuszności" i atrakcyjności produktu).

10. Podsumowanie

Dziękuję Ci za dobrnięcie do końca. Mam nadzieję, że ten projekt przypadł Ci do gustu. Jeśli masz jakiekolwiek uwagi, pytania, lub spostrzeżenia, to zapraszam do dyskusji w komentarzach pod wpisem. 🙂

photo: pixabay.com (MetsikGarden)

Podobał Ci się ten artykuł?

Jeśli tak, to zarejestruj się, by otrzymywać informacje o nowych wpisach. Dodatkowo w prezencie wyślę Ci bezpłatny poradnik 🙂

4 Komentarze

  1. Witam,
    bardzo ciekawy artykuł. Mam pytanie dotyczące przypisywanie etykiet (0-4)
    „Etykiety będą mieć wartości z 1-4. W tym momencie należy zwrócić uwagę na biznesowe znaczenie poszczególnych zmiennych. Nadając etykiety wartościom zmiennych, trzeba pamiętać, że wyższa etykieta powinna oznaczać lepszą sytuację danej osoby/firmy z punktu widzenia sprzedającego. W związku z tym w przypadku „Recency” – im mniejsza wartość zmiennej, tym wyższa etykieta (preferujemy klientów, którzy są aktywni). Odwrotnie wygląda sytuacja w przypadku „Frequency” – im większa wartość zmiennej, tym wyższa etykieta (preferujemy klientów, którzy kupują częściej).”

    W którym miejscu w kodzie określamy, że etykiety dla Recency i Frequency mają być przypisywane wg innych zasad?

    • Cześć Rafał! Dziękuję za komentarz i celną uwagę. 🙂 Bardzo fajnie, że to zaznaczyłeś.

      Nadając etykiety wartościom zmiennych, trzeba pamiętać, że wyższa etykieta powinna oznaczać lepszą sytuację danej osoby/firmy z punktu widzenia sprzedającego.

      Nie zrobiłem tego (dziś już nie pamiętam, czy było to celowe działanie, czy też najzwyczajniej zapomniałem o tym). W klasycznym RFM łączymy wartości kolejnych elementów (konkatenacja, ew. suma) i wtedy ma to kolosalne znaczenie. Gdy używamy RFM w połączeniu z grupowaniem ten zabieg nie jest konieczny. Rozważamy grupy obserwacji podobnych do siebie. Nie musimy wskazywać, że ktoś jest lepszy lub gorszy. Po prostu grupujemy klientów, sprawdzamy statystyki na surowych danych, a następnie do różnych grup mapujemy indywidualne działania marketingowe.

Dodaj komentarz

Twój adres email nie zostanie opublikowany.


*