Πριν αναλύσουμε τα πραγματικά δεδομένα, δημιουργούμε ένα ελεγχόμενο πείραμα προσομοίωσης (simulation) για να επαληθεύσουμε τις στατιστικές μας μεθόδους.
# --- Παράμετροι πειράματος ---
n_control <- 8000 # μέγεθος ομάδας ελέγχου
n_treatment <- 8000 # μέγεθος πειραματικής ομάδας
p_control <- 0.08 # baseline conversion rate
p_treatment <- 0.10 # μετά την αλλαγή (true effect = +2%)
# TODO 1: Simulation
experiment <- tibble(
user_id = 1:(n_control + n_treatment),
group = c(rep("control", n_control), rep("treatment", n_treatment)),
converted = c(rbinom(n_control, 1, p_control), rbinom(n_treatment, 1, p_treatment))
)
# TODO 2: summary_stats
summary_stats <- experiment |>
group_by(group) |>
summarise(
n = n(),
conversions = sum(converted),
conversion_rate = mean(converted),
se = sqrt(conversion_rate * (1 - conversion_rate) / n),
ci_lower = conversion_rate - 1.96 * se,
ci_upper = conversion_rate + 1.96 * se
)
knitr::kable(summary_stats, caption = "Συνοπτικά Στατιστικά Προσομοίωσης")| group | n | conversions | conversion_rate | se | ci_lower | ci_upper |
|---|---|---|---|---|---|---|
| control | 8000 | 643 | 0.080375 | 0.0030396 | 0.0744173 | 0.0863327 |
| treatment | 8000 | 798 | 0.099750 | 0.0033504 | 0.0931833 | 0.1063167 |
ggplot(summary_stats, aes(x = group, y = conversion_rate, fill = group)) +
geom_col(width = 0.5, alpha = 0.85) +
geom_errorbar(aes(ymin = ci_lower, ymax = ci_upper), width = 0.15, linewidth = 0.7, color = "#E5E9F0") +
scale_y_continuous(labels = percent_format(accuracy = 0.1)) +
scale_fill_manual(values = c("control" = "#4C566A", "treatment" = "#81A1C1")) +
labs(title = "Simulated A/B Test Results", x = "Ομάδα", y = "Conversion Rate (%)") +
theme_minimal()# TODO 4: Έλεγχος Υποθέσεων με prop.test
test_result <- prop.test(x = summary_stats$conversions, n = summary_stats$n, correct = FALSE)
print(test_result)##
## 2-sample test for equality of proportions without continuity correction
##
## data: summary_stats$conversions out of summary_stats$n
## X-squared = 18.323, df = 1, p-value = 1.865e-05
## alternative hypothesis: two.sided
## 95 percent confidence interval:
## -0.02824139 -0.01050861
## sample estimates:
## prop 1 prop 2
## 0.080375 0.099750
# TODO 5: Χειρωνακτική Επαλήθευση
p_pool <- sum(summary_stats$conversions) / sum(summary_stats$n)
se_pool <- sqrt(p_pool * (1 - p_pool) * (1/n_control + 1/n_treatment))
delta <- summary_stats$conversion_rate[2] - summary_stats$conversion_rate[1]
manual_ci <- c(delta - 1.96 * se_pool, delta + 1.96 * se_pool)
cat(sprintf("Χειρωνακτικό Delta (p_treatment - p_control): %.4f\n", delta))## Χειρωνακτικό Delta (p_treatment - p_control): 0.0194
## Χειρωνακτικό 95% CI της διαφοράς: [0.0105, 0.0282]
h_val <- ES.h(p1 = 0.10, p2 = 0.08)
pwr_calc <- pwr.2p.test(h = h_val, sig.level = 0.05, power = 0.8, alternative = "two.sided")
print(pwr_calc)##
## Difference of proportion power calculation for binomial distribution (arcsine transformation)
##
## h = 0.069988
## n = 3204.715
## sig.level = 0.05
## power = 0.8
## alternative = two.sided
##
## NOTE: same sample sizes
Ελέγχουμε αν η κατανομή των χρηστών έγινε σωστά χωρίς μεροληψία, παρά την εμφανή ανισορροπία μεγέθους (96% vs 4%).
# (α) Αναλογία ad/psa στο δείγμα
ads |>
count(group) |>
mutate(percentage = n / sum(n)) |>
knitr::kable(caption = "Κατανομή Δείγματος ανά Ομάδα")| group | n | percentage |
|---|---|---|
| psa | 23524 | 0.0399999 |
| ad | 564577 | 0.9600001 |
# (β) Οπτικοποίηση κατανομής ανά ημέρα της εβδομάδας
ads |>
count(group, most_ads_day) |>
group_by(group) |>
mutate(pct = n / sum(n)) |>
ggplot(aes(x = most_ads_day, y = pct, fill = group)) +
geom_col(position = "dodge", alpha = 0.85) +
scale_y_continuous(labels = percent) +
scale_fill_manual(values = c("psa" = "#4C566A", "ad" = "#88C0D0")) +
labs(title = "Invariant Check: Distribution of Users Across Days", x = "Ημέρα", y = "Ποσοστό (%) Ομάδας") +
theme_minimal()Παρόλο που οι ομάδες είναι ανισομεγέθεις (κάτι σύνηθες σε πραγματικά ad campaigns), η ποσοστιαία κατανομή των χρηστών ανά ημέρα της εβδομάδας είναι σχεδόν ταυτόσημη και για τις δύο ομάδες (π.χ. η Παρασκευή παρουσιάζει τη μέγιστη έκθεση και στις δύο). Αυτό αποδεικνύει ότι η τυχαιοποίηση λειτούργησε ορθά.
Υπολογίζουμε τα ποσοστά μετατροπής και τρέχουμε τον στατιστικό έλεγχο.
real_summary <- ads |>
group_by(group) |>
summarise(
n = n(),
conversions = sum(converted),
conversion_rate = mean(converted),
se = sqrt(conversion_rate * (1 - conversion_rate) / n)
)
knitr::kable(real_summary, caption = "Στατιστικά Πραγματικών Δεδομένων")| group | n | conversions | conversion_rate | se |
|---|---|---|---|---|
| psa | 23524 | 420 | 0.0178541 | 0.0008634 |
| ad | 564577 | 14423 | 0.0255466 | 0.0002100 |
# χρησιμοποιούμε τη rev() για να βγει πρώτο το ad και να έχουμε θετικό lift
real_test <- prop.test(x = rev(real_summary$conversions), n = rev(real_summary$n), correct = FALSE)
broom::tidy(real_test)## # A tibble: 1 × 9
## estimate1 estimate2 statistic p.value parameter conf.low conf.high method
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 0.0255 0.0179 54.3 1.71e-13 1 0.00595 0.00943 2-sample …
## # ℹ 1 more variable: alternative <chr>
ads_by_day <- ads |>
group_by(most_ads_day, group) |>
summarise(
n = n(),
conversion_rate = mean(converted),
se = sqrt(conversion_rate * (1 - conversion_rate) / n),
ci_lower = conversion_rate - 1.96 * se,
ci_upper = conversion_rate + 1.96 * se,
.groups = "drop"
)
ggplot(ads_by_day, aes(x = most_ads_day, y = conversion_rate, color = group, group = group)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
geom_ribbon(aes(ymin = ci_lower, ymax = ci_upper, fill = group), alpha = 0.15, color = NA) +
scale_y_continuous(labels = percent_format(accuracy = 0.1)) +
scale_color_manual(values = c("psa" = "#4C566A", "ad" = "#88C0D0")) +
scale_fill_manual(values = c("psa" = "#4C566A", "ad" = "#88C0D0")) +
labs(title = "Conversion Rate per Day with 95% Confidence Ribbons", x = "Ημέρα", y = "Conversion Rate") +
theme_minimal()Η Δευτέρα παρουσιάζει το υψηλότερο conversion rate για την ομάδα “ad”. Τα ribbons των διαστημάτων εμπιστοσύνης δεν επικαλύπτονται σε καμία ημέρα, γεγονός που υποδηλώνει σταθερή και σημαντική υπεροχή της διαφήμισης καθ’ όλη τη διάρκεια της εβδομάδας.
p_psa <- real_summary$conversion_rate[1]
p_ad <- real_summary$conversion_rate[2]
abs_lift <- p_ad - p_psa
rel_lift <- abs_lift / p_psa
cat(sprintf("Absolute Lift: %+.4f (%+.2f ποσοστιαίες μονάδες)\n", abs_lift, abs_lift * 100))## Absolute Lift: +0.0077 (+0.77 ποσοστιαίες μονάδες)
## Relative Lift: +43.09%
Το p-value θα είναι \(1.71 \times 10^{-13}\) (ή γραμμένο στην R ως < 2.2e-16, δηλαδή ουσιαστικά μηδενικό). Εφόσον \(p\text{-value} < 0.05\), απορρίπτουμε κατηγορηματικά τη μηδενική υπόθεση (\(H_0\)). Η διαφορά στα ποσοστά μετατροπής (Conversion Rates) είναι στατιστικά ακραία σημαντική.
Οι απόλυτες τιμές και το εύρος των διαστημάτων συμπίπτουν σχεδόν απόλυτα επειδή και οι δύο μέθοδοι βασίζονται στην προσέγγιση της Κανονικής Κατανομής (z = 1.96). Ωστόσο, παρατηρείται διαφορά στα πρόσημα:
Η prop.test() λαμβάνει τα επίπεδα του factor αλφαβητικά, εκτελώντας την αφαίρεση \(p_{control} - p_{treatment}\), με αποτέλεσμα να παράγει αρνητικό διάστημα.
Η χειρωνακτική μέθοδος εκτελεί την αφαίρεση \(p_{treatment} - p_{control}\) για να αποτυπώσει απευθείας το θετικό lift της διαφήμισης, παράγοντας θετικό διάστημα.
Απαιτούμενο δείγμα: Βάσει του Power Analysis, χρειαζόμασταν περίπου 3.205 άτομα ανά ομάδα (σύνολο ~6.410) για να ανιχνεύσουμε με ασφάλεια μια διαφορά της τάξης του +2%.
Πραγματικό δείγμα: Τρέξαμε το πείραμα σε 588.101 άτομα (23.524 στην psa και 564.577 στην ad).
Συνοψίζοντας, το πείραμα είναι ακραία overpowered. Αυτό σημαίνει ότι έχουμε στατιστική ισχύ που αγγίζει το 100%, επιτρέποντάς μας να ανιχνεύσουμε ακόμα και απειροελάχιστες διαφορές με απόλυτη βεβαιότητα. Επιχειρηματικά, η εταιρεία θα μπορούσε να είχε ολοκληρώσει το τεστ πολύ νωρίτερα, εξοικονομώντας πολύτιμο χρόνο και διαφημιστικό budget.
Η έκθεση στη διαφήμιση (“ad”) αύξησε το Conversion Rate από 1.78% σε 2.55%, επιφέροντας ένα εντυπωσιακό Relative Lift +43.1%.
Το διάστημα εμπιστοσύνης της διαφοράς βρίσκεται εξ ολοκλήρου πάνω από το κατώφλι επιχειρηματικής ουσίας (\(\delta_{min} = 0.005\)). Επομένως, ισχύει η Περίπτωση Α (Στατιστικά σημαντικό και επιχειρηματικά ουσιώδες αποτέλεσμα).
Σύσταση: Full Rollout. Η νέα καμπάνια είναι εξαιρετικά αποδοτική και προτείνεται η άμεση και καθολική εφαρμογή της στην πλατφόρμα.