Ghostwriting-Service Dr. Rainer Hastedt

Fachtexte, White Papers, statistische Auswertungen

Erfolgsmessung im Marketing - Folge 32: Googles CausalImpact-Package (Teil 2)

Im ersten Teil dieses Betrags habe ich mich mit der Frage beschäftigt, wie sich die Rentabilität einer Google-Ads-Kampagne einschätzen lässt. Ich habe hierfür zwei Zeiträume unterschieden, den Vorher-Zeitraum ohne die Kampagne und den Nachher-Zeitraum mit der Kampagne. Im Vorher-Zeitraum erhielt eine Website Visits durch Klicks auf Google-Suchergebnisse und im Nachher-Zeitraum durch Klicks auf Google-Suchergebnisse und Klicks auf Textanzeigen.

Den Effekt der Kampagne hatte ich im ersten Teil geschätzt, indem ich anhand der Daten des Vorher-Zeitraums prognostizierte, wie hoch die durchschnittliche Anzahl der Visits im Nachher-Zeitraum ohne die Kampagne gewesen wäre und diesen Wert von der durchschnittlichen Anzahl der tatsächlichen Visits im Nachher-Zeitraum abzog.

Ich werde den Effekt jetzt schätzen, indem ich Daten für einen Vergleichsmarkt berücksichtige. Ähnlich wie im ersten Teil schätze ich den Effekt zunächst ohne das CausalImpact-Package, mit einem als Difference-in-Differences bezeichneten Ansatz. Anschließend verwende ich das CausalImpact-Package und vergleiche meine zwei Schätzungen.

Der Difference-in-Differences-Ansatz

Als Ausgangspunkt dient mein Datensatz zwei, den Sie in der folgenden Grafik sehen. Die Zeitreihe y1 im oberen Teil der Grafik stammt aus Datensatz eins, den ich im ersten Teil dieses Beitrags verwendet habe. Neu ist Zeitreihe y0.

Datensatz zwei

library(ggplot2)
svg("emim-32-1.svg", width=4.3, height=3.1, bg="transparent")
ggplot(daten,aes(x=x,y=y1)) +
geom_line() +
geom_vline(xintercept=70.5,colour="darkgrey",
size=0.8,linetype="dashed") +
geom_point() +
geom_line(aes(x=x,y=y0)) +
geom_point(aes(x=x,y=y0)) +
annotate("text",x=69.5, y=85.5,label="Vorher-Zeitraum",hjust=1,vjust=0, size=5) +
annotate("text",x=71.5, y=85.5,label="Nachher-Zeitraum",hjust=0,vjust=0, size=5) +
annotate("text",x=1, y=102.2, label="y1 (mit Treatment)",hjust=0,vjust=0, size=5,col="#dd0000",fontface=1) +
annotate("text",x=1, y=91.9, label="y0 (ohne Treatment)",hjust=0,vjust=0, size=5,col="#dd0000",fontface=1) +
labs(x="Zeit", y="Visits", title="Datensatz zwei") +
theme_bw(base_size=15) +
theme(plot.background=element_rect(colour=NA,
fill="transparent"))
dev.off()

Der Difference-in-Differences-Ansatz ist anwendbar, wenn Sie zwei gleichartige Märkte voneinander getrennt bearbeiten können und die Entwicklung auf einem dieser Märkte unabhängig davon ist, wie Sie den anderen Markt bearbeiten.

Sie schalten zum Beispiel seit Beginn des Nachher-Zeitraums Textanzeigen nur für Nutzer mit Standort in bestimmten Bundesländern (Markt 1, Zeitreihe y1) und bilden die Vergleichszeitreihe anhand der Visits von Nutzern mit Standort in den übrigen Bundesländern (Markt 0, Zeitreihe y0). In diesem Fall setzt der Difference-in-Differences-Ansatz voraus, dass beide Märkte gleichartig sind und die Textanzeigen auf Markt 1 keinen Einfluss auf Markt 0 haben.

Unter dieser Voraussetzung zeigt die Entwicklung auf Markt 0 während des Nachher-Zeitraums, wie sich Markt 1 in diesem Zeitraum entwickelt hätte, wenn Sie dort keine Textanzeigen geschaltet hätten.

Ich kann den Effekt der Textanzeigen somit wie folgt ermitteln:

1. Vorher-Nachher-Vergleich für Markt 1

mw11 <- mean(daten$y1[71:100])
mw10 <- mean(daten$y1[1:70])
mw11-mw10 # 6.363274

Auf Markt 1 hat sich der Mittelwert im Nachher-Zeitraum im Vergleich zum Vorher-Zeitraum um 6,363274 Einheiten erhöht.

2. Vorher-Nachher-Vergleich für Markt 0

mw01 <- mean(daten$y0[71:100]) # 92.14353
mw00 <- mean(daten$y0[1:70]) # 90.08725
mw01-mw00 # 2.056278

Auf Markt 0 hat sich der Mittelwert im Nachher-Zeitraum im Vergleich zum Vorher-Zeitraum um 2,056278 Einheiten erhöht.

3. Effekt auf Markt 1

mw11-mw10 - (mw01-mw00) # 4.306996

Ich korrigiere den Vorher-Nachher-Vergleich für Markt 1, indem ich berücksichtige, dass der Mittelwert auf dem Vergleichsmarkt auch ohne Textanzeigen gestiegen ist. Auf diese Weise erhalte ich einen Effekt in Höhe von 4,31 (gerundet) - deutlich weniger als im Fall eines einfachen Vorher-Nachher-Vergleichs.

Difference-in-Differences-Design

svg("emim-32-2.svg", width=4.3, height=3.1, bg="transparent")
ggplot(daten,aes(x=x,y=y1)) +
geom_vline(xintercept=70.5,colour="darkgrey",
size=0.8,linetype="dashed") +
geom_rect(aes(xmin=70,xmax=71,ymin=mw00,ymax=mw01),
colour="orange",fill="orange") +
geom_rect(aes(xmin=70,xmax=71,ymin=mw10,ymax=mw10+mw01-mw00),colour="orange",fill="orange") +
geom_rect(aes(xmin=70,xmax=71,ymin=mw10+mw01-mw00,ymax=mw11),colour="lightgreen",fill="lightgreen") +
geom_line() +
geom_line(aes(y=y0)) +
labs(x="",y="", title="Difference-in-Differences-Design", caption=expression(paste("Effekt = ", mw['11']-mw['10'], " - (", mw['01']-mw['00'],")",sep=""))) +
geom_segment(aes(x=70.5,xend=100,y=mw11,yend=mw11), colour="darkblue",linetype="dashed") +
geom_segment(aes(x=1,xend=70.5,y=mw10,yend=mw10), colour="darkblue",linetype="dashed") +
geom_segment(aes(x=70.5,xend=100,y=mw01,yend=mw01), colour="darkblue",linetype="dashed") +
geom_segment(aes(x=1,xend=70.5,y=mw00,yend=mw00), colour="darkblue",linetype="dashed") +
annotate("text",x=72.5, y=84.5,label="Nachher",hjust=0,vjust=0, size=5,col="#dd0000",fontface=1) +
annotate("text",x=68.5, y=84.5,label="Vorher",hjust=1,vjust=0, size=5,col="#dd0000",fontface=1) +
annotate("text",x=1, y=101.9, label="y1 (mit Treatment)",hjust=0,vjust=0, size=5,col="#dd0000",fontface=1) +
annotate("text",x=1, y=91.7, label="y0 (ohne Treatment)",hjust=0,vjust=0, size=5,col="#dd0000",fontface=1) +
annotate("text",x=72.5, y=103.9,label="Effekt = 4,31",hjust=0,vjust=0, size=5,col="#dd0000",fontface=1) +
annotate("text",x=72.5, y=100.7,label="mw['01']-mw['00']",hjust=0,vjust=0, size=5,col="#dd0000",fontface=1,parse=TRUE) +
theme_bw(base_size=15) +
theme(plot.background=element_rect(colour=NA,
fill="transparent"))
dev.off()

Für meinen Vergleich mit dem CausalImpact-Package schätze ich mit Hilfe einer Regressionsanalyse ein 0,95-Konfidenzintervall für den Effekt. Hierzu verwende ich die Daten der beiden Zeitreihen und drei (0,1)-Variablen.

Y <- c(daten[,"y1"],daten[,"y0"])
Dt <- c(rep(0,70),rep(1,30),rep(0,70),rep(1,30))
Dj <- c(rep(1,100),rep(0,100))
Djt <- c(rep(0,70),rep(1,30),rep(0,100))

Die zugehörige Regressionsgleichung lautet:

Yijt = α+β1Dt2Dj3Djtjit

Index i ist für die einzelnen Beobachtungswerte (i = 1 bis 200)

Index j ist für den Markt (j = 0 für y0 und j = 1 für y1)

Index t ist für den Zeitraum (t = 0 für Vorher und t = 1 für Nachher)

Ich schätze die Regressionsgleichung:

reg <- lm(Y~Dt+Dj+Djt,daten2)
summary(reg)
Estimate Std. Error t value Pr(>|t|)
(Intercept) 90.0873 0.1694 531.707 0.000
Dt 2.0563 0.3093 6.647 0.000
Dj 10.0921 0.2396 42.119 0.000
Djt 4.3070 0.4375 9.845 0.000

In der Regressionsgleichung Yijt = α+β1Dt2Dj3Djtjit ist …

α = mw00 (90,0873)

β1 = mw01-mw00 (2,0563)

β2 = mw10-mw00 (10,0921)

β3 = Effekt (4,3070)

α + β1 + β2 + β3 = mw11

confint(reg)

Als 0,95-Konfidenzintervall für den Effekt (β3) erhalte ich (3,444250; 5,169742). Der Effekt ist demnach gleich 4,31 ± 0,87 (0,95-Konfidenzintervall, Werte gerundet).

Schätzung des Effekts mit CausalImpact

Ich definiere zunächst den Vorher- und den Nachher-Zeitraum:

pre.period <- c(1,70) # Vorher
post.period <- c(71,100) # Nachher

Jetzt schätze ich den Effekt:

library(CausalImpact)
impact <- CausalImpact(daten[,2:3],pre.period,post.period)

Ich versehe die vom CausalImpact-Package angebotene Grafik mit zusätzlichen Erläuterungen:

mwp <- impact$summary$Pred[1] # 101.1753
svg("emim-32-3.svg", width=4.3, height=3.1, bg="transparent")
plot(impact,"original") +
scale_y_continuous(breaks=c(95,100,mwp,105,mw11),
labels=c("95","100",expression(mw[p]),
"105",expression(mw[11]))) +
geom_hline(yintercept=mw11,size=0.4, linetype="dashed") +
geom_hline(yintercept=mwp,size=0.4, linetype="dashed") +
geom_segment(aes(x=78.1,xend=78.1,y=mwp,yend=mw11),
size=1.2, colour="#dd0000",
arrow=arrow(ends="last",length=unit(0.3,"cm"))) +
annotate("text",x=80.1, y=103.8,label="Effekt = 5,36",hjust=0,vjust=0, size=5,col="#dd0000",fontface=1) +
labs(x="Zeit",y="Visits",title="CausalImpact:", subtitle="Effekt = Mittelwert Nachher - Mittelwert Nachher prognostiziert") +
theme(plot.background=element_rect(colour=NA,
fill="transparent"), panel.grid.minor.y=element_blank())
dev.off()

Effekt laut CausalImpact

Der Effekt ist gemäß Grafik gleich mw11 - mwp (Mittelwert der Visits im Nachher-Zeitraum minus Mittelwert der Visits im Nachher-Zeitraum für den Fall, dass die Marketing-Maßnahme unterblieben wäre). Das Ganze ist daher ein Vorher-Nachher-Vergleich in modifizierter Form.

Mit dem CausalImpact-Package erhalte ich für mwp einen Schätzwert in Höhe von 101,18 (gerundet). Bei dieser Schätzung sind die Daten für den Vergleichsmarkt 0 berücksichtigt.

Nach dem Difference-in-Differences-Ansatz würde ich mwp anders schätzen: Mittelwert für Markt 1 im Vorher-Zeitraum plus Mittelwertdifferenz zwischen Nachher- und Vorher-Zeitraum auf Markt 0 (mwp = mw10 + mw01 - mw00). Nach diesem Ansatz würde ich mwp höher ansetzen (= 102,2356).

Vergleich der Schätzergebnisse

Ich erstelle eine Tabelle mit den Schätzwerten für die Konfidenzintervalle und den Effekt:

ci_OLS <- c(confint(reg)[4,1],mw11-mw10-(mw01-mw00),confint(reg)[4,2])
ci_CI <- c(impact$summary$AbsEffect.lower[1],
impact$summary$AbsEffect[1],
impact$summary$AbsEffect.upper[1])
daten3 <- data.frame(x=c("OLS","CausalImpact"), yu=c(ci_OLS[1],ci_CI[1]),
ym=c(ci_OLS[2],ci_CI[2]),
yo=c(ci_OLS[3],ci_CI[3]), stringsAsFactors=FALSE)
x yu ym yo
OLS 3,44425 4,306996 5,169742
CausalImpact 4,97359 5,367289 5,772576

Ich erstelle hieraus eine Grafik:

svg("emim-32-4.svg", width=4.3, height=3.1, bg="transparent")
ggplot(daten3,aes(x=x,y=ym)) +
geom_hline(yintercept=mw11-mw10, size=0.4, linetype="dashed") +
geom_errorbar(aes(ymin=yu, ymax=yo),width=0.2) +
geom_point(shape=4,size=5) +
annotate("text", x=1.4, y=mw11-mw10-0.03,
label="Effekt laut Vorher-Nachher-Vergleich",
hjust=0, vjust=1, size=5) +
labs(x="Schätzverfahren",y="Visits (Mittelwerte)", title="Schätzwerte für den Effekt: 0,95-Konfidenzintervalle") +
theme_bw(base_size=15) +
theme(plot.background=element_rect(colour=NA,
fill="transparent"))
dev.off()

Konfidenzintervalle CausalImpact vs. Difference-in-Differences

Mit dem CausalImpact-Package ergibt sich ein deutlich größerer Effekt als mit dem Difference-in-Differences-Ansatz. Das mit dem CausalImpact-Package geschätzte 0,95-Konfidenzintervall ist wesentlich kleiner als das mit der Methode der kleinsten Quadrate (OLS) geschätzte. Beide Konfidenzintervalle überschneiden sich.

Datensatz zwei

x y1 y0
1 99,5015543 89,6482221
2 98,9813896 89,8413467
3 100,741802 90,4258613
4 99,7982887 90,4368996
5 98,8711229 90,1023042
6 101,616058 89,4263443
7 98,325559 88,5937074
8 98,3667855 89,4104808
9 99,2337765 87,3249274
10 99,4636658 91,0348756
11 100,324057 91,4345511
12 97,8719502 85,3124084
13 99,1075943 88,6998855
14 98,4932543 87,0421841
15 99,216314 86,4736069
16 101,213564 90,8761838
17 100,774714 91,0355861
18 100,058527 89,7098422
19 100,104992 89,2748558
20 99,8198409 89,8334942
21 100,109563 89,3028533
22 100,489431 90,8805894
23 99,4666881 90,2915663
24 101,820151 90,9101212
25 101,093173 91,2773054
26 99,8306003 89,8668906
27 99,0590092 87,7400207
28 99,7600472 90,8314462
29 100,958284 90,6144389
30 101,766345 91,628054
31 100,323126 90,1550201
32 99,8555863 88,2509892
33 100,512612 88,6882828
34 100,002685 89,8016941
35 98,4797086 87,7517422
36 100,683796 90,1669316
37 101,364484 90,282709
38 101,677215 93,8888994
39 101,117848 90,0883721
40 101,372664 90,7342114
41 99,9401036 91,7958523
42 100,207955 88,8727943
43 99,7585705 90,2175355
44 99,4495427 90,0176797
45 99,9927617 91,4238594
46 102,95352 95,6577216
47 99,9741758 88,7259631
48 100,592913 92,425005
49 100,070976 87,9545574
50 101,04321 90,9447298
51 100,71251 92,5700577
52 99,8241224 88,7133079
53 100,069161 90,6612204
54 100,443498 87,5830749
55 102,632423 93,7736582
56 100,821653 90,318876
57 102,488252 94,9411412
58 98,5524819 87,4668417
59 99,0693248 88,7930501
60 96,8541086 88,7369013
61 98,2336541 87,8472127
62 99,8574657 89,7868695
63 101,043623 91,3292896
64 101,304247 89,9964982
65 101,656343 93,0979749
66 100,784154 91,1988152
67 101,314706 91,2296913
68 99,3770573 89,8613478
69 99,1705215 88,9735121
70 102,731858 92,102828
71 107,39714 92,2992047
72 106,664968 92,1048928
73 106,7075 93,4290951
74 105,510513 90,3191733
75 107,915939 92,0289111
76 106,798159 93,1622582
77 105,34848 91,2989413
78 105,703675 89,7313042
79 106,208017 92,363067
80 106,443917 92,7340496
81 105,33143 91,7649921
82 106,278507 91,0682845
83 107,558315 91,4539453
84 106,579954 92,0975072
85 106,558143 91,13953
86 106,5903 90,4363841
87 107,465578 93,2041334
88 106,229093 93,1121284
89 106,224054 91,3673051
90 106,182379 92,4585074
91 106,308635 93,0372503
92 106,956843 93,0238109
93 106,971453 93,534951
94 105,75681 90,8462551
95 104,435021 91,493366
96 106,904138 92,5693517
97 107,002995 93,1772631
98 107,120277 91,9801904
99 106,4397 91,916269
100 108,686009 95,153541