Dane do testu A/B z GA4 w BigQuery — plan przed startem
- A/B testing
- BigQuery
- GA4
- Statystyka
Kalkulator wielkości próby potrzebuje dwóch liczb z danych: ilu użytkowników wchodzi w tygodniu na testowaną stronę i ilu z nich kupuje. Najczęściej bierze się je z raportu GA4 — sesje z ostatniego tygodnia, konwersje z całej witryny — i plan rozjeżdża się z wynikiem, zanim test w ogóle ruszy. Test liczy osoby od pierwszego wejścia, a plan policzono na czymś innym.
Dane do testu A/B z GA4 w BigQuery liczysz przed startem tą samą definicją, którą po teście ocenisz wynik. Poniżej zapytanie i powód każdej decyzji w nim.
Dlaczego dane do testu A/B liczysz w BigQuery, nie w raporcie GA4
Raport GA4 odpowiada na inne pytanie. Pokazuje ruch całej witryny, a konwersję liczy na sesjach albo na wszystkich użytkownikach — bez warunku, że zakup nastąpił po wejściu na testowaną stronę. Test A/B karty produktu dotyczy węższej grupy: ludzi, którzy kartę zobaczyli, bo tylko oni mogli trafić do wariantu. Zakup sprzed tego wejścia nie zależał od wariantu, więc nie ma go w wyniku testu — i nie powinno go być w planie.
Eksport GA4 do BigQuery daje surowe zdarzenia. Na nich ustawiasz definicje sam:
- Populacja — użytkownicy, którzy wykonali zdarzenie wejścia do testu. Dla karty
produktu to
view_item; dla koszyka byłobyview_cart. - Konwersja —
purchasepo pierwszym wejściu. Zakup sprzed wejścia odpada. - Jednostka — osoba, nie sesja i nie zamówienie. Ktoś, kto kupił dwa razy, liczy się raz.
Dlaczego osoby, a nie sesje, opisałem w testach A/B dla analityków: test statystyczny zakłada niezależne obserwacje, a pięć sesji jednej osoby nimi nie jest. Te same trzy definicje stoją w zapytaniu po teście, które pokazałem przy własnych testach A/B w GTM. Plan i ocena liczą wtedy to samo — pod warunkiem, że wariant przydzielasz na tym samym zdarzeniu, od którego liczysz plan.
Pułapka: tydzień razy liczba tygodni
Najprostszy pomysł brzmi: policz jeden tydzień i pomnóż. Test potrwa cztery tygodnie, więc zbierze cztery razy tylu użytkowników. Nie zbierze.
Ludzie wracają. Ktoś, kto obejrzał kartę produktu w poniedziałek pierwszego tygodnia i wrócił w trzecim, w teście liczy się raz — od pierwszego wejścia. Unikalnych użytkowników w oknie czterech tygodni jest więc mniej niż cztery razy tyle co w jednym. Test zbiera ich wolniej, niż sugeruje tydzień.
Drugi efekt działa w przeciwną stronę. Im dłuższe okno, tym więcej czasu na zakup, więc CR liczony na unikalnych użytkownikach rośnie. Mniej użytkowników wydłuża test, wyższy CR go skraca. Który efekt wygra, zależy od sklepu — dlatego nie zgadujesz, tylko liczysz na danych, w oknie tak długim jak planowany test.
Wynik zapytania wygląda tak (liczby poglądowe):
| Okno (tygodnie) | Użytkownicy na tydzień | Konwertujący na tydzień |
|---|---|---|
| 1 | 25 000 | 375 |
| 2 | 22 500 | 383 |
| 3 | 21 000 | 392 |
| 4 | 20 000 | 400 |
| 5 | 19 200 | 403 |
„Na tydzień” to wartość z okna podzielona przez liczbę tygodni. Spada, bo powracający liczą się raz. CR rośnie z 1,5% w oknie tygodniowym do 2,1% w pięciotygodniowym.
Z pierwszego wiersza wyszłoby 100 000 użytkowników w czterech tygodniach zamiast 80 000 i CR 1,5% zamiast 2%. W tym przykładzie oba błędy prawie się znoszą: kalkulator i tak policzy 4 tygodnie, ale 49 193 użytkowników na wariant zamiast 36 693. W sklepie, w którym CR rośnie z oknem wolniej, sam zawyżony ruch skróci plan poniżej tego, czego test naprawdę potrzebuje.
Zapytanie: okna narastające na eksporcie GA4
Zapytanie liczy okna od 1 do max_weeks tygodni, wszystkie od tego samego poniedziałku.
Każdy użytkownik wchodzi do okna od pierwszego view_item, a jako konwertujący — jeśli
kupił po tym wejściu do końca okna.
-- Użytkownicy i konwertujący na tydzień
-- w oknach 1..max_weeks tygodni przed testem
DECLARE window_start DATE DEFAULT DATE '2020-11-02';
DECLARE max_weeks INT64 DEFAULT 5;
ASSERT EXTRACT(DAYOFWEEK FROM window_start) = 2
AS 'window_start musi być poniedziałkiem';
ASSERT max_weeks BETWEEN 1 AND 8
AS 'max_weeks: od 1 do 8 tygodni';
WITH events AS (
SELECT
user_pseudo_id,
event_name,
event_timestamp,
DIV(DATE_DIFF(
PARSE_DATE('%Y%m%d', event_date),
window_start, DAY), 7) + 1 AS week_no
FROM `projekt.analytics_123456.events_*`
-- _TABLE_SUFFIX ogranicza skanowane dni i koszt
WHERE _TABLE_SUFFIX BETWEEN
FORMAT_DATE('%Y%m%d', window_start)
AND FORMAT_DATE('%Y%m%d', DATE_ADD(
window_start, INTERVAL max_weeks * 7 - 1 DAY))
AND event_name IN ('view_item', 'purchase')
),
-- pierwsze wejście na kartę produktu
first_view AS (
SELECT
user_pseudo_id,
MIN(event_timestamp) AS first_view_ts,
MIN(week_no) AS first_week
FROM events
WHERE event_name = 'view_item'
GROUP BY user_pseudo_id
),
-- tydzień pierwszego zakupu PO wejściu
first_purchase AS (
SELECT
f.user_pseudo_id,
MIN(p.week_no) AS purchase_week
FROM first_view AS f
JOIN events AS p
ON p.user_pseudo_id = f.user_pseudo_id
AND p.event_name = 'purchase'
AND p.event_timestamp > f.first_view_ts
GROUP BY f.user_pseudo_id
),
-- okno k: weszli w tyg. 1..k, kupili do końca k
windows AS (
SELECT
w AS weeks,
COUNTIF(f.first_week <= w) AS users_unique,
COUNTIF(f.first_week <= w
AND fp.purchase_week <= w) AS converters
FROM UNNEST(GENERATE_ARRAY(1, max_weeks)) AS w
CROSS JOIN first_view AS f
LEFT JOIN first_purchase AS fp
ON fp.user_pseudo_id = f.user_pseudo_id
GROUP BY w
)
SELECT
weeks,
CAST(ROUND(users_unique / weeks) AS INT64)
AS users_per_week,
CAST(ROUND(converters / weeks) AS INT64)
AS converters_per_week
FROM windows
ORDER BY weeks;
Kilka decyzji, które łatwo przeoczyć:
- Pełne tygodnie od poniedziałku. W poniedziałek i w sobotę kupują inni ludzie, więc
każde okno ma pełne tygodnie, czyli każdy dzień tygodnia tyle samo razy. Poniedziałek
dlatego, że test startuje w poniedziałek, a okna mają kończyć się dzień przed nim.
ASSERTzatrzymuje zapytanie, gdywindow_startwypada w inny dzień. DrugiASSERTpilnuje, żebymax_weeksmieściło się między 1 a 8 — dłuższego testu kalkulator nie dopuszcza. - Okno kończy się przed testem. Dane z okresu testu mieszają ruch z wariantem B. Bierzesz tygodnie bezpośrednio przed planowanym startem, bez bieżącego, niepełnego tygodnia.
purchase_weekto tydzień pierwszego zakupu. Gdy ktoś kupił w tygodniu 2 i 4, w oknie trzytygodniowym jest konwertującym. W dwutygodniowym też. W jednotygodniowym — nie.CROSS JOINz tablicą tygodni liczy wszystkie okna jednym przebiegiem, bez pętli i bez pięciu osobnych zapytań.
Na własnym projekcie podmieniasz tylko nazwę tabeli. Bez własnego eksportu wstaw w miejsce
projekt.analytics_123456.events_* publiczny
bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_* (dane z 1 listopada 2020
do 31 stycznia 2021). Domyślne daty pasują do tego zbioru.
Jak przenieść wynik do kalkulatora
Wiersz wybierasz po planowanej długości testu, a długość testu wynika z kalkulatora. To pętla, ale krótka:
- Planujesz 5 tygodni, więc bierzesz wiersz 5 i wpisujesz 19 200 w „Użytkownicy w tygodniu” oraz 403 w „Konwertujący w tygodniu”.
- Kalkulator wielkości próby przy MDE +15%, 95% ufności, 80% mocy, teście dwustronnym i 2 wariantach liczy 3,64 tygodnia, czyli 4 pełne tygodnie.
- Bierzesz wiersz 4: 20 000 i 400. Kalkulator liczy 3,67 tygodnia, czyli znowu 4.
- Wiersz i wynik się zgadzają — plan gotowy.
Zwykle wystarczy jedna albo dwie iteracje. Gdy wynik skacze między dwoma wierszami, weź dłuższy: test, który trwa tydzień za długo, kosztuje mniej niż test o zbyt małej mocy.
Przychód na tydzień — żeby MDE miało cenę w złotówkach
MDE to najmniejszy wzrost, który test ma wykryć. Biznesowi trudno ocenić „+14%”, łatwiej „+18 tys. zł tygodniowo” (przy przychodzie 125 tys. zł na tydzień). Kalkulator przeliczy wykrywalne MDE na złotówki, jeśli w polu „Tygodniowy przychód ze strony testowej” dostanie przychód z tej samej populacji:
-- Przychód na tydzień od tych, którzy weszli
-- na kartę produktu: 4 pełne tygodnie przed testem
DECLARE window_end DATE DEFAULT DATE '2020-12-06';
DECLARE window_weeks INT64 DEFAULT 4;
WITH events AS (
SELECT
user_pseudo_id,
event_name,
event_timestamp,
ecommerce.purchase_revenue AS revenue
FROM `projekt.analytics_123456.events_*`
WHERE _TABLE_SUFFIX BETWEEN
FORMAT_DATE('%Y%m%d', DATE_SUB(
window_end, INTERVAL window_weeks * 7 - 1 DAY))
AND FORMAT_DATE('%Y%m%d', window_end)
AND event_name IN ('view_item', 'purchase')
),
first_view AS (
SELECT
user_pseudo_id,
MIN(event_timestamp) AS first_view_ts
FROM events
WHERE event_name = 'view_item'
GROUP BY user_pseudo_id
)
SELECT
CAST(ROUND(IFNULL(SUM(p.revenue), 0)
/ window_weeks) AS INT64) AS weekly_revenue
FROM events AS p
JOIN first_view AS f
ON f.user_pseudo_id = p.user_pseudo_id
AND p.event_timestamp > f.first_view_ts
WHERE p.event_name = 'purchase';
Cztery pełne tygodnie zamiast 30 dni, bo każdy dzień tygodnia wchodzi tyle samo razy.
Tydzień zamiast miesiąca, bo w tej samej jednostce kalkulator dostaje ruch. Na publicznym
eksporcie użyj purchase_revenue_in_usd — sklep demo raportuje w dolarach.
Kalkulator mnoży przychód tygodniowy przez wykrywalne MDE i pokazuje kwotę obok każdego tygodnia testu. To założenie, że przychód na kupującego się nie zmieni — test mierzy CR, nie przychód.
Czego to zapytanie nie załatwi
user_pseudo_idto przeglądarka, nie osoba. Ten sam człowiek na telefonie i laptopie to dwóch użytkowników, a wyczyszczone cookies robią z niego nowego. Test liczy tak samo, więc plan i wynik są spójne — ale żaden nie liczy ludzi.- Bez zgody nie ma danych. W podstawowym trybie Consent Mode użytkownicy bez zgody na analitykę nie trafiają do eksportu. Plan i test widzą tę samą populację, więc porównanie się trzyma. Przychód tygodniowy z eksportu jest za to niższy niż w systemie sprzedaży, więc kwota w kalkulatorze jest raczej zaniżona niż zawyżona.
- Zdublowany
purchasezawyża przychód. Tag potrafi wysłać zakup dwa razy, np. po odświeżeniu strony podziękowania. Raporty GA4 usuwają takie duplikaty potransaction_id, eksport do BigQuery ich nie usuwa. Konwertujących to nie zmienia, bo liczysz osoby. Przychód — tak. Jeśli widzisz duplikaty, licz przychód po unikalnymecommerce.transaction_id. - Segmenty trzeba zaplanować osobno. Plan dla samych nowych użytkowników wymaga
filtra w
first_view, statusu z chwili pierwszego wejścia i osobnego przebiegu kalkulatora. Segment dopisany po obejrzeniu wyniku to szukanie wzrostu, nie analiza. - Sezonowość zostaje. Okno przed Black Friday nie powie nic o ruchu w Black Friday. Gdy test ma objąć nietypowy okres, licz okno z analogicznego okresu rok wcześniej.
Kalkulator robi statystykę. Jakość planu zależy od tego, czy liczby, które do niego wpisujesz, mierzą to samo co test. Godzina nad SQL-em przed startem oszczędza miesiąc testu, który nic nie rozstrzygnie.