Einführung
Eine gemeinnützige Werkstatt hat zwei Varianten ihrer Anmeldeseite, A und B. Vor dem Vergleich muss die Analystin festlegen, was als eine Beobachtung zählt. Eine Person kann die Seite dreimal besuchen oder zweimal auf die Anmeldung klicken, während eine andere überhaupt kein Ereignis erzeugt. Solche Ereigniszeilen als verschiedene Personen zu zählen würde den Vergleich verzerren.
Du liest zuerst ein Beispiel mit sechs Personen und erstellst dann für jeden zugewiesenen Besucher ein binäres Ergebnis innerhalb von sieben Tagen in einem größeren, festen Experiment. Diese eigens erstellten synthetischen Lehrdaten stellen eine zufällige Zuweisung dar; sie sind weder echte Besucherverfolgung noch Belege für ein reales Geschäft. Python, pandas, NumPy, SciPy und Matplotlib stehen in einer unabhängigen WebIDE-Umgebung bereit. Vorausgesetzt werden grundlegende Terminalnavigation, Python-Funktionen und die Auswahl von Tabellen mit pandas; die nötigen Verknüpfungen und Zeitoperationen werden hier erklärt. Statistische Inferenz wird nicht vorausgesetzt. Die 55 Minuten sind eine Schätzung des Autors einschließlich Lesen und gewöhnlicher Fehlerkorrekturen.
Vergleiche Personen statt Klicks
In diesem Schritt liest du sechs Besucherkarten und berechnest Anmelderaten mit den zugewiesenen Personen als Nennern.
Beginne in WebIDE und öffne Terminal → New Terminal. Terminal und Dateiexplorer greifen auf dieselben Dateien zu. cd ändert das Arbeitsverzeichnis dieses Terminals; wiederhole den Befehl, wenn du ein weiteres Terminal öffnest.
cd /home/labex/project/signup-experiment
cat zeigt eine Textdatei an, ohne sie zu verändern. Lies das kleine Rechenbeispiel:
cat warmup-visitors.csv
Jede Zeile ist ein zugewiesener Besucher. Die ersten drei gehören zu A, die letzten drei zu B. Für dieses Beispiel wurden die qualifying_signup_events bereits innerhalb des siebentägigen Fensters jeder Person eingeordnet. signed_up ist das entsprechende binäre Ergebnis: 1, wenn mindestens eine gültige Anmeldung vorliegt, sonst 0.
P-8 hat drei Besuche und zwei Anmeldeereignisse, trägt aber eine Person und ein positives Ergebnis bei. Q-3 hat weder Besuch noch Anmeldeereignis, bleibt jedoch eine zugewiesene Person mit Ergebnis 0. Lies die sechs Karten vor der Berechnung: A hat eine Anmeldung unter drei zugewiesenen Personen, B zwei unter drei.
Die Anmelderate von A ist 1 / 3, also etwa 33.33%. Die von B ist 2 / 3, etwa 66.67%. Die Differenz ist B minus A, etwa +33.33 Prozentpunkte. Prozentpunkte ergeben sich durch direktes Subtrahieren von Prozentwerten; das ist kein relativer prozentualer Anstieg. Weitere Besuche vergrößern keinen der Nenner.
Schreibe eine wiederverwendbare Vergleichsfunktion. Das Ausführungsprogramm übergibt einen pandas-DataFrame namens people mit den Spalten visitor_id, group und signed_up. Eine boolesche Maske wie people["group"] == "A" wählt die Zeilen von A aus. len(a) zählt zugewiesene Personen; die Summe einer binären Spalte zählt Personen mit Anmeldung. int und float erzeugen gewöhnliche Python-Zahlen für JSON-Dateien.
Die Shell-Umleitung > ersetzt die angegebene Datei. Ein Here-Dokument mit Anführungszeichen, <<'PY', übergibt alles bis zu einer Zeile, die nur PY enthält, ohne Shell-Erweiterung an diese Datei. Füge den gesamten Block einschließlich des letzten PY ein. Alternativ bearbeitest du dieselbe Datei in WebIDE und wählst File → Save.
cat > compare.py <<'PY'
def summarize(people):
result = {}
for group in ["A", "B"]:
selected = people[people["group"] == group]
assigned = len(selected)
signed_up = int(selected["signed_up"].sum())
result[group] = {
"assigned": assigned,
"signed_up": signed_up,
"rate_pct": 100 * signed_up / assigned,
}
result["difference_pp"] = result["B"]["rate_pct"] - result["A"]["rate_pct"]
return result
PY
python3 führt Python aus. Das vorbereitete run.py ruft deine Funktion auf und schreibt ihren tatsächlichen Rückgabewert; sein Argument warmup wählt diese Eingabe. Fehlende Antworten ergänzt es nicht.
python3 run.py warmup
Der Befehl speichert output/warmup.json. JSON speichert benannte Objekte und Zahlen. Das vorbereitete Anzeigeprogramm zeigt nur vorhandene Dateien; es führt dein Programm niemals erneut aus und repariert kein fehlendes Ergebnis.
python3 show_results.py warmup
Prüfe die Gruppengrößen 3 und 3, die positiven Ergebnisse 1 und 2 sowie eine Differenz nahe +33.33 Prozentpunkten. Speichere ungerundete Zahlen; gerundete Werte in dieser Erklärung dienen nur der Lesbarkeit. Eine höhere beobachtete Rate allein belegt nicht, dass B die Seite verbessert hat.
In diesem Lab müssen nur compare.py und anschließend outcomes.py geändert werden. Du darfst gewöhnliche Python-Hilfsdateien daneben ergänzen. Erhalte assignments.csv, events.csv, warmup-visitors.csv, config.json, data-dictionary.md, run.py und show_results.py. Andere Einheiten haben unabhängige Kopien und benötigen keine Dateien aus dieser VM.
Ist ein Dateiname oder Verzeichnis falsch, zeigt pwd das aktuelle Verzeichnis und ls dessen Dateien. Kehre dann mit dem obigen cd zurück. Kopiere den vollständigen Funktionsblock erneut, um einen gewöhnlichen Bearbeitungsfehler zu korrigieren; Setup muss nicht erneut ausgeführt und die Umgebung nicht zurückgesetzt werden.
Erstelle vollständige Ergebnisse über sieben Tage
In diesem Schritt wandelst du rohe Zuweisungs- und Ereignisdatensätze in die vollständige Tabelle auf Personenebene um, die der Vergleich benötigt.
Fahre im selben WebIDE-Terminal unter /home/labex/project/signup-experiment fort. Lies zuerst die Felddefinitionen, dann die ursprünglichen Tabellen:
cat data-dictionary.md
cat assignments.csv
cat events.csv
Die vollständige Tabelle enthält 12 zugewiesene Personen, sechs je Gruppe. visitor_id ist eine stabile Textidentität, keine Zeilenposition. In der Ereignistabelle kommen Personen mehrfach sowie der nicht zugehörige Besucher OUTSIDE vor. Die Tabellen dürfen daher nicht anhand der Zeilennummer gepaart werden. Das Protokoll ist für dieses Lehrbeispiel vollständig: Kein gültiges Ereignis bedeutet keine Anmeldung, nicht ein unbekanntes Ergebnis.
Jede Person hat einen Zuweisungszeitpunkt mit Zeitzone, assigned_at. Das Fenster lautet [Zuweisung, Zuweisung + sieben Tage): Der Beginn zählt, die exakte obere Grenze nicht. UTC ist eine gemeinsame Zeitreferenz; 12:00:00+02:00 und 10:00:00Z bezeichnen denselben Zeitpunkt. pd.to_datetime(..., utc=True, format="ISO8601") wandelt diese ausdrücklich formatierten Zeichenfolgen in vergleichbare UTC-Zeitstempel um. Ein Timedelta(days=7) steht für sieben verstrichene Tage.
Zum angegebenen Analysezeitpunkt müssen alle Nachbeobachtungen abgeschlossen sein:
cat config.json
In dieser Einheit endet jedes Fenster vor analysis_at. Die übrigen Einstellungen werden in späteren Labs verwendet. Ändere den Analysezeitpunkt nicht, um eine noch nicht vollständig beobachtete Person vollständig erscheinen zu lassen. Alle bereitgestellten Kurstabellen enthalten 4–14 Personen mit mindestens zwei je Gruppe. Doppelte Zuweisungs-IDs, einschließlich widersprüchlicher Gruppen, sind ungültig; die Zeile mit dem schöneren Ergebnis auszuwählen ist keine Reparatur.
Ein Left Merge geht von jeder Zuweisung aus und hängt passende Ereignisse an. how="left" erhält eine Person ohne Ereignis. validate="one_to_many" verlangt, dass jede ID höchstens einmal in der Zuweisungstabelle vorkommt, erlaubt aber mehrere Ereigniszeilen. Eine fremde Ereignis-ID kann der zugewiesenen Population keine Person hinzufügen.
Die Vergleiche erzeugen boolesche Masken. In pandas kombiniert & Bedingungen, während | mindestens eine der Bedingungen bedeutet; setze jeden Vergleich in Klammern. groupby(...).any() fragt dann, ob für jede Person irgendein gültiges Ereignis vorhanden ist. Die Rückzuordnung dieser Kennzeichen über die ID erhält eine Zeile pro Zuweisung einschließlich berechtigter Nullen. ValueError weist eine ungültige Zuweisung oder unvollständige Nachbeobachtung ausdrücklich zurück, statt Vergleichbarkeit vorzutäuschen.
cat > outcomes.py <<'PY'
import pandas as pd
def build_people(assignments, events, analysis_at):
a = assignments.copy()
if (a["visitor_id"].isna().any()
or (a["visitor_id"] == "").any()
or a["visitor_id"].duplicated().any()):
raise ValueError("Each visitor needs one unique, nonempty assignment ID.")
if not a["group"].isin(["A", "B"]).all():
raise ValueError("Only groups A and B are valid.")
a["assigned_at"] = pd.to_datetime(a["assigned_at"], utc=True, format="ISO8601")
a["window_end"] = a["assigned_at"] + pd.Timedelta(days=7)
if not (a["window_end"] <= pd.Timestamp(analysis_at)).all():
raise ValueError("Every seven-day follow-up must be complete.")
e = events.copy()
e["occurred_at"] = pd.to_datetime(e["occurred_at"], utc=True, format="ISO8601")
joined = a.merge(e, on="visitor_id", how="left", validate="one_to_many")
eligible = ((joined["kind"] == "signup")
& (joined["occurred_at"] >= joined["assigned_at"])
& (joined["occurred_at"] < joined["window_end"]))
flags = eligible.groupby(joined["visitor_id"]).any()
a["signed_up"] = a["visitor_id"].map(flags).astype(int)
return a[["visitor_id", "group", "signed_up"]]
PY
Ohne passendes Ereignis fehlen bei einem Left Merge die Ereignisfelder. Die Gültigkeitsvergleiche sind dann falsch; diese Person erhält deshalb Ergebnis 0. Damit wird keine unbekannte Zuweisung oder unvollständige Nachbeobachtung mit null aufgefüllt.
Der nächste Befehl schreibt absichtlich einen Stufennamen falsch. Er sollte invalid choice ausgeben und mit Status 2 enden. Dies ist ein Fehler im Befehlsargument; deine Daten wurden weder ungültig gemacht noch verändert.
python3 run.py pepole
Korrigiere den Stufennamen und speichere die Tabelle auf Personenebene:
python3 run.py people
python3 show_results.py people
Es müssen 12 unterschiedliche IDs vorliegen. Prüfe P-8 (zwei gültige Ereignisse, aber Ergebnis 1), Q-3 (keine Ereignisse, aber Ergebnis 0), M-90 (Anmeldung genau an der ausgeschlossenen oberen Grenze, Ergebnis 0) und K-4 (eine Sekunde vor dieser Grenze, Ergebnis 1). X-02 meldete sich vor der Zuweisung an und bleibt 0. OUTSIDE darf nicht erscheinen.
Verwende deine Vergleichsfunktion nun erneut für diese tatsächlich gespeicherte Tabelle:
python3 run.py summary
python3 show_results.py summary
Im vollständigen Experiment haben sich in A 1 von 6 und in B 5 von 6 Personen angemeldet, eine Differenz nahe +66.67 Prozentpunkten. Du hast jetzt einen begründbaren Nenner und ein begründbares Fenster, aber noch keinen Beleg dafür, dass eine neue Seite eine Verbesserung verursacht hat. Die nächsten Labs untersuchen, wie zufällige Zuweisung die beobachtete Differenz verändern kann.
Die Prüfungen lesen deine gespeicherte Arbeit und rufen deine unveränderten Funktionen nur für isolierte Kopien innerhalb desselben öffentlichen Vertrags auf. Sie umfassen unabhängig geänderte Eingabezeilenfolgen, andere IDs, Eingaben ohne Ereignisse sowie ungültige Zuweisungen/Nachbeobachtungen. Gleichwertige pandas- oder Python-Implementierungen sind willkommen, wenn build_people dieselben benannten DataFrame-Spalten und summarize die dokumentierten Objekte zurückgibt. Wiederholte Prüfungen erstellen die vorhandenen Ergebnisse nicht neu und ändern deinen Fortschritt nicht.
Zusammenfassung
Du hast sechs Karten verglichen, eine wiederverwendbare Ratenfunktion geschrieben und vollständige Siebentageergebnisse aus rohen Ereignissen erstellt. Stabile IDs, ein ausdrückliches UTC-Fenster und Nenner aus zugewiesenen Personen verhinderten, dass wiederholte Klicks und fehlende Ereignisse das Ergebnis verzerren. Die gespeicherte Personentabelle und Zusammenfassung bilden nun die Grundlage für einen ehrlichen Blick auf zufällige Variation.



