ΜΕΡΟΣ Α

TODO 1: Δημιούργησε ένα tibble «experiment» με στήλες:

# --- Παράμετροι πειράματος ---
n_control   <- 8000      # μέγεθος ομάδας ελέγχου
n_treatment <- 8000      # μέγεθος πειραματικής ομάδας
p_control   <- 0.08      # baseline conversion rate
p_treatment <- 0.10      # μετά την αλλαγή (true effect = +2%)

set.seed(42)
  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))
  )

  glimpse(experiment)
## Rows: 16,000
## Columns: 3
## $ user_id   <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 1…
## $ group     <chr> "control", "control", "control", "control", "control", "cont…
## $ converted <int> 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, …

TODO 2: Υπολόγισε ανά ομάδα: n, conversions, conversion_rate, standard error (se) και 95% CI (ci_lower, ci_upper)

    summary_stats <- experiment |>
      group_by(group) |>
      summarise(
        n         = n(),
        conversions    = sum(converted),
        conversion_rate       = (conversions / n) * 100,
        cvr = mean(converted),
        se        = sqrt(cvr * (1 - cvr) / n),
        ci_lower  = cvr - 1.96 * se,
        ci_upper  = cvr + 1.96 * se
      )
    
    print(summary_stats)
## # A tibble: 2 × 8
##   group         n conversions conversion_rate    cvr      se ci_lower ci_upper
##   <chr>     <int>       <int>           <dbl>  <dbl>   <dbl>    <dbl>    <dbl>
## 1 control    8000         677            8.46 0.0846 0.00311   0.0785   0.0907
## 2 treatment  8000         764            9.55 0.0955 0.00329   0.0891   0.102

TODO 3: Οπτικοποίησε με ggplot2 (geom_col + geom_errorbar)

  ggplot(summary_stats, aes(x = group, y = cvr, fill = group)) +
  geom_col(width = 0.5, alpha = 0.85) +
  geom_errorbar(aes(ymin = ci_lower, ymax = ci_upper),
                width = 0.15, linewidth = 0.8) +
  geom_text(aes(label = percent(cvr, accuracy = 0.1)),
            vjust = -1.5, size = 5, fontface = "bold") +
  scale_y_continuous(labels = percent, limits = c(0, 0.16)) +
  scale_fill_manual(values = c("control (psa)" = "#6b7280",
                               "treatment (ads)" = "#3b82f6")) +
  labs(
    title    = "A/B Test: Πιθανότητα Conversion ανά ομάδα",
    subtitle = "Με 95% διαστήματα εμπιστοσύνης",
    x = NULL, y = "Conversion Rate Probability "
  ) +
  theme_minimal(base_size = 13) +
  theme(legend.position = "none")

Παρατηρούμε πως τα διαστήματα εμπιστοσύνης έχουν πολύ μικρή επικάλυψη {0.10 μονάδες}.

Control CI ->[0.0785, 0.0907] Treatment CI -> [0.0891,0.102]

Είμαστε 95% σίγουροι πως η πιθανότητα conversion οταν ένας χρήστης βλέπει τις διαφημίσεις είναι 8.91%-10.20%

TODO 4: Διεξήγαγε έλεγχο υποθέσεων με prop.test()

  conversions   <- c(summary_stats$conversions[1], summary_stats$conversions[2])
  visitors <- c(summary_stats$n[1],      summary_stats$n[2])
  
  test_result <- prop.test(
    x           = conversions,
    n           = visitors,
    conf.level  = 0.95,
    correct     = FALSE   # χωρίς Yates correction (ταιριάζει με τον τύπο μας)
  )
  
   print(test_result)
## 
##  2-sample test for equality of proportions without continuity correction
## 
## data:  conversions out of visitors
## X-squared = 5.7725, df = 1, p-value = 0.01628
## alternative hypothesis: two.sided
## 95 percent confidence interval:
##  -0.019744875 -0.002005125
## sample estimates:
##   prop 1   prop 2 
## 0.084625 0.095500

To p value < 0.05 σημαινει οτι απορρίπτουμε την μηδενική υπόθεση (H0) και συμπεραίνουμε οτι ειναι στατιστικά σημαντική συσχέτιση ( ο ρόλος των διαφημίσεων στα conversions). Το prop 2 δείχνει οτι η ομάδα treatment (που είδε τις διαφημίσεις) έφερε περισσότερα leads.

Το p value μας δείχνει πως το να βλέπαμε αυτα τα αποτελέσματα αν οι δύο ομάδες ήταν ίδιες ειναι μόλις 0.01628%.

TODO 5: Χειρωνακτική επαλήθευση — υπολόγισε:

  • (α) pooled estimate p̂_pool
  • (β) pooled SE
  • (γ) δ = p_treatment - p_control
  • (δ) 95% CI για τη διαφορά δ
  • Συγκρίνετε με το CI του prop.test()
  # 1. Pooled estimate
  p_pool <- sum(conversions) / sum(visitors)
  
  # 2. Pooled standard error
  se_pool <- sqrt(p_pool * (1 - p_pool) *
                    (1/visitors[1] + 1/visitors[2]))
  
  # 3. Διαφορά και διάστημα εμπιστοσύνης
  delta  <- summary_stats$cvr[2] - summary_stats$cvr[1]
  m      <- 1.96 * se_pool
  
  cat(sprintf("Pooled p̂ = %.4f\n", p_pool))
## Pooled p̂ = 0.0901
  cat(sprintf("Pooled SE = %.4f\n", se_pool))
## Pooled SE = 0.0045
  cat(sprintf("δ = %.4f\n", delta))
## δ = 0.0109
  cat(sprintf("95%% CI for δ: [%.4f, %.4f]\n", delta - m, delta + m))
## 95% CI for δ: [0.0020, 0.0197]
  # Επιχειρηματική απόφαση
  delta_min <- 0.01   # κατώφλι επιχειρηματικής ουσίας
  
  if (delta - m > delta_min) {
    cat("✅ ΣΥΜΠΕΡΑΣΜΑ: Υλοποιήστε την αλλαγή!\n")
  } else {
    cat("⚠️ ΣΥΜΠΕΡΑΣΜΑ: Στατιστικά σημαντικό αλλά χωρίς επιχειρηματική ουσία.\n")
  }
## ⚠️ ΣΥΜΠΕΡΑΣΜΑ: Στατιστικά σημαντικό αλλά χωρίς επιχειρηματική ουσία.

Τα διαστήματα εμπιστοσύνης μεταξύ των 2 τρόπων είναι ακριβώς τα ίδια, ωστόσο στο prop.test η R κάνει prop [1] - prop [2] για αυτό βγήκε αρνητικό το διάστημα εμπιστοσύνης (η Ομάδα 2 έχει μεγαλύερο σκορ).

Η διαφορά μας αφού τοποθετήθηκαν οι διαφημίσεις είναι αύξηση 1.73% στα conversions. Απο επιχειρηματικής πλευράς μειώνεται το user experience και δεν είναι μεγάλη η αύξηση για να βάζουμε τόσες διαφημίσεις. Επίσης θα φύγουν κάποιοι χρήστες εάν βλέπουν τόσες πολλες διαφημίσεις την ημέρα (μερικοί είδαν παραπάνω απο 20 σε μια μέρα).

TODO 6: Power Analysis — υπολόγισε με pwr.2p.test()

Πόσο δείγμα χρειαζόταν πραγματικά για power = 80% και α = 0.05;

# h = Cohen's effect size για δύο αναλογίες
effect_size <- ES.h(p1 = 0.08, p2 = 0.10)
cat(sprintf("Cohen's h = %.4f\n", effect_size))
## Cohen's h = -0.0700
# Υπολογισμός απαιτούμενου μεγέθους δείγματος ανά ομάδα
sample_size_calc <- pwr.2p.test(
  h           = effect_size,
  sig.level   = 0.05,
  power       = 0.80,
  alternative = "two.sided"
)

print(sample_size_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

Το πλήθος n που χρειαζόμαστε είναι 3204 ανα ομάδα ενω εμεις εχουμε 16.000 σύνολο (control + experiment).

ΜΕΡΟΣ Β — Real-World A/B Analysis

TODO 7: Invariants check — έλεγξε αν η τυχαιοποίηση δούλεψε. (Α) Ποια η αναλογία ad/psa; Είναι 50/50;

  1. (β) Οπτικοποίησε την κατανομή ανά ημέρα εβδομάδας (most_ads_day), για κάθε ομάδα — ψάξε ανισορροπίες
ads <- read_csv("data.csv") |>
  janitor::clean_names() |>
  rename(user_id = user_id) |>
  mutate(
    group     = factor(test_group, levels = c("psa", "ad")),
    converted = as.integer(converted)
  )
  # 1. Αναλογία στις ομάδες
  ads |>
    count(group) |>
    mutate(pct = n / sum(n))
## # A tibble: 2 × 3
##   group      n    pct
##   <fct>  <int>  <dbl>
## 1 psa    23524 0.0400
## 2 ad    564577 0.960
  # 2. Κατανομή ανά ημέρα εβδομάδας — πρέπει να είναι παρόμοια
  
  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") +
    scale_y_continuous(labels = percent) +
    labs(title = "Invariant check: κατανομή ανά ημέρα",
         x = NULL, y = "% της ομάδας") +
    theme_minimal()

Παρατηρούμε πως η ομάδα psa αποτελεί μόνο το 4% του συνολικού δείγματος. Είναι ανισόρροπες ομάδες. Υπάρχουν ανισσοροπίες κάθε Τετάρτή, Πέμπτη, Σάββατο και Κυριακή,

TODO 8: Υπολόγισε conversion rate, SE, 95% CI ανά ομάδα

  ads_summary <- ads |>
  group_by(group) |>
  summarise(
    n              = n(),
    conversions    = sum(converted),
    conversion_rate = mean(converted),
    cvr = mean(converted) * 100, 
    
    se             = sqrt(conversion_rate * (1 - conversion_rate) / n)
  ) |>
  mutate(
    ci_lower = conversion_rate - 1.96 * se,
    ci_upper = conversion_rate + 1.96 * se
  )

print(ads_summary)
## # A tibble: 2 × 8
##   group      n conversions conversion_rate   cvr       se ci_lower ci_upper
##   <fct>  <int>       <int>           <dbl> <dbl>    <dbl>    <dbl>    <dbl>
## 1 psa    23524         420          0.0179  1.79 0.000863   0.0162   0.0195
## 2 ad    564577       14423          0.0255  2.55 0.000210   0.0251   0.0260

Βήμα B4 — Στατιστικός έλεγχος + μορφοποιημένο output με broom

  test <- prop.test(
    x = ads_summary$conversions,
    n = ads_summary$n,
    correct = FALSE
  )
  
  # tidy output — πολύ καλύτερο από το σκέτο print()
  broom::tidy(test) |>
    select(estimate1, estimate2, statistic, p.value,
           conf.low, conf.high)
## # A tibble: 1 × 6
##   estimate1 estimate2 statistic  p.value conf.low conf.high
##       <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
## 1    0.0179    0.0255      54.3 1.71e-13 -0.00943  -0.00595
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),
    .groups = "drop"
  )



ggplot(ads_by_day, aes(x = most_ads_day,
                       y = conversion_rate,
                       color = group, group = group)) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 3) +
  geom_ribbon(aes(ymin = conversion_rate - 1.96 * se,
                  ymax = conversion_rate + 1.96 * se,
                  fill = group),
              alpha = 0.15, color = NA) +
  scale_y_continuous(labels = percent_format(accuracy = 0.1)) +
  scale_color_manual(values = c("psa" = "#6b7280", "ad" = "#3b82f6")) +
  scale_fill_manual(values = c("psa" = "#6b7280", "ad" = "#3b82f6")) +
  labs(title = "Conversion rate ανά ημέρα εβδομάδας",
       subtitle = "Με 95% διαστήματα εμπιστοσύνης",
       x = NULL, y = "Conversion rate") +
  theme_minimal(base_size = 13)

Παρατηρούμε αισθητά οτι το group που βλέπει διαφημίσεις αποφέρει περισσότερα conversions. H Δευτέρα είναι η καλύτερη μέρα για να τρέξουμε διαφημίσεις.

TODO 10 (BONUS): Επιχειρηματική απόφαση

  1. Όρισε δ_min = 0.005 (κατώφλι επιχειρηματικής ουσίας)
  2. Υπολόγισε το absolute lift και το relative lift
  3. Ποια από τις 6 περιπτώσεις CI ισχύει; (A/B/C/D/E/F)
  4. Ποια η τελική σύστασή σου;
# CI για τη διαφορά (lift)
conf_int <- broom::tidy(test)
# Σωστοί υπολογισμοί βασισμένοι στο Experiment (estimate2) έναντι του Control (estimate1)
abs_lift <- (conf_int$estimate2 - conf_int$estimate1) * 100
lift_pct <- (conf_int$estimate2 - conf_int$estimate1) / conf_int$estimate1 * 100
delta_min <- 0.005   # κατώφλι επιχειρηματικής ουσίας
delta <- (conf_int$estimate2 - conf_int$estimate1) 

# Ορισμός των σωστών ορίων μετά την αντιστροφή των προσήμων
ci_lower_positive <- abs(conf_int$conf.high)
ci_upper_positive <- abs(conf_int$conf.low)

cat(sprintf("Absolute lift: %.2f ποσοστ. μονάδες\n", abs_lift))
## Absolute lift: 0.77 ποσοστ. μονάδες
cat(sprintf("Relative lift: %+.1f%%\n", lift_pct))
## Relative lift: +43.1%
# Μετατρέπουμε τα όρια σε θετικά (απόλυτες τιμές) για να συμφωνούν με το Experiment - Control
cat(sprintf("95%% CI for difference: [%.4f, %.4f]\n",
            abs(conf_int$conf.high), abs(conf_int$conf.low)))
## 95% CI for difference: [0.0060, 0.0094]
if (ci_lower_positive > delta_min) {
  cat("✅ ΣΥΜΠΕΡΑΣΜΑ: Υλοποιήστε την αλλαγή!\n")
} else {
  cat("⚠️ ΣΥΜΠΕΡΑΣΜΑ: Στατιστικά σημαντικό αλλά χωρίς επιχειρηματική ουσία.\n")
}
## ✅ ΣΥΜΠΕΡΑΣΜΑ: Υλοποιήστε την αλλαγή!

Ερμηνεία relative lift/absolute lift:

Βελτιώθηκε κατά \(+43\%\) το conversion rate, απο το experiment group. Αξίζει επιχειρηματικά η αλλάγη επειδή το κατώφλι της επιχείρησης ήθελε ελάχιστη αύξηση 0.5% και εμείς έχουμε 0.77%.

Δηλαδή αν είχαμε 100 conversions με 1000 χρήστες , με το experiment group θα είχαμε 177 conversions απο 1000 χρήστες.

** Κάναμε estimate 2 - estimate 1 και βγήκαν τα νούμερα θετικά **