[PYTHON] Einfache Statistiken, mit denen die Auswirkungen von Maßnahmen auf EG-Standorte und Codes analysiert werden können, die in Jupyter-Notebooks verwendet werden können

Einführung

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.

Zielperson

CVR-Unschärfe

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)

image.png

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.

image.png

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.

Test der CVR-Differenz

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.

image.png

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.

Ob B größer als A ist

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')

image.png

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.

Konfidenzintervall des durchschnittlichen Kaufpreises

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))

image.png

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)

image.png

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)

image.png

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)

image.png

Zuverlässiger Verkaufsbereich

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)

image.png

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

Einfache Statistiken, mit denen die Auswirkungen von Maßnahmen auf EG-Standorte und Codes analysiert werden können, die in Jupyter-Notebooks verwendet werden können
Goroutine (parallele Steuerung), die im Feld eingesetzt werden kann
[Django] Feldnamen, die für das Benutzermodell, die Benutzerregistrierung und die Anmeldemethoden verwendet werden können
Goroutine, die im Feld verwendet werden kann (errgroup.Group Edition)
[Python] Ein Programm, um die Anzahl der Äpfel und Orangen zu ermitteln, die geerntet werden können
Verstehen Sie die Wahrscheinlichkeiten und Statistiken, die für das Fortschrittsmanagement mit einem Python-Programm verwendet werden können
Ein Timer (Ticker), der im Feld verwendet werden kann (kann überall verwendet werden)
Python-Standardmodul, das in der Befehlszeile verwendet werden kann
Einfaches Auffüllen von Daten, die in der Verarbeitung natürlicher Sprache verwendet werden können
Blenden Sie die Warnung aus, dass zsh auf dem Mac standardmäßig verwendet werden kann
Ich wollte schnell einen Mailserver erstellen, der mit postfix + dovecot auf EC2 frei verwendet werden kann
QPS-Steuerung, die im Feld verwendet werden kann (Ratenlimit) Begrenzt die Ausführung auf n-mal pro Sekunde
[Python3] Code, der verwendet werden kann, wenn Sie die Erweiterung eines Bildes sofort ändern möchten
Persönliche Notizen zu Pandas-bezogenen Vorgängen, die in der Praxis verwendet werden können
So geben Sie mit Jupyter Notebook einen Wert in der Mitte einer Zelle aus
Einfache Programminstallation und automatische Programmaktualisierung, die in jeder Sprache verwendet werden kann
Ein Memorandum zur Ausführung des Befehls! Sudo magic in Jupyter Notebook
Erstellen Sie eine PYNQ-Umgebung auf Ultra96 V2 und melden Sie sich bei Jupyter Notebook an
Eine Bibliothek für Datenwissenschaft "Ich möchte das tun" auf dem Jupyter Notebook
So stellen Sie die Schriftbreite des in pyenv eingegebenen Jupyter-Notizbuchs gleich
Liste der Tools, mit denen Sie auf einfache Weise die Emotionsanalyse japanischer Sätze mit Python ausprobieren können (versuchen Sie es mit Google Colab).
Funktionen, die in der for-Anweisung verwendet werden können
Die Geschichte vom Starten des Jupyter-Notizbuchs von python2.x mit Docker (am Samstag und Sonntag zerquetscht)
Zusammenfassung der statistischen Datenanalysemethoden mit Python, die im Geschäftsleben verwendet werden können
Plotly Trace- und Layout-Vorlagen, die wahrscheinlich in Streudiagrammen verwendet werden
So filtern Sie die externen Schlüssel, die auf dem Django-Verwaltungsbildschirm ausgewählt werden können
Visualisierung von geografischen Informationen von R und Python, die von Power BI ausgedrückt werden können
[Python] Einführung in das WEB-Scraping | Zusammenfassung der Methoden, die mit dem Webdriver verwendet werden können
Hinweise zur Verwendung von StatsModels, die lineare Regression und GLM in Python verwenden können
Lesen Sie die CSV-Datei mit dem Jupiter-Notizbuch und schreiben Sie die Grafik übereinander
Grundlegende Algorithmen, die bei Wettkampfprofis eingesetzt werden können
Der Jupiter-Notebook-Kernel kann keine Verbindung mehr herstellen
ANTs Bildregistrierung, die in 5 Minuten verwendet werden kann
[Django] Über Benutzer, die für Vorlagen verwendet werden können
Wie man mit dem Phänomen umgeht, dass Python (Jupyter Notebook) auf WSL ausgeführt wird, wird abgebrochen
So starten Sie einen einfachen WEB-Server, der CGI von PHP und Python ausführen kann
Installieren Sie Mecab und CaboCha auf ubuntu16.04LTS, damit es aus der Python3-Serie verwendet werden kann
So richten Sie einen einfachen SMTP-Server ein, der lokal in Python getestet werden kann
[Python3] Code, der verwendet werden kann, wenn Sie die Größe von Bildern Ordner für Ordner ändern möchten
Eine einfache Version der Regierungsstatistik (Einwanderungskontrolle), die mit Jupyter einfach zu handhaben ist
So legen Sie Variablen fest, die in der gesamten Django-App verwendet werden können ~ Praktisch für Vorlagen usw. ~
Indem ich den Unterschied zwischen "Statistik" und "maschinellem Lernen" anordne, kann ich den Grund erkennen, warum "maschinelles Lernen" in vielen Unternehmen nicht verwendet werden kann!