Wenn Sie eine allgemeine EC-Site betreiben, überprüfen Sie, was mit CVR passiert ist, wenn Sie einige Maßnahmen ergriffen haben, und wie sich der Umsatz geändert hat, wenn Sie Gutscheine verteilt haben. Es kann jedoch mit "CVR ist gestiegen, aber ist es kein Zufall?" Oder "Es ist bedeutend! (Irgendwie)" enden. In diesem Artikel werde ich erklären, wie Sie die Signifikanz mithilfe von Statistiken überprüfen und ein einfaches Tool einfügen, das Sie kopieren und in das Jupyter-Notizbuch einfügen können. Die Erklärung ist nicht sehr streng.
Die Firma von Herrn A verkauft Äpfel auf seiner Website. Herr A wurde von seinem Chef angewiesen, den CVR zu berechnen. CVR ist die verkaufte Zahl geteilt durch PV. An diesem Tag wurden 30 Stück für 1.000 PV verkauft, sodass der CVR 3,0% betrug. Am nächsten Tag wurden 28 Stück für 1.000 PV verkauft, der CVR betrug also 2,8%, und mein Chef wurde wütend, als ich log. CVR ist eine verschwommene Sache, daher müssen Sie es so weit sagen, dass es ungefähr passt. Derzeit können es 2,5% bis 3,5% sein, aber ich würde gerne statistischer denken.
Hier gibt es ein Phänomen namens Bernoulli-Verteilung oder Binomialverteilung, das das Phänomen ausdrückt, dass Menschen, die die Website besuchen, zwei Möglichkeiten haben: "Kaufen" oder "Nicht kaufen". In der folgenden Grafik zeigt die horizontale Achse die Anzahl der verkauften Artikel und die vertikale Achse die Wahrscheinlichkeit zu diesem Zeitpunkt. Es besteht eine hohe Wahrscheinlichkeit, dass 30 am meisten verkauft werden, und es ist unwahrscheinlich, dass es 20 oder weniger sein wird.
import numpy as np
from scipy.stats import binom, norm
import pandas as pd
p = 0.03
N = 1000
k = np.arange(100)
pd.Series(binom.pmf(k, N, p), name='binom').plot(figsize=(12,6), legend=True)
Mit diesem scheint es, dass Sie den Ort mit einem großen Wert aufnehmen und einen Bereich ausgeben können, der ungefähr übereinstimmt. Dieser Bereich wird übrigens als ** Konfidenzintervall ** bezeichnet, und die Wahrscheinlichkeit, in das Konfidenzintervall einzutreten, wird als ** Konfidenzkoeffizient ** bezeichnet. Verwenden Sie die folgenden Tools, um eine 95% ige Chance zu erhalten, den richtigen Bereich zu erhalten. (Die Anzahl der Proben entspricht der Anzahl der PV.)
#Konfidenzintervall des Bevölkerungsverhältnisses
import ipywidgets as widgets
import math
from scipy.stats import binom, norm
def calc(v):
input_N = w.value
input_cvr = w2.value
input_conf = w3.value
p = input_cvr / 100
#Im Falle einer Binomialverteilung
max_index = binom.isf((100 - input_conf) / 100, input_N, input_cvr / 100)
min_index = binom.isf(input_conf / 100, input_N, input_cvr / 100)
#Bei Annäherung an eine Normalverteilung
#max_index = norm.isf((100 - input_conf)/100, loc=input_N*p, scale=np.sqrt(input_N*p*(1-p)))
#min_index = norm.isf(input_conf/100, loc=input_N*p, scale=np.sqrt(input_N*p*(1-p)))
print(f'{math.floor(min_index / input_N * 10000) / 100}(%) <= CVR <= {math.ceil(max_index / input_N * 10000) / 100}(%)')
button = widgets.Button(description="Berechnung")
button.on_click(calc)
w = widgets.IntText(
value=1000,
description='Anzahl von Beispielen:',
disabled=False
)
w2 = widgets.BoundedFloatText(
value=1,
min=0,
max=100.0,
description='CVR(%):',
disabled=False
)
w3 = widgets.BoundedIntText(
value=95,
min=0,
description='Konfidenzkoeffizient (%):',
disabled=False
)
output = widgets.Output()
display(w, output)
display(w2, output)
display(w3, output)
display(button, output)
Bei der Ausführung sieht es wie im Bild unten aus. Sie können es frei eingeben.
2,11 (%) <= CVR <= 3,89 (%)! Es ist ziemlich breit. Ein Konfidenzfaktor von 95-> 90% schränkt den Bereich ein. Während es eine hohe Wahrscheinlichkeit gibt, dass Sie eine "Person in Ihren Teenagern bis 20 Jahren oder eine Person in Ihren 30ern bis 40ern oder eine Person in Ihren 50ern oder älter" sind, scheint es, dass "Teenager bis 20s" eher abgehen. Wenn Sie die Anzahl der Proben erhöhen, wird der Bereich möglicherweise kleiner.
Der auskommentierte Teil der Quelle ist die Bernoulli-Verteilung [Normalverteilung](https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E5%88%86%E5 Wenn es nahe an% B8% liegt 83). Wenn die Anzahl der Proben groß genug ist, wird fast das gleiche Ergebnis erzielt. Wenn ich es starte, scheint es, dass selbst wenn die Anzahl der Samples 1.000 beträgt, es groß genug ist. Es scheint nicht notwendig zu sein, eine Annäherung vorzunehmen, aber die Annahme einer Normalverteilung ist statistisch oft einfach.
Da sich das Produkt nicht gut verkaufte, habe ich versucht, hypnotische Bilder modal zu drucken, und der CVR stieg von 2,0% auf 3,0%. Beide sind 1.000 PV. Es scheint signifikant zu sein, weil es sich um das 1,5-fache erhöht hat, aber ich werde versuchen, es statistisch zu verifizieren (= zu testen).
Der Test beschreibt es als "signifikant", wenn es passiert, obwohl angenommen wurde, dass es selten passieren würde. Die Wahrscheinlichkeit wird als Signifikanzniveau bezeichnet und dies wird zuerst entschieden. Wenn in diesem Fall das Signifikanzniveau auf 5% eingestellt ist, kann gesagt werden, dass es "signifikant" ist, wenn die Wahrscheinlichkeit, dass es einen Unterschied gibt, 5% oder weniger beträgt, weil es auf 2,0-> 3,0% gestiegen ist.
Ich werde die Details weglassen, aber da die Bernoulli-Verteilung an die Normalverteilung angenähert werden kann, wenn die Anzahl der Proben groß genug ist, und der Unterschied zwischen den Normalverteilungen auch die Normalverteilung ist (Referenz: [Reproduzierbarkeit der Normalverteilung](Referenz: https://bellcurve.jp/statistics/course/7799.html)) Für genaue Ergebnisse ist es besser, etwa 1.000 Proben zu haben.
#Test des Unterschieds im Bevölkerungsverhältnis
import matplotlib.pyplot as plt
import ipywidgets as widgets
import math
from scipy.stats import norm
def calc_plot(v):
input_N1 = w_n1.value
input_cvr1 = w_cvr1.value
input_N2 = w_n2.value
input_cvr2 = w_cvr2.value
input_conf = w3.value
p1 = input_cvr1 / 100
p2 = input_cvr2 / 100
N1 = input_N1
N2 = input_N2
p = (N1 * p1 + N2 * p2) / (N1 + N2)
z = (p2 - p1) / math.sqrt(p * (1 - p) * (1 / N1 + 1 / N2))
min_index = norm.isf(1 - (100 - input_conf)/(2*100), loc=0, scale=1)
max_index = norm.isf((100 - input_conf)/(2*100), loc=0, scale=1)
if min_index <= z and z <= max_index:
print('Nicht signifikant')
print(f'|{z}| <= {max_index}')
else:
print('Es gibt einen signifikanten Unterschied!')
print(f'{max_index} <= |{z}|')
xlimit = np.array([math.ceil(abs(z)), 5]).max()
x = np.arange(- xlimit * 100, xlimit * 100)/100
y = norm.pdf(x)
plt.figure(figsize=(15, 7))
plt.vlines([min_index, max_index], y.min(), y.max(), "red", linestyles='dashed', label='rejection')
plt.legend()
plt.vlines([z], y.min(), y.max(), "black", linestyles='dashed', label='statistics')
plt.legend()
plt.plot(x, y,'b-', lw=1, label='norm pdf')
button = widgets.Button(description="Berechnung")
button.on_click(calc_plot)
w_n1 = widgets.IntText(
value=10000,
description='Anzahl von Beispielen:',
disabled=False
)
w_n2 = widgets.IntText(
value=12000,
description='Anzahl von Beispielen:',
disabled=False
)
w_cvr1 = widgets.BoundedFloatText(
value=2,
min=0,
max=100.0,
description='CVR(%):',
disabled=False
)
w_cvr2 = widgets.BoundedFloatText(
value=3,
min=0,
max=100.0,
description='CVR(%):',
disabled=False
)
w3 = widgets.BoundedIntText(
value=95,
min=0,
description='Konfidenzkoeffizient (%):',# 100 -Signifikanzniveau
disabled=False
)
w_a = widgets.VBox([widgets.Label('A'), w_n1, w_cvr1])
w_b = widgets.VBox([widgets.Label('B'), w_n2, w_cvr2])
whbox = widgets.HBox([w_a, widgets.Label(' '), w_b])
output = widgets.Output()
display(whbox, output)
display(w3, output)
display(button, output)
Als ich den obigen Code ausgeführt habe, habe ich das im Bild unten gezeigte Ergebnis erhalten.
Wenn sich innerhalb der beiden roten Linien eine schwarze Linie befindet, wird dies als "diese Art von Unschärfe ist möglich" angesehen und das Ergebnis ist nicht signifikant. Im Gegenteil, wenn es außen ist, ist es bedeutend. Wenn nach einer Weile des A / B-Tests beide von 2,0% -> 3,0% bei 10.000 PV ansteigen, kann gesagt werden, dass es einen signifikanten Unterschied gibt. Es ist kein Zufall, dass die Unschärfe durch Erhöhen der Anzahl der Proben verringert wird, aber der Unterschied beträgt 1,0%. Hierbei ist zu beachten, dass "nicht signifikant" nicht als unbedeutend angesehen werden kann. Nur weil die schwarzen und roten Linien so weit voneinander entfernt sind, ist dies nicht sehr bedeutsam oder sehr bedeutsam. Es gibt zwei Möglichkeiten, ob es wichtig ist oder nicht. Angesichts dessen habe ich das Gefühl, dass das Konfidenzintervall mehr Informationen enthält als das Testergebnis.
Im obigen Fall haben wir getestet, ob es einen Unterschied gibt, aber in Wirklichkeit möchten wir normalerweise nur wissen, ob sich die CVR durch Maßnahmen erhöht hat. Verwenden Sie in diesem Fall den ** einseitigen Test **. (Im obigen Fall wird es als zweiseitiger Test bezeichnet.) Wenn Sie einen einseitigen Test durchführen, ersetzen Sie calc_plot im obigen Python-Code durch den folgenden Code.
input_N1 = w_n1.value
input_cvr1 = w_cvr1.value
input_N2 = w_n2.value
input_cvr2 = w_cvr2.value
input_conf = w3.value
p1 = input_cvr1 / 100
p2 = input_cvr2 / 100
N1 = input_N1
N2 = input_N2
p = (N1 * p1 + N2 * p2) / (N1 + N2)
z = (p2 - p1) / math.sqrt(p * (1 - p) * (1 / N1 + 1 / N2))
max_index = norm.isf((100 - input_conf)/100, loc=0, scale=1)
if z <= max_index:
print('Nicht signifikant')
print(f'|{z}| <= {max_index}')
else:
print('Es gibt einen signifikanten Unterschied!')
print(f'{max_index} <= |{z}|')
xlimit = np.array([math.ceil(abs(z)), 5]).max()
x = np.arange(- xlimit * 100, xlimit * 100)/100
y = norm.pdf(x)
plt.figure(figsize=(15, 7))
plt.vlines([max_index], y.min(), y.max(), "red", linestyles='dashed', label='rejection')
plt.legend()
plt.vlines([z], y.min(), y.max(), "black", linestyles='dashed', label='statistics')
plt.legend()
plt.plot(x, y,'b-', lw=1, label='norm pdf')
In diesem Fall ist es wichtig, ob sich rechts von der roten Linie eine schwarze Linie befindet. Es ist einfacher, mit einer kleinen Anzahl von Proben eine signifikante Beurteilung vorzunehmen, da Sie nur sehen können, ob diese höher ist.
Selbst wenn der CVR höher ist, kann es sein, dass der durchschnittliche Kaufpreis gesunken ist. (Wenn der Mindestnutzungsbetrag erhöht wird, wenn der Gutschein verteilt wird usw.) Berechnen wir, wie stark der durchschnittliche Kaufpreis schwanken wird. Im Allgemeinen weist das Jahreseinkommen eine logarithmische Normalverteilung auf, und es besteht eine positive Korrelation zwischen dem Jahreseinkommen und dem Kaufpreis. Daher wird angenommen, dass der Kaufpreis auch auf EG-Standorten, die verschiedene Produkte handhaben, eine ähnliche Verteilung aufweist. (Referenz: Beispiel für logarithmische Normalverteilung und Mittelwert, Varianz)
#Normalverteilung protokollieren
from scipy.stats import lognorm
x = np.arange(1, 100000) / 1
A = 1.5000e+04 #Durchschnittlicher Kaufpreis
B = 1.5000e+04 ** 2 #Verteilt
s = np.sqrt(np.log(B/(A ** 2) + 1))
mu = np.log(A) - (s ** 2 / 2)
y = pd.Series(lognorm.pdf(x, s, 0, np.exp(mu)))
y.index = x
y.plot(figsize=(12, 6))
Wenn Sie diese kombinieren und den Durchschnitt nehmen, erhalten Sie wieder eine Normalverteilung. Der folgende Code ist ein Histogramm des Ergebnisses der Wiederholung der 100.000-fachen Berechnung des Durchschnitts von 500 Stück. Laut Center Pole Limitation sieht das durchschnittliche Histogramm ähnlich aus, wenn genügend Proben vorhanden sind, auch wenn es sich nicht um eine logarithmische Normalverteilung handelt. .. Wie viele Proben ausreichen, hängt von der Verteilung ab, aber ungefähr 1.000 scheinen gut zu sein.
means = []
n = 500
for i in range(0, 100000):
means.append(np.array(lognorm.rvs(s, 0, np.exp(mu), size=n)).mean())
pd.Series(means).hist(bins=100)
Das folgende Bild wird als QQ-Diagramm bezeichnet. Wenn Blau nahe der roten Geraden liegt, liegt es nahe an der Normalverteilung.
import pylab
stats.probplot(means, dist="norm", plot=pylab)
Der folgende Code berechnet das Konfidenzintervall für eine Normalverteilung. Die "Anzahl der Proben" ist diesmal nicht die PV, sondern die Anzahl der Käufe.
#Konfidenzintervall des durchschnittlichen Kaufpreises
import ipywidgets as widgets
import math
import numpy as np
import pandas as pd
from scipy.stats import binom, norm
def calc(v):
n = w.value
mu = w2.value
sigma = w3.value
input_conf = w4.value
max_index = norm.isf((100 - input_conf)/100, loc=mu, scale=sigma / np.sqrt(n))
min_index = norm.isf(input_conf/100, loc=mu, scale=sigma / np.sqrt(n))
print(f'{min_index} <=Durchschnittlicher Kaufpreis<= {max_index}')
button = widgets.Button(description="Berechnung")
button.on_click(calc)
w = widgets.IntText(
value=1000,
description='Anzahl von Beispielen:',
disabled=False
)
w2 = widgets.FloatText(
value=15000,
description='Durchschnittlicher Kaufpreis:',
disabled=False
)
w3 = widgets.FloatText(
value=15000,
description='Standardabweichung:',
disabled=False
)
w4 = widgets.BoundedIntText(
value=95,
min=0,
description='Konfidenzkoeffizient (%):',
disabled=False
)
output = widgets.Output()
display(w, output)
display(w2, output)
display(w3, output)
display(w4, output)
display(button, output)
Umsatz = Anzahl PV x CVR x durchschnittlicher Kaufpreis Daher wird es berechnet, indem der Vertrauensbereich des durchschnittlichen Kaufpreises CVR x multipliziert wird.
#Zuverlässiger Verkaufsbereich
import ipywidgets as widgets
import math
from scipy.stats import binom, norm
def calc(v):
n = w_n.value
cvr = w_cvr.value
mu = w_mu.value
sigma = w_s.value
input_conf = w_conf.value
p = cvr / 100
min_index_sales = norm.isf(1 - (100 - input_conf)/(2*100), loc=mu, scale=sigma / np.sqrt(n*p))
max_index_sales = norm.isf((100 - input_conf)/(2*100), loc=mu, scale=sigma / np.sqrt(n*p))
max_index = norm.isf((100 - input_conf)/100, loc=n*p, scale=np.sqrt(n*p*(1-p))) / n
min_index = norm.isf(input_conf/100, loc=n*p, scale=np.sqrt(n*p*(1-p))) / n
print(f'{n * min_index * min_index_sales} <=Der Umsatz<= {n * max_index * max_index_sales}')
button = widgets.Button(description="Berechnung")
button.on_click(calc)
w_n = widgets.IntText(
value=10000,
description='Anzahl der Proben (PV):',
disabled=False
)
w_cvr = widgets.BoundedFloatText(
value=12.4,
min=0,
max=100.0,
description='CVR(%):',
disabled=False
)
w_mu = widgets.FloatText(
value=18303,
description='durchschnittlich:',
disabled=False
)
w_s = widgets.FloatText(
value=15217,
description='Standardabweichung:',
disabled=False
)
w_conf = widgets.BoundedIntText(
value=90,
min=0,
description='Konfidenzkoeffizient (%):',
disabled=False
)
output = widgets.Output()
display(w_n, output)
display(w_cvr, output)
display(w_mu, output)
display(w_s, output)
display(w_conf, output)
display(button, output)
In der Realität sind CVR und durchschnittlicher Kaufpreis nicht immer unabhängig voneinander. Wenn der durchschnittliche Kaufpreis steigt und der CVR sinkt, wird das Vertrauensintervall meines Erachtens wahrscheinlich enger. Ich war erschöpft, bevor ich es im Detail untersuchte ...
Recommended Posts