Werkzeuge der Segmentierung: k means, Latent Class und mehr als RFM
Kurz gesagt
Niveau: Profi
- RFM sortiert nach Wert zu einem Zeitpunkt. Es enthält keine Bedürfnisse, keinen Kaufanlass und keine Reaktion, deshalb landeten eine Firmenkundin und eine private Schenkende in derselben Zelle 515.
- k means teilt stetige Werte in feste Gruppen, indem es den quadrierten Abstand zu den Mittelpunkten minimiert. Die Latent Class Analyse passt ein Wahrscheinlichkeitsmodell an Ja oder Nein Antworten an und gibt jedem Kunden eine Wahrscheinlichkeit je Klasse.
- Wähl die Zahl der Segmente mit Ellenbogen, Silhouette, kleinster Gruppe und dem Geschäft am Tisch, und bestätig sie mit einem zweiten Verfahren. Im Beispiel stimmen k means und LCA bei 93,7 % der Kunden überein.
- Im Beispiel bringt das kleinste Segment, die Firmenversender mit 7 % der Kundschaft, 41 % des Umsatzes, während RFM 90 % von ihnen als Einmal oder Zweimalkäufer einstufte.
RFM Scoring ist die Segmentierung, die die meisten Shops wirklich betreiben, und es sortiert nach Wert, statt Verhalten zu beschreiben. An Korbwerk, dem erfundenen Shop für Geschenkkörbe in Hamburg aus den letzten beiden Kapiteln, gehe ich k means und Latent Class Analyse durch, mit der Mathematik, lauffähigem Code, der Wahl der Segmentzahl, einem Profil, Prüfungen der Stabilität und einem Zuordnungsskript. Kapitel 14 von 21 der Serie Marketing Analytics.
Die zwei Kundinnen, die RFM für gleich hielt
Im letzten November kamen bei Korbwerk innerhalb einer Stunde zwei Bestellungen herein. Korbwerk ist Lenas erfundener Onlineshop für Geschenkkörbe in Hamburg, zu finden unter korbwerk.example, derselbe Shop wie in den letzten beiden Kapiteln.
Die erste kam von einer Office Managerin in Hamburg: zwölfmal derselbe Korb, zwölf Lieferadressen, ein Firmenname auf der Rechnung. 540 €. Die zweite kam von einer Frau in München, die ihren drei Schwestern je einen Korb schickte, jeder mit eigener Grußkarte. 190 €. Beides waren erste Bestellungen, beide frisch, und beide lagen im obersten Fünftel der Bestellwerte, denn die meisten Bestellungen im Shop sind ein einzelner Korb für rund 40 bis 70 €.
Das RFM Scoring, das Lena als schnellen Gewinn eingerichtet hatte, steckte beide in dieselbe Zelle: Recency 5, Frequency 1, Monetary 5. Die Automatisierung an dieser Zelle schickte beiden im Dezember eine Mail "Willkommen, hier sind 10 % auf deinen nächsten Korb".
Die Office Managerin brauchte keine 10 %. Sie brauchte jemanden, der sie im nächsten Oktober anruft und fragt, wie viele es diesmal sein sollen. Die Frau in München brauchte Mitte April eine Erinnerung, dass bald Muttertag ist. RFM sah zwei Zahlen und ein Verhalten. Es waren zwei Verhaltensweisen, ehrlich gesagt zwei Geschäfte, in einem Shop. In diesem Kapitel geht es um die Werkzeuge, die sie auseinanderhalten.
Korbwerk, die Kundschaft und jede Zahl hier sind erfunden, damit ich jeden Schritt zeigen kann. Die Zahlen stammen aus einer simulierten Kundendatei, und der Code weiter unten gibt genau das aus, was die Tabellen zeigen.
Wo dieses Kapitel in der Serie steht
Das ist Kapitel 14 von 21 meiner Serie Marketing Analytics und das letzte des dritten Teils, der Verfahren für Wechselbeziehungen. Hauptkomponenten und Faktorenanalyse für Marketer hat 18 Verhaltensspalten für 6.200 Kundinnen und Kunden von Korbwerk auf drei Komponentenwerte eingedampft: Volumen, Schenkneigung und Rabattneigung. Segmentierung: Strategie vor Algorithmen hat dafür argumentiert, erst festzulegen, wofür ein Segment da ist, und dann einen Algorithmus anzufassen. Dieses Kapitel lässt die Algorithmen auf diese drei Werte los. Der vierte Teil beginnt mit Den Wert von Marketingkommunikation modellieren. Jede Grundidee, die hier vorkommt, baut die Seite Grundlagen von Grund auf auf.
Das Strategiekapitel arbeitete mit zwei Jahren Bestellungen und 12.400 Kunden. Hier enthält die Datei, wie in Kapitel 12, die 6.200 Kunden mit einer Bestellung in den letzten 14 Monaten. Die Zahlen sind deshalb kleiner, und die Anteile weichen ab.
Was eine gute Segmentierung leisten muss
Vier Dinge, und eine Segmentierung, die an einem davon scheitert, ist gescheitert.
Trennung. Die Segmente liegen bei dem, was Verhalten treibt, weit auseinander. Zwei Segmente, die sich nur ein wenig unterscheiden, bekommen dasselbe Marketing. Sie sind also ein Segment mit einem Strich mittendurch.
Geschlossenheit. Die Kunden innerhalb eines Segments ähneln sich so stark, dass eine Botschaft, ein Angebot und eine Landingpage für die meisten von ihnen funktionieren.
Verständlichkeit. Wer das Mailprogramm betreut, kann jedes Segment in einem Satz beschreiben, ohne Tabelle. Braucht der Satz die Worte "Komponente zwei", fang noch einmal an.
Umsetzbarkeit. Jedes Segment braucht mindestens eine Sache, die du dafür anders machen würdest: Kanal, Angebot, Zeitpunkt oder Produkt. Sonst ist es eine statistische Kuriosität.
Die ersten beiden lassen sich messen, und die Silhouette weiter unten misst beide. Die letzten beiden sind Urteilssache, und genau dort sterben die meisten Segmentierungen leise: wunderschön getrennte Cluster, mit denen niemand etwas anfangen kann.
Ein Beispiel: Die RFM Zelle 515 besteht die ersten beiden Prüfungen auf dem Papier, denn alle darin haben dieselben drei Werte. An der dritten und vierten scheitert sie. Von den 49 Kunden bei Korbwerk mit 515 sind 40 Firmenkunden und neun private Schenkende, und keine einzelne Maßnahme passt zu beiden.
Der Werkzeugkasten in klaren Worten
Hierarchisches Clustering beginnt mit jedem Kunden als eigenem Cluster und verschmilzt immer wieder das nächstgelegene Paar, bis ein einziger Cluster übrig ist. Den entstehenden Baum, das Dendrogramm, schneidest du dort, wo er die gewünschte Zahl an Gruppen ergibt. Die Ward Verknüpfung verschmilzt das Paar, das die Varianz innerhalb der Cluster am wenigsten erhöht. Sie liefert kompakte Gruppen und ist meine Voreinstellung.1 Der Preis: Jeder Schritt prüft alle möglichen Verschmelzungen,1 in Ordnung bei 6.000 Kunden und nicht machbar bei 6 Millionen.
k means wählt Mittelpunkte, ordnet jeden Kunden dem nächsten Mittelpunkt zu, schiebt jeden Mittelpunkt auf den Mittelwert seiner Mitglieder und wiederholt das, bis sich nichts mehr ändert.1 Das Verfahren ist schnell, es skaliert, und es ist das, was die meisten meinen, wenn sie "Clustering" sagen. Es hat auch feste Vorstellungen: numerische Variablen auf vergleichbaren Skalen, ungefähr runde Gruppen mit ähnlicher Streuung,1 und es findet Cluster, ob die Daten welche enthalten oder nicht.
Latent Class Analyse (LCA) nimmt an, dass die Kundschaft eine Mischung aus wenigen nicht beobachteten Klassen ist, jede mit eigener Wahrscheinlichkeit für jede Antwort, und dass die Variablen innerhalb einer Klasse unabhängig sind.2 Zugehörigkeit ist eine Wahrscheinlichkeit: Eine Kundin gehört zu 92 % zur einen Klasse und zu 8 % zu einer anderen. Ja oder Nein Antworten und Kategorien verarbeitet die LCA von Haus aus, k means kann das nicht.
Gaußsche Mischmodelle tun dasselbe für stetige Variablen: Ellipsen statt Kreisen und eine Wahrscheinlichkeit der Zugehörigkeit statt eines festen Etiketts.3
Entscheidungsbäume sind das Werkzeug fürs Profil, kein Verfahren zur Segmentierung. Ein flacher Baum, der das Segment aus wenigen einfachen Variablen vorhersagt, liefert die kürzeste Beschreibung jeder Gruppe, in Regeln, die du in ein Mailtool tippen kannst.
Ein Beispiel: Die drei Komponentenwerte von Korbwerk sind stetig und bereits standardisiert, das ist Gebiet von k means. Die rohen Merkmale, etwa "Grußkarte bei den meisten Bestellungen" oder "Firma auf der Rechnung", sind Ja oder Nein Antworten, das ist Gebiet der LCA. Dieses Kapitel lässt beide laufen und vergleicht sie.
Der RFM Wert sortiert, er beschreibt nicht
RFM fasst die Vergangenheit eines Kunden in drei Zahlen zusammen: Recency (wie lange die letzte Bestellung her ist), Frequency (wie viele Bestellungen) und Monetary Value (der durchschnittliche Betrag pro Bestellung).4 Lenas Variante teilt jeden Kunden bei allen drei in Fünftel ein und schreibt die drei Werte nebeneinander:
Dabei ist der dreistellige Code für Kunde , der Recency Wert von 1 bis 5, mit 5 für das jüngste Fünftel, der Frequency Wert und der Monetary Wert, jeweils von 1 für das unterste bis 5 für das oberste Fünftel. 555 ist die beste Zelle, wer auf 111 steht, ist wahrscheinlich weg. In Fünftel einteilen heißt sortieren und schneiden, wie beim Median, siehe Grundlagen: Median und Modus.
Rechenbeispiel: zwei Kundinnen, eine Zelle
In der simulierten Datei hat das jüngste Fünftel der Kundschaft innerhalb der letzten 46 Tage bestellt. Beide Kundinnen, mit 12 Tagen, bekommen also .
Die Frequenz ist klumpig. 34 % der Kunden haben in den 14 Monaten genau eine Bestellung, mehr als ein Fünftel, also teilen sie sich alle den niedrigsten Wert. Beide Kundinnen haben eine Bestellung: .
Das oberste Fünftel nach durchschnittlichem Bestellwert beginnt bei 69,08 €. 540 € und 190 € liegen beide darüber: .
Also ist für beide. Im Code, mit den Kunden 1 und 2 als den beiden vom Anfang:
import numpy as np, pandas as pd
c = pd.read_csv("hamper_customers.csv").set_index("customer_id")
def fifth(s): # 1 für das unterste Fünftel, 5 für das oberste, Gleichstände teilen sich einen Wert
return np.ceil(5 * s.rank(method="min", pct=True)).astype(int)
c["R"] = fifth(-c.days_since_last) # weniger Tage seit der letzten Bestellung ergeben einen höheren Wert
c["F"] = fifth(c.orders)
c["M"] = fifth(c.avg_order_value)
c["RFM"] = 100 * c.R + 10 * c.F + c.M
print(c.loc[[1, 2], ["days_since_last", "orders", "avg_order_value", "RFM"]])
Er gibt zweimal 515 aus. Der Code sagt "neue Kundin mit hohem Wert". Er kann nicht sagen, dass die eine eine Firma ist, die für zwölf Büros kauft, und die andere eine Schwester, die für drei Schwestern kauft, denn keine der beiden Tatsachen steckt in den drei Zahlen.
Wie k means funktioniert
k means misst, wie weit ein Kunde von einem Mittelpunkt entfernt ist, mit dem gewöhnlichen geraden Abstand, nur in mehr Dimensionen:
Dabei ist der Abstand zwischen Kunde und dem Mittelpunkt von Cluster , die Zeile mit den Werten des Kunden, die Zeile des Mittelpunkts, die Zahl der Variablen (drei bei Korbwerk), der Wert des Kunden bei Variable und der Wert des Mittelpunkts dort. Quadrierte Differenzen sind der Rohstoff der Varianz, siehe Grundlagen: Streuung, und eine Zahlenzeile ist ein Vektor, siehe Grundlagen: Matrizen.
Der Algorithmus minimiert die gesamte quadrierte Entfernung zwischen jedem Kunden und dem Mittelpunkt seines eigenen Clusters:1
Dabei ist die Quadratsumme innerhalb der Cluster, kurz WCSS (nach dem englischen within cluster sum of squares), die Zahl der Cluster, die Menge der Kunden in Cluster , heißt "jeder Kunde in Cluster ", der Mittelpunkt von Cluster , also der Mittelwert seiner Mitglieder, und der quadrierte Abstand aus der Formel oben. Es ist dieselbe Summe quadrierter Abweichungen wie bei der Varianz, nur um den eigenen Mittelwert jedes Clusters, siehe Grundlagen: der Mittelwert und Grundlagen: Streuung.
Der Algorithmus garantiert nur ein lokales Minimum, und das hängt davon ab, wo die Mittelpunkte starten. Deshalb lässt du ihn von vielen Startpunkten laufen und behältst das beste Ergebnis.1
Rechenbeispiel: sechs Kunden von Hand
Sechs Kunden mit je zwei Werten, geschrieben als (Schenkneigung | Volumen): P1 (2 | minus 1), P2 (3 | minus 1), P3 (2 | 0), P4 (minus 1 | 2), P5 (0 | 2), P6 (minus 1 | 1). Zwei Cluster, und ein absichtlich schlechter Start: Die Mittelpunkte liegen auf P1 und P3.
- Zuordnen. P1 und P2 liegen am nächsten am ersten Mittelpunkt (Abstände 0 und 1, gegen 1 und 1,41). P3 bis P6 liegen am nächsten am zweiten. P4 zum Beispiel ist vom ersten entfernt und vom zweiten.
- Verschieben. Der erste Mittelpunkt wandert zum Mittelwert von P1 und P2, (2,5 | minus 1). Der zweite wandert zum Mittelwert von P3 bis P6, (0 | 1,25). Mit diesen Gruppen ist .
- Wieder zuordnen. P3 ist jetzt 1,12 vom ersten Mittelpunkt entfernt und 2,36 vom zweiten, also wechselt es. Alle anderen bleiben.
- Verschieben. Die Mittelpunkte wandern nach (2,33 | minus 0,67) und (minus 0,67 | 1,67). Die quadrierten Abstände jedes Clusters summieren sich auf 1,33, also ist .
- Wieder zuordnen. Niemand wechselt, also hält der Algorithmus an.
Ein schlechter Start hat hier eine Runde extra gekostet. Auf echten Daten kann ein schlechter Start in einem ganz schlechteren lokalen Minimum enden, deshalb nutzt der Lauf für Korbwerk weiter unten 50 Starts.
k wählen: Ellenbogen und Silhouette
Für die Wahl von stütze ich mich auf zwei Maße. Der Ellenbogen trägt für jedes auf und sucht die Stelle, ab der ein weiterer Cluster nicht mehr viel bringt. Die Silhouette vergleicht für jeden Kunden, wie nah er an seinem eigenen Cluster liegt und wie nah am nächsten anderen:5
Dabei ist die Silhouette von Kunde , der mittlere Abstand von Kunde zu den anderen Mitgliedern seines eigenen Clusters, der mittlere Abstand zu den Mitgliedern des nächstgelegenen anderen Clusters und der größere der beiden, der zwischen minus 1 und 1 hält. Nahe 1 heißt gut platziert, nahe 0 heißt auf einer Grenze, unter 0 heißt wahrscheinlich im falschen Cluster. Der Mittelwert von über alle Kunden ergibt eine Zahl pro , ein gewöhnlicher Durchschnitt, siehe Grundlagen: der Mittelwert und Grundlagen: Verhältnisse und Raten.
Ein Beispiel: Nimm P3 in den beiden fertigen Clustern oben. Seine Abstände zu P1 und P2 sind 1 und 1,41, also ist . Seine Abstände zu P4, P5 und P6 sind 3,61, 2,83 und 3,16, also ist . Dann ist . P3 sitzt klar im richtigen Cluster, wenn auch weniger fest als P1, dessen Silhouette 0,74 beträgt.
Rechenbeispiel: k means auf den drei Komponenten von Korbwerk
Die Eingabe sind die drei Komponentenwerte aus Kapitel 12 für alle 6.200 Kunden. Sie sind bereits standardisiert und unkorreliert, damit ist die erste Falle von k means, ungleiche Skalen, schon aus dem Weg. Der Lauf nutzt scikit learn,1 mit von 1 bis 8 und je 50 zufälligen Starts:
import numpy as np, pandas as pd
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
Z = pd.read_csv("hamper_components.csv").set_index("customer_id") # Volumen, Schenkneigung, Rabattneigung
for k in range(1, 9):
km = KMeans(n_clusters=k, n_init=50, random_state=1).fit(Z)
sil = silhouette_score(Z, km.labels_) if k > 1 else float("nan")
print(k, round(km.inertia_), round(sil, 2), np.bincount(km.labels_).min())
Er gibt für jedes die WCSS, die mittlere Silhouette und die Größe des kleinsten Clusters aus:
| k | WCSS | Mittlere Silhouette | Kleinster Cluster |
|---|---|---|---|
| 1 | 18.590 | keine | 6.200 |
| 2 | 13.646 | 0,34 | 1.263 |
| 3 | 9.025 | 0,39 | 1.254 |
| 4 | 5.681 | 0,45 | 447 |
| 5 | 4.770 | 0,38 | 431 |
| 6 | 4.066 | 0,36 | 431 |
| 7 | 3.547 | 0,34 | 431 |
| 8 | 3.279 | 0,31 | 425 |
Die waagrechte Achse ist die Zahl der Cluster, die senkrechte die Quadratsumme innerhalb der Cluster. Jeder der ersten drei zusätzlichen Cluster nimmt zwischen 3.300 und 5.000 weg. Der fünfte nur noch 911, und jeder weitere weniger. Der Ellenbogen liegt bei vier.
Silhouette und Größen erzählen dieselbe Geschichte. Zwei Cluster trennen die regelmäßigen Eigenkäufer von allen anderen, richtig und wenig hilfreich. Drei fügen eine rabattgetriebene Gruppe hinzu, lassen die Firmenkunden aber im Cluster der Schenkenden und dehnen ihn. Vier trennen die Firmenkunden ab, die Silhouette erreicht ihr Maximum bei 0,45, und der kleinste Cluster hat 447 Kunden, 7 %, klein, aber echt. Fünf schneiden die Schenkenden allein nach Rabattneigung in zwei, und die Silhouette fällt. Es bleibt bei vier, und das passt zu dem, was laut Strategiekapitel das Geschäft umsetzen kann.
Das Profil
Die Mittelpunkte in Standardeinheiten, wobei 0 der durchschnittliche Kunde ist und 1 eine Standardabweichung darüber:
| Segment | Volumen | Schenkneigung | Rabattneigung |
|---|---|---|---|
| A Anlassschenker | minus 0,42 | 0,12 | minus 0,82 |
| B Firmenversender | 0,59 | 2,86 | minus 0,13 |
| C Selbstgenießer | 1,58 | minus 0,82 | minus 0,08 |
| D Gelegenheitssparer | minus 0,58 | minus 0,26 | 1,03 |
Mittelpunkte in Standardeinheiten sind etwas für die Analyse. Das Geschäft braucht die Rohspalten, gemittelt pro Segment, aus der Bestellhistorie, aus der die Komponenten gebaut wurden:
import joblib
km = KMeans(n_clusters=4, n_init=50, random_state=1).fit(Z)
c = pd.read_csv("hamper_customers.csv").set_index("customer_id")
c["cluster"] = km.labels_
g = c.groupby("cluster")
profile = pd.DataFrame({
"customers": g.size(),
"orders_per_year": g.orders.mean() * 12 / 14, # das Zeitfenster umfasst 14 Monate
"average_order": g.spend.sum() / g.orders.sum(),
"gift_message": g.gift_message_share.mean(),
"company_invoice": g.company_on_invoice.mean(),
"voucher": g.voucher_share.mean(),
"december": g.december_share.mean(),
"revenue_share": g.spend.sum() / c.spend.sum(),
})
print(profile.round(2))
joblib.dump(km, "kmeans_k4.pkl") # das Zuordnungsskript nutzt das wieder
| Segment | Kunden | Anteil | Bestellungen pro Jahr | Durchschnittliche Bestellung | Grußkarte | Firma auf der Rechnung | Gutschein genutzt | Dezember | Umsatzanteil |
|---|---|---|---|---|---|---|---|---|---|
| A Anlassschenker | 2.424 | 39 % | 2,4 | 63 € | 88 % | 1 % | 24 % | 45 % | 24 % |
| B Firmenversender | 447 | 7 % | 1,3 | 1.042 € | 87 % | 91 % | 12 % | 79 % | 41 % |
| C Selbstgenießer | 1.241 | 20 % | 6,9 | 50 € | 9 % | 1 % | 31 % | 18 % | 28 % |
| D Gelegenheitssparer | 2.088 | 34 % | 1,3 | 38 € | 57 % | 0 % | 74 % | 35 % | 7 % |
Grußkarte, Gutschein genutzt und Dezember sind der durchschnittliche Anteil an den Bestellungen jedes Kunden, Firma auf der Rechnung ist der Anteil der Kunden.
Segment A ist der Schwerpunkt des Shops: zwei Fünftel der Kundschaft, eine Grußkarte bei fast jeder Bestellung, zwei oder drei Anlässe im Jahr, wenige Gutscheine. Diese Leute kaufen nach Datum, nicht nach Preis. Ihr Mittelpunkt bei der Schenkneigung liegt nur bei 0,12, weil die Firmenversender diese Achse dehnen, ein Grund mehr, das Rohprofil zu lesen und nicht nur die Mittelpunkte.
Segment B ist 7 % der Kundschaft und 41 % des Umsatzes: 698 Bestellungen zu durchschnittlich 1.042 € ergeben 726.992 € von den 1.770.862 € der Datei. RFM gab 90 % von ihnen einen Frequency Wert von 1 oder 2, weil sie einmal im Jahr bestellen. In einer Rangfolge nach Häufigkeit sehen sie aus wie Gelegenheitskäufer. Es sind die wertvollsten Beziehungen, die der Shop hat.
Segment C verschickt fast nie ein Geschenk. Diese Kunden bestellen fast siebenmal im Jahr für sich selbst, das natürliche Publikum für ein Korbabo. Ihre Bestellungen verteilen sich übers Jahr: Sie warten nicht auf den Ausverkauf.
Segment D ist die Gruppe, für die die Rabattautomatisierung gebaut wurde: ein Drittel der Kundschaft, 7 % des Umsatzes, etwa eine Bestellung im Jahr und ein Gutschein bei drei von vier Bestellungen. Mehr als die Hälfte ihrer Bestellungen trägt trotzdem eine Grußkarte. Der Name "Gelegenheitssparer" ist eine Hypothese, mehr dazu bei den Fallstricken.
Die waagrechte Achse zeigt die vier Segmente, die senkrechte den Anteil jedes Segments am Umsatz der 14 Monate. Das kleinste Segment nach Köpfen trägt den größten Anteil, und das größte Publikum für Rabatte den kleinsten.
Zurück zu den beiden Kundinnen vom Anfang. Die Office Managerin in Hamburg landet in B. Die Frau in München landet in A. RFM sah zweimal 515. Die Segmentierung sieht einen Firmenkunden, den man im Oktober anruft, und eine Anlassschenkerin, die man im April erinnert. Keine der beiden hätte 10 % bekommen sollen.
Latent Class Analyse: Zugehörigkeit als Wahrscheinlichkeit
Die LCA passt ein Modell an und maximiert seine Likelihood. In Worten: Die Wahrscheinlichkeit für das Antwortmuster eines Kunden ist die Summe über alle Klassen aus der Wahrscheinlichkeit, in dieser Klasse zu sein, mal der Wahrscheinlichkeit dieses Musters innerhalb der Klasse. Innerhalb einer Klasse gelten die Variablen als unabhängig, also ist diese zweite Wahrscheinlichkeit ein schlichtes Produkt:2
Dabei ist die Likelihood aller Parameter zusammen, die Zahl der Kunden, die Zahl der Klassen, der Anteil der Kunden in Klasse , die Zahl der beobachteten Variablen, die Antwort von Kunde bei Variable und die Wahrscheinlichkeit, dass ein Mitglied von Klasse diese Antwort gibt. Multiplizieren für "und" und Addieren für "entweder oder" sind die Grundregeln der Wahrscheinlichkeit, siehe Grundlagen: Wahrscheinlichkeit. In der Praxis maximiert die Software den Logarithmus von , der das lange Produkt in eine Summe verwandelt, siehe Grundlagen: Logarithmen.
Nach der Anpassung gibt die Bayes Regel jedem Kunden eine A posteriori Wahrscheinlichkeit für jede Klasse:2
Dabei ist die Wahrscheinlichkeit, dass Kunde bei seinen Antworten zu Klasse gehört. Der Zähler ist der Anteil von Klasse mal der Wahrscheinlichkeit dieser Antworten in ihr, der Nenner addiert dieselbe Größe über jede Klasse , damit sich die A posteriori Wahrscheinlichkeiten eines Kunden zu 1 summieren. Es ist die Bayes Regel genau wie in Grundlagen: Wahrscheinlichkeit.
Diese A posteriori Wahrscheinlichkeit ist der praktische Unterschied zu k means. k means sagt "du bist in Cluster D". Die LCA sagt "du bist zu 0,92 D und zu 0,08 A", und die 0,08 sind eine Information, mit der du arbeiten kannst.
Rechenbeispiel: eine A posteriori Wahrscheinlichkeit von Hand
Zwei Klassen und zwei Ja oder Nein Antworten, mit Zahlen zur Veranschaulichung. Klasse A, die Anlassschenker, umfasst 55 % der Kunden: 95 % von ihnen verschicken meist Geschenke, 5 % nutzen bei den meisten Bestellungen einen Gutschein. Klasse D umfasst 45 %: 70 % verschicken meist Geschenke, 95 % nutzen bei den meisten Bestellungen einen Gutschein. Ein Kunde antwortet zweimal mit Ja.
- Klasse A: .
- Klasse D: .
- Die Summe ist .
- A posteriori: und .
Wer verschenkt und Gutscheine nutzt, ist sehr wahrscheinlich ein Gelegenheitssparer, aber einer von zwölf Kunden mit diesem Muster gehört zu den Schenkenden. Das sind die "0,92 D und 0,08 A" von oben.
Die Zahl der Klassen mit dem BIC wählen
Mehr Klassen erhöhen die Likelihood immer, also braucht die LCA eine Strafe für Komplexität. Das bayessche Informationskriterium, das BIC, liefert sie, und das Modell mit dem niedrigsten BIC gewinnt:26
Dabei ist der natürliche Logarithmus der maximierten Likelihood, die Zahl der freien Parameter (die Klassenanteile plus jede Antwortwahrscheinlichkeit je Klasse) und die Zahl der Kunden. Der erste Term belohnt die Anpassung, der zweite berechnet jeden Parameter, und zwar umso teurer, je größer die Datei. Den Logarithmus erklärt Grundlagen: Logarithmen.
Ein Beispiel mit dem Modell mit vier Klassen von unten: Die Log Likelihood ist minus 17.632,67, es gibt 31 freie Parameter und 6.200 Kunden. Also ist .
Rechenbeispiel: Latent Class Analyse als zweite Meinung
Um zu prüfen, dass k means keine ordentlichen Kreise auf Rauschen gezeichnet hat, passe ich dieselben 6.200 Kunden noch einmal mit der LCA an, auf kategorialen Fassungen der Rohspalten: überwiegend Geschenkbestellungen, Firma auf der Rechnung, Gutschein bei den meisten Bestellungen, überwiegend Bestellungen im Dezember, drei oder mehr Bestellungen (jeweils Ja oder Nein) und die durchschnittliche Bestellung in drei Bändern. "An eine andere Adresse geliefert" bleibt draußen, weil es fast dasselbe sagt wie die Grußkarte und die Annahme der Unabhängigkeit brechen würde.
Wer mit R arbeitet, hat das Paket poLCA.2 In Python ist die Schleife für Expectation Maximization kurz genug, um sie auszuschreiben, und sie zeigt die beiden Formeln oben bei der Arbeit:
c = pd.read_csv("hamper_customers.csv").set_index("customer_id")
items = pd.DataFrame({
"gift": c.gift_message_share >= 0.5,
"company": c.company_on_invoice == 1,
"voucher": c.voucher_share >= 0.5,
"december": c.december_share >= 0.5,
"repeat": c.orders >= 3,
"basket": np.digitize(c.avg_order_value, [50, 250]), # unter 50 €, 50 bis 250 €, über 250 €
}).astype(int)
H = [np.eye(items[v].max() + 1)[items[v]] for v in items] # eine Spalte pro Antwortstufe
def lca(H, K, starts=10, iters=200, seed=0):
rng = np.random.default_rng(seed)
best = None
for _ in range(starts):
pi = np.full(K, 1 / K)
theta = [rng.dirichlet(np.ones(h.shape[1]), K) for h in H] # P(Antwort | Klasse)
for _ in range(iters):
logp = np.log(pi) + sum(h @ np.log(t).T for h, t in zip(H, theta))
ll = np.logaddexp.reduce(logp, axis=1) # Logarithmus der Summe über die Klassen
post = np.exp(logp - ll[:, None]) # Bayes: A posteriori je Klasse
pi = post.mean(axis=0)
theta = [np.clip(post.T @ h / post.sum(axis=0)[:, None], 1e-9, 1) for h in H]
if best is None or ll.sum() > best[0]:
best = (ll.sum(), pi, theta, post)
return best
n, fits = len(items), {}
for K in range(2, 7):
ll, pi, theta, post = fits[K] = lca(H, K)
p = (K - 1) + K * sum(h.shape[1] - 1 for h in H) # freie Parameter
print(K, round(-ll), p, round(-2 * ll + p * np.log(n)), np.bincount(post.argmax(1), minlength=K).min())
Wie k means findet auch diese Schleife nur ein lokales Maximum, also startet sie zehnmal und behält das beste Ergebnis.2 Sie gibt aus:
| Klassen | Negative Log Likelihood | Parameter | BIC | Kleinste Klasse |
|---|---|---|---|---|
| 2 | 20.754 | 15 | 41.640 | 2.108 |
| 3 | 18.701 | 23 | 37.603 | 430 |
| 4 | 17.633 | 31 | 35.536 | 430 |
| 5 | 17.628 | 39 | 35.597 | 430 |
| 6 | 17.628 | 47 | 35.666 | 224 |
Das BIC erreicht seinen Tiefpunkt bei vier Klassen. Eine fünfte Klasse kauft fünf Punkte Log Likelihood und bezahlt dafür 61 Punkte BIC.
Dann der Vergleich. Die Klassennummern der LCA sind willkürlich, also werden die Klassen den Clustern von k means nach ihrem Inhalt zugeordnet, nicht nach ihrer Nummer:
from scipy.optimize import linear_sum_assignment
post = fits[4][3]
table = pd.crosstab(km.labels_, post.argmax(1))
rows, cols = linear_sum_assignment(-table.values) # beste Zuordnung eins zu eins
print(table, round(table.values[rows, cols].sum() / n, 3))
93,7 % der Kunden, 5.808 von 6.200, landen in der passenden Klasse. Von den 392, die wechseln, wechseln 284 zwischen A und D: Anlassschenker, die Gutscheine genutzt haben, oder Gelegenheitssparer, die Grußkarten geschrieben haben. Die A posteriori Wahrscheinlichkeiten sind sich einig. Bei 93,6 % der Kunden liegt die größte über 0,9, bei 4,6 % zwischen 0,7 und 0,9, und bei 111 Kunden, 1,8 %, bei 0,7 oder darunter. 84 dieser 111 liegen zwischen A und D. Diese Grenze ist echt. k means versteckt sie nur, indem es einen harten Strich hindurchzieht.
Welches Werkzeug für welche Aufgabe
Der Ablauf liest sich von oben nach unten: zuerst der Zweck, dann entscheiden die Eingaben über das Verfahren. Meine Regel: k means, wenn die Eingaben stetig sind und schon zu Komponenten bereinigt, LCA, wenn sie überwiegend Ja oder Nein Antworten, Umfragefragen oder Kategorien sind, ein Gaußsches Mischmodell für weiche Zugehörigkeit bei stetigen Daten. Was du auch wählst, lass eins der anderen als Prüfung laufen. Eine Übereinstimmung über 75 bis 80 % zwischen zwei Verfahren ist der stärkste Beleg, den ich kenne, dass die Segmente in den Daten leben und nicht im Algorithmus. Die 93,7 % von Korbwerk liegen bequem darüber.
Was RFM nicht sieht und Segmente nach Verhalten schon
RFM ist eine gute Rangfolge des Werts zu einem Zeitpunkt, das richtige Werkzeug für "wen spreche ich zuerst an", wenn das Budget für eine einzige Aussendung reicht. Als Segmentierung scheitert es aus drei Gründen, die in seiner Definition liegen.
Es enthält keine Bedürfnisse. Recency, Frequency und Monetary Value sagen, was passiert ist, nicht warum. Hinter identischen Werten können eine Hochzeit, ein Trauerfall, eine Vertragsverlängerung oder ein ganz normaler Dienstag stecken.
Es enthält keinen Kaufanlass. Wer verschenkt, kauft, weil ein Datum näher rückt, eine Firma, weil ein Budgetjahr endet, wer für sich kauft, weil der Kühlschrank leer ist. Ein Frequency Wert macht alle drei Kalender platt.
Es enthält keine Reaktion. Was hat jede Gruppe getan, als der Shop zuletzt einen Rabatt, eine Produktgeschichte oder eine Erinnerung verschickt hat? Mit einem Segmentfeld am Kundendatensatz wird das zu einer einfachen Abfrage.
Ein Beispiel, mit Zahlen, die Annahmen dieses Beispiels sind und keine Ergebnisse der Simulation: D klickt Rabattmails dreimal so oft wie A, A klickt die Mail "In drei Wochen ist Muttertag" häufiger als alle anderen, und B öffnet überhaupt keine Marketingmails, während ein Anruf im Oktober bei rund 60 % von ihnen eine Nachbestellung bringt. Nichts davon lässt sich messen, solange das Segment nicht existiert.
Segmente nach Verhalten liefern dir den Auslöser, das Bedürfnis und die Reaktion, und genau das brauchst du, um verschiedenen Menschen verschiedenes Marketing zu schreiben, der Kern von Segmentierung ist keine Demografie.
So läuft es in der Praxis
Daten. Eine Zeile pro Kunde aus 12 bis 24 Monaten Bestellungen: gekaufte Kategorien, ob Liefer und Rechnungsadresse abweichen (nicht die Adressen selbst), Grußkarten, Gutscheine, das Firmenfeld und, falls vorhanden, das Verhalten bei Mails. In einem Solidus Shop liegt all das in den Tabellen spree_orders, spree_line_items, spree_addresses und spree_adjustments.7 Ein paar Hundert Kunden sind zu wenige, damit k means etwas bedeutet. Ich würde ab etwa tausend anfangen und dem Ergebnis ab etwa fünftausend trauen.
Schritte. Die Tabelle mit 10 bis 20 Verhaltensvariablen bauen. Standardisieren oder auf Komponenten verdichten. k means für von 2 bis 8 mit vielen Starts laufen lassen. LCA oder ein Gaußsches Mischmodell als zweite Meinung anpassen. wählen, mit dem Geschäft am Tisch. Profil erstellen, benennen, das Zuordnungsskript schreiben und das Segmentfeld in den Shop und ins Mailtool laden.
Prüfungen, bevor du dem Ergebnis traust. k means mit 20 verschiedenen Seeds neu anpassen und jedes Ergebnis mit dem ursprünglichen über den adjustierten Rand Index vergleichen, der bei identischen Gruppierungen 1 ist und bei zufälliger Übereinstimmung nahe 0.8 Unter 0,9 ist die Lösung instabil, und ich würde sie nicht ausliefern. Dann der Bootstrap: Kunden mit Zurücklegen neu ziehen, neu anpassen, alle zuordnen, vergleichen.9 Ich will einen Median des adjustierten Rand Index über 0,7.
from sklearn.metrics import adjusted_rand_score
ref = km.labels_
seeds = [adjusted_rand_score(ref, KMeans(4, n_init=10, random_state=s).fit(Z).labels_) for s in range(20)]
rng = np.random.default_rng(0)
boot = []
for b in range(100): # Kunden neu ziehen, neu anpassen, alle zuordnen
sample = Z.iloc[rng.integers(0, len(Z), len(Z))]
boot.append(adjusted_rand_score(ref, KMeans(4, n_init=10, random_state=b).fit(sample).predict(Z)))
print(round(min(seeds), 3), round(np.median(boot), 3), round(np.percentile(boot, 5), 3))
Er gibt 0.999 0.991 0.978 aus: Der schlechteste der 20 Seeds stimmt fast perfekt überein, und selbst die schwächsten 5 % der Bootstrap Läufe bleiben nahe 0,98. Zwei weitere Prüfungen brauchen Zeit statt Code. Eine zeitliche Holdout Prüfung: auf den ersten zehn Monaten anpassen, die letzten vier zuordnen und schauen, ob sich das Profil noch genauso liest. Vorhersagekraft: Sagt die Zugehörigkeit etwas voraus, das das Modell nie gesehen hat, etwa die Reaktion auf die Kampagne im letzten Frühjahr? Segmente, die außerhalb ihrer eigenen Eingaben nichts vorhersagen, sind eine Beschreibung, kein Werkzeug.
Zuordnung. Das Zuordnungsskript ist kurz, und es muss das angepasste Modell wiederverwenden statt neu anzupassen:
km = joblib.load("kmeans_k4.pkl") # Mittelpunkte beim Anpassen eingefroren
names = {0: "A", 3: "B", 2: "C", 1: "D"} # nach dem Profil zugeordnet, nicht nach der Nummer
Z = pd.read_csv("hamper_components.csv").set_index("customer_id") # die Werte dieses Monats
out = pd.DataFrame(index=Z.index)
out["segment"] = [names[c] for c in km.predict(Z)]
out["distance"] = km.transform(Z).min(axis=1) # weit weg von jedem Mittelpunkt: zur Prüfung markieren
out.to_csv("customer_segments.csv")
Im Monat der Anpassung bildet es das Profil exakt ab: 2.424 in A, 447 in B, 1.241 in C, 2.088 in D. Das eine Prozent, das am weitesten von jedem Mittelpunkt entfernt liegt, sitzt mehr als 1,98 Standardeinheiten weg, und diese Kunden sehe ich mir von Hand an. Im Betrieb kommen die Komponentenwerte des Monats aus dem Skalierer und den Gewichten, die in Kapitel 12 gespeichert wurden, nie aus einer neuen Anpassung.
Monatlich neu zuordnen, aber nicht monatlich neu anpassen: Eine neue Anpassung verschiebt die Mittelpunkte und benennt alle neu, und das bricht jeden Kampagnenbericht, der ein Segment beim Namen nennt. Ein oder zweimal im Jahr neu anpassen, als kleines Projekt mit einer Wanderungstabelle. Für einen Shop dieser Größe dauert die ganze Arbeit zwei bis drei Wochen, die Hälfte davon an der Kundentabelle.
Fallstricke
Unskalierte Eingaben. Der Bestellwert in Euro neben einem Merkmal Grußkarte mit 0 oder 1: k means clustert nach Euro und nach sonst nichts. Standardisieren, oder besser, auf Komponenten clustern.
Kreise, wo die Daten keine haben. Ist die wahre Struktur eine lange, schmale Gruppe, etwa Eigenkäufer entlang eines Gefälles beim Ausgeben, schneidet k means sie in zwei. Mischmodelle mildern das, und eine Silhouette je Cluster zeigt es meistens.
RFM neu erfinden. Dominieren Ausgaben und Häufigkeit die Variablen fürs Clustering, heißen die Cluster am Ende "hoher, mittlerer, niedriger Wert", und du hast RFM mit mehr Schritten gebaut. Halt die Wertvariablen aus dem Clustering heraus und nutz sie danach fürs Profil. Deshalb ist der Umsatzanteil von Segment B ein Ergebnis und keine Eingabe.
Zu viele Segmente. Jedes Segment braucht ein Kreativbriefing, eine Variante der Landingpage, eine Mailstrecke und eine verantwortliche Person. Vier sind für ein kleines Team zu schaffen. Acht sind eine Tabelle, die niemand öffnet.
Starre Zugehörigkeit. Menschen ändern sich. Eine Kundin aus D, die dreimal zum vollen Preis kauft, ist jetzt eine C oder eine A. Monatlich zuordnen und eine Wanderungstabelle führen. Ein gesundes Programm bringt Menschen von D nach A und von A nach B.
Namen werden zu Tatsachen. "Gelegenheitssparer" ist eine Hypothese über 2.088 Menschen, entstanden aus drei Zahlen. Manche davon verschenken und haben den Shop über eine Gutscheinseite gefunden und würden für einen Korb, dem sie vertrauen, gern den vollen Preis zahlen. Teste den Namen: Schick der Hälfte von D eine Produktgeschichte zum vollen Preis statt eines Gutscheins und miss das Ergebnis.
Die LCA hat zwei eigene. Vertauschte Etiketten: Klasse 1 in einem Lauf ist Klasse 3 im nächsten, also ordne Klassen nach ihrem Profil zu, nie nach ihrer Nummer.2 Lokale Unabhängigkeit: Zwei fast doppelte Merkmale, wie "Grußkarte" und "an eine andere Adresse geliefert", brechen die Annahme und erfinden gern zusätzliche Klassen. Lass eins weg oder leg beide zusammen, wie oben.
Wie ich das für Kunden mache
Ich brauche Lesezugriff auf die Bestelldatenbank oder einen Export davon, 12 bis 24 Monate, dazu das Verhalten bei Mails, falls es erfasst ist. Nichts über das hinaus, was der Shop schon hat, und nie die Adressen selbst.
Es beginnt mit dem kostenlosen Workshop, in dem wir festlegen, wofür die Segmente da sind und welche zwei oder drei Maßnahmen das Geschäft für jedes realistisch umsetzen könnte. Danach zwei Wochen echte Arbeit auf meine Rechnung: die Kundentabelle, die Komponenten, k means und LCA nebeneinander, die Prüfungen auf Stabilität, das Profil, die Namen. Du kannst jederzeit aussteigen, und der schriftliche Plan gehört dir.
Was du bekommst:
- Eine Kundentabelle mit einem Segment und einer Sicherheit für jeden Kunden, geladen in deinen Shop und dein Mailtool.
- Ein Profil auf einer Seite je Segment, geschrieben für die Menschen, die damit arbeiten.
- Ein Entscheidungspapier mit der Zahl, auf die es ankommt (im Beispiel: 7 % der Kundschaft bringen 41 % des Umsatzes und werden wie Einmalkäufer behandelt), mit Fehlermarge und Risiko.
- Das Zuordnungsskript und ein Dashboard, das eine Frage beantwortet: Wie viele Kunden sind diesen Monat in jedem Segment, und woher sind sie gewandert?
Danach, wenn du willst, übernehme ich die Aktivierung: eine Kundenliste je Segment im Shop, im Mailtool und auf den Werbeplattformen, mit der Anrufliste für B im Oktober, der Erinnerung für A im April, dem Aboangebot für C und Lookalike Audiences, die nur aus A und B gespeist werden, denn aus der ganzen Datei gespeist würden sie vor allem mehr D finden.
Deine Rohdaten bleiben deine, und die Regeln für Code und Modelle stehen in den Bedingungen für Analytics und Data Science. Die Methode gehört zu Data Science, die Aktivierung zu Performance Marketing, und die Honorare stehen auf der Preisseite.
Fragen, bevor du eine Segmentierung abnimmst
- Welche Variablen wurden fürs Clustering verwendet, und waren Ausgaben und Häufigkeit darunter?
- Wie wurde die Zahl der Segmente gewählt, und was sagten die Silhouette und ein zweites Verfahren?
- Lässt sich jedes Segment in einem Satz beschreiben, ohne auf eine Tabelle zu schauen?
- Was macht das Geschäft für jedes Segment anders, und wer ist dafür verantwortlich?
- Wie stabil sind die Segmente beim Neuziehen und über die Zeit?
- Wie sieht der Plan für die Neuzuordnung aus, und wie wird Wanderung zwischen Segmenten sichtbar?
- Welche Segmentnamen sind Hypothesen, die noch getestet werden müssen?
Die Serie ist von Mike Grigsbys Marketing Analytics inspiriert.10 Die Erklärungen, Beispiele und Zahlen hier sind meine eigenen.
Footnotes
-
Entwicklerinnen und Entwickler von scikit learn, Clustering (Abschnitte zu k means, hierarchischem Clustering und dem Silhouettenkoeffizienten), Benutzerhandbuch von scikit learn, https://scikit-learn.org/stable/modules/clustering.html
-
Drew A. Linzer und Jeffrey B. Lewis, poLCA: An R Package for Polytomous Variable Latent Class Analysis, Journal of Statistical Software, Band 42, Heft 10, 2011, https://www.jstatsoft.org/article/view/v042i10
-
Entwicklerinnen und Entwickler von scikit learn, Gaussian mixture models, Benutzerhandbuch von scikit learn, https://scikit-learn.org/stable/modules/mixture.html
-
Peter S. Fader, Bruce G. S. Hardie und Ka Lok Lee, RFM and CLV: Using Iso value Curves for Customer Base Analysis, Journal of Marketing Research, 2005 (Arbeitspapier der Autoren), https://www.brucehardie.com/papers/rfm_clv_2005-02-16.pdf
-
Peter J. Rousseeuw, Silhouettes: a graphical aid to the interpretation and validation of cluster analysis, Journal of Computational and Applied Mathematics, Band 20, 1987, S. 53 bis 65, https://linkinghub.elsevier.com/retrieve/pii/0377042787901257
-
Gideon Schwarz, Estimating the Dimension of a Model, The Annals of Statistics, Band 6, Nr. 2, 1978, S. 461 bis 464, https://projecteuclid.org/journals/annals-of-statistics/volume-6/issue-2/Estimating-the-Dimension-of-a-Model/10.1214/aos/1176344136.full
-
Mitwirkende von Solidus, Datenbankschema des Kerns (spree_orders, spree_line_items, spree_addresses, spree_adjustments), Quellcode von Solidus auf GitHub, https://github.com/solidusio/solidus/blob/main/core/db/migrate/20160101010000_solidus_one_four.rb
-
Lawrence Hubert und Phipps Arabie, Comparing partitions, Journal of Classification, Band 2, 1985, S. 193 bis 218, https://link.springer.com/article/10.1007/BF01908075
-
Christian Hennig, Cluster wise assessment of cluster stability, Computational Statistics and Data Analysis, 2007, https://linkinghub.elsevier.com/retrieve/pii/S0167947306004622
-
Mike Grigsby, Marketing Analytics: A Practical Guide to Improving Consumer Insights Using Data Techniques, Kogan Page, 2022, ISBN 9781398608191, https://www.netgalley.co.uk/catalog/book/267990