
Introduktion
Under de senaste 10–15 åren har marknadsföringsstrategier förändrats avsevärt i takt med den digitala utvecklingen. Stora varumärken och mindre aktörer har samlat in enorma mängder data om transaktioner, kundköp, preferenser, köpkraft, köpbeteende, demografi, recensioner med mera. All denna data kan hjälpa marknadsförare att förstå kundbeteende i olika skeden, från köpprocessens intention till faktisk affär och att bli en återkommande kund. Här kommer potentialen i data science in i bilden.
Data science omvandlar marknadsföringens big data till handlingsbara insikter, även om det ibland är mindre intuitivt vid första anblicken, till exempel icke uppenbara mönster och samförekomster i konsumentbeteende. Resultatet är att marknadsförare får en tydligare bild av sin målgrupp, kan attrahera nya kunder och behålla befintliga, optimera sina strategier, öka företagets synlighet, skapa mer lyckade kampanjer, ta in nya kanaler och därigenom avsevärt maximera intäkterna.
Ett av de vanligaste användningsfallen för data science i marknadsföring är att förutsäga kundbortfall. Låt oss titta närmare på detta.
Data science i marknadsföring: förutsägelse av kundbortfall
Kundbortfall är tendensen att kunder säger upp sin prenumeration på en tjänst de har använt och därmed slutar vara kund. Kundbortfallsgrad är andelen kunder som fallit ifrån under ett fördefinierat tidsintervall. Det är motsatsen till kundtillväxt, som spårar nya kunder.
Kundbortfallsgraden är en mycket viktig indikator på kundnöjdhet och företagets övergripande hälsa. Förutom naturligt bortfall som alltid förekommer i alla verksamheter, eller säsongsbetingat bortfall för vissa tjänster, finns det andra faktorer som kan tyda på att något i företaget inte fungerar och behöver åtgärdas. Dessa faktorer är:
- bristande eller lågkvalitativ kundsupport,
- negativa kundupplevelser,
- byte till en konkurrent med bättre villkor eller prissättning,
- förändrade kundprioriteringar,
- långvariga kunder känner sig inte nöjda,
- tjänsten motsvarade inte kundernas förväntningar,
- ekonomiska problem,
- bedrägeriskydd vid kunders betalningar.
Hög kundbortfallsgrad är ett allvarligt problem för alla företag av följande skäl:
- Den korrelerar med förlorade intäkter.
- Det kostar betydligt mer att skaffa nya kunder än att behålla befintliga. Detta gäller särskilt på hårt konkurrensutsatta marknader.
- Om bortfallet beror på bristfällig kundservice kan företagets rykte skadas allvarligt av negativa omdömen från missnöjda före detta kunder i sociala medier eller på recensionssajter.
Kundlojalitet är en avgörande del av affärsstrategin för alla prenumerationsbaserade tjänster. För att förutsäga kundbortfall och vidta förebyggande åtgärder behöver du samla in och analysera information om kundbeteende (köpintervall, total kundlivslängd, uppsägningar, uppföljande samtal och meddelanden, onlineaktivitet) och ta reda på vilka attribut och kombinationer som kännetecknar kunder som riskerar att lämna. Att i förväg veta vilka kunder som kan falla ifrån snart, särskilt om de står för höga intäkter eller har varit kunder länge, hjälper företaget att fokusera på just dem och utveckla en effektiv strategi för att försöka få dem att stanna. Tillvägagångssättet kan omfatta ett samtal med ett specialerbjudande om en gåva, rabatt, uppgradering av prenumerationen till samma pris eller någon annan skräddarsydd upplevelse.
Tekniskt sett är förutsägelse av kundbortfall ett klassificeringsproblem inom maskininlärning där kunder märks som "ja" eller "nej" i termer av risk att falla ifrån eller inte. Låt oss undersöka detta användningsfall i Python på verklig data.
Vi ska modellera bortfall i en telekomaffärsmodell där kunder kan ha flera tjänster hos ett telekombolag under ett huvudavtal. Datasetet innehåller funktioner från rensad kundaktivitet och en etikett som anger om en kund har fallit ifrån eller inte.
Låt oss titta på datan och utforska fördelningen av bortfallet:
import pandas as pd
telcom = pd.read_csv('telco.csv')
print(f'Number of customers: {telcom.shape[0]:,}\n'
f'Churn values: {set(telcom['Churn'])}\n\n'
f'Churn distribution, %:\n{round(telcom.groupby(['Churn']).size()/telcom.shape[0]*100).convert_dtypes()}')
Number of customers: 7,032
Churn values: {0, 1}
Churn distribution, %:
Churn
0 73
1 27
dtype: float64
27% av kunderna föll ifrån, vilket är ganska högt. Jämfört med det föregående data science-exemplet verkar detta dataset dock inte ha något allvarligt problem med obalanserade klasser.
Nu ska vi förbehandla datan för att kunna tillämpa maskininlärning för att förutsäga bortfall. Detta inkluderar att dela upp datan i tränings- och testmängder samt extrahera funktioner och målvariabel:
from sklearn.model_selection import train_test_split
target = ['Churn']
custid = ['customerID']
cols = [col for col in telcom.columns if col not in custid + target]
X = telcom[cols]
y = telcom[target]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
Den första modelleringsalgoritmen vi ska använda för att förutsäga bortfallsetiketter och uppskatta noggrannheten är en enkel klassificeringsmodell med logistisk regression:
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
lr = LogisticRegression()
lr.fit(X_train, y_train)
predictions = lr.predict(X_test)
print(f'Test accuracy: {round(accuracy_score(y_test, predictions), 4)}')
Test accuracy: 0.8009
Därefter lägger vi till en funktion i vår logistiska regressionsmodell, nämligen att köra den på skalad data med L1-regularisering för att göra funktionsurval parallellt med modellbygget. Olika värden på parametern C (som är inversen av regulariseringsstyrkan) påverkar modellens noggrannhet. Nu sätter vi C-värdet till 0,025:
lr = LogisticRegression(penalty='l1', C=0.025, solver='liblinear')
lr.fit(X_train, y_train)
predictions = lr.predict(X_test)
print(f'Test accuracy: {round(accuracy_score(y_test, predictions), 4)}')
Test accuracy: 0.7969
Nu ska vi finjustera C-parametern för L1-regularisering för att hitta det optimala värdet som minskar modellens komplexitet samtidigt som bra prestandamått bibehålls. För detta itererar vi över olika C-värden, bygger en logistisk regressionsmodell för varje och beräknar prestandamåtten.
Listan C har skapats i förväg med möjliga parametervärden. Arrayen l1_metrics har byggts med tre kolumner, där den första är C-värdena och de två följande är platshållare för antalet icke-nollkoefficienter och modellens noggrannhet. Låt oss testa detta tillvägagångssätt:
C Non-Zero Coeffs Accuracy
0 1.0000 23.0 0.801479
1 0.5000 22.0 0.799204
2 0.2500 21.0 0.802048
3 0.1000 20.0 0.802617
4 0.0500 18.0 0.802048
5 0.0250 13.0 0.796928
6 0.0100 5.0 0.790102
7 0.0050 3.0 0.783276
8 0.0025 2.0 0.745734
Vi ser att lägre C-värden minskar antalet icke-nollkoefficienter (dvs funktioner för modellering) och därmed modellens komplexitet, men de minskar också noggrannheten. Det verkar som att C-värdet 0,05 är optimalt: det minskar antalet funktioner till 18 och ger en noggrannhet som är något högre än i den icke-regulariserade modellen.
Nu testar vi en annan modelleringsalgoritm – ett beslutsträd:
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
clf.fit(X_train, y_train)
predictions = clf.predict(X_test)
print(f'Test accuracy: {round(accuracy_score(y_test, predictions), 4)}')
Test accuracy: 0.7275
För att välja en mer träffsäker modell och samtidigt undvika överanpassning kan vi prova att justera trädets djup (parametern max_depth) och identifiera ett optimalt värde. Tekniskt sett liknar processen den vi använde för att välja optimalt C-värde i den logistiska regressionen ovan: här itererar vi över flera värden på max_depth, tränar ett beslutsträd för varje och beräknar sedan prestandamåtten.
Listan depth_list har skapats i förväg med möjliga parametervärden. Arrayen depth_tuning har byggts med två kolumner, där den första fyllts med djupkandidaterna och den andra är en platshållare för noggrannheten. Låt oss tillämpa detta för att hitta optimalt trädjup:
depth_list = list(range(2, 15))
depth_tuning = np.zeros((len(depth_list), 2))
depth_tuning[:, 0] = depth_list
for index in range(len(depth_list)):
clf = DecisionTreeClassifier(max_depth=depth_list[index])
clf.fit(X_train, y_train)
predictions = clf.predict(X_test)
depth_tuning[index, 1] = accuracy_score(y_test, predictions)
col_names = ['Max_Depth', 'Accuracy']
print(pd.DataFrame(depth_tuning, columns=col_names))
Max_Depth Accuracy
0 2.0 0.756542
1 3.0 0.783276
2 4.0 0.782708
3 5.0 0.791809
4 6.0 0.778157
5 7.0 0.780432
6 8.0 0.757110
7 9.0 0.762230
8 10.0 0.763936
9 11.0 0.752560
10 12.0 0.745165
11 13.0 0.732651
12 14.0 0.727531
Alltså ökar noggrannheten först med större djup och börjar sedan sjunka. Vid max_depth 5 visar trädet högst noggrannhet, så vi kan betrakta detta som det optimala djupet.
Efter att ha identifierat de bästa parametervärdena för både logistisk regression och beslutsträd bygger vi om modellerna och identifierar samt tolkar de viktigaste faktorerna som driver bortfallet uppåt eller nedåt.
För den logistiska regressionsmodellen ska vi extrahera och undersöka exponenterna av de resulterande koefficienterna:
# Reconstructing the best model
lr = LogisticRegression(penalty='l1', C=0.05, solver='liblinear')
lr.fit(X_train, y_train)
predictions = lr.predict(X_test)
# Combining feature names and coefficients into one dataframe
feature_names = pd.DataFrame(X_train.columns, columns=['Feature'])
log_coef = pd.DataFrame(np.transpose(lr.coef_), columns=['Coefficient'])
coefficients = pd.concat([feature_names, log_coef], axis=1)
# Calculating exponents of the coefficients
coefficients['Exp_Coefficient'] = np.exp(coefficients['Coefficient'])
# Removing coefficients that are equal to zero
coefficients = coefficients[coefficients['Coefficient']!=0]
print(coefficients.sort_values(by=['Exp_Coefficient']))
Feature Coefficient Exp_Coefficient
21 tenure -0.907750 0.403431
4 PhoneService_Yes -0.820517 0.440204
17 Contract_Two year -0.595271 0.551413
8 TechSupport_Yes -0.418254 0.658195
16 Contract_One year -0.414158 0.660896
5 OnlineSecurity_Yes -0.412228 0.662173
6 OnlineBackup_Yes -0.143100 0.866667
3 Dependents_Yes -0.039299 0.961463
7 DeviceProtection_Yes -0.017465 0.982687
11 PaperlessBilling_Yes 0.071389 1.073999
1 SeniorCitizen_Yes 0.097904 1.102857
19 PaymentMethod_Electronic check 0.188533 1.207477
22 MonthlyCharges 0.901454 2.463182
Vi ser att den funktion som har störst effekt på oddsen för bortfall är tenure (kundens tid som kund). Generellt minskar koefficienternas exponenter under 1 oddsen, medan de över 1 ökar dem.
För beslutsträdet ska vi extrahera och visualisera if-else-reglerna:
# Reconstructing the best model
clf = DecisionTreeClassifier(max_depth=5)
clf.fit(X_train, y_train)
predictions = clf.predict(X_test)
from sklearn import tree
import graphviz
# Exporting a graphviz object from the trained decision tree
exported = tree.export_graphviz(decision_tree=clf,
out_file=None,
feature_names=cols,
precision=1,
class_names=['Not churn', 'Churn'],
filled=True)
graph = graphviz.Source(exported)
display(graph)

Vi fick en tydlig visualisering av beslutsträdet som kan tolkas som en uppsättning if-else-regler uppifrån och ned. Återigen ser vi att kundens tid som kund är den viktigaste variabeln som driver bortfallet. Beslutsträdet kan byggas med fler lager, vilket ger mer insikter om andra variabler.
Som möjliga nästa steg kan vi fortsätta att justera modellparametrar, använda olika tillvägagångssätt för uppdelning i träning/test, tillämpa och jämföra andra maskininlärningsalgoritmer samt analysera olika mått för att bedöma modellens prestanda.
Om du vill fördjupa dig i förutsägelse av kundbortfall och andra tillämpningar av data science i marknadsföring kan den här kursen om Machine Learning for Marketing in Python vara en bra start.