Εισαγωγικά

Στόχοι & Μέθοδος


🎯 Επιχειρηματικός Στόχος

Ο πρωτεύων στόχος αυτού του Case Study είναι η αξιολόγηση μιας νέας διαφημιστικής καμπάνιας για μια fintech startup. Καλούμαστε να αποφανθούμε με στατιστική αυστηρότητα αν η νέα διαφήμιση αυξάνει πραγματικά και ουσιαστικά τις μετατροπές (conversions) των χρηστών ή αν η όποια διαφορά οφείλεται στην τύχη, παραδίδοντας μια τεκμηριωμένη επιχειρηματική σύσταση.


⚙️ Μεθοδολογία Ανάλυσης

Η προσέγγιση δομήθηκε σε δύο διακριτά μέρη, συνδυάζοντας τη θεωρητική επαλήθευση με την ανάλυση πραγματικών δεδομένων μεγάλης κλίμακας:

Μέρος Α: Προσομοίωση και Στατιστική Επαλήθευση

  • Δημιουργία Ελεγχόμενου Περιβάλλοντος

  • Μαθηματικό Sanity Check

  • Ανάλυση Ισχύος (Power Analysis)

Μέρος Β: Ανάλυση Πραγματικών Δεδομένων (Kaggle Marketing AB)

  • Έλεγχος Αξιοπιστίας Δείγαμτος

  • Ανάλυση Conversion Rates & Έλεγχος Υποθέσεων

  • Τμηματοποίηση (Segmentation)

  • Επιχειρηματική Ουσία (Practical Significance)

Dataset Walkthrough

Σε πρώτη φάση, ας δούμε τι είδους μεταβλητές περιλαμβάνει το Dataset, τι αναπαριστούν αλλά και τι τιμές λαμβάνουν:

# 1. Δημιουργία πίνακα με τη δομή των μεταβλητών (Data Dictionary)
data_dictionary <- tibble(
  "Όνομα Μεταβλητής" = c("user_id", "test_group", "converted", "total_ads", "most_ads_day", "most_ads_hour"),
  "Τύπος Δεδομένων" = c("Numeric (Integer)", "Character / Factor", "Logical (0/1)", "Numeric (Integer)", "Character / Factor", "Numeric (Integer)"),
  "Περιγραφή & Επιχειρηματική Σημασία" = c(
    "Μοναδικό αναγνωριστικό για κάθε πελάτη/χρήστη.",
    "Η ομάδα του πειράματος: 'ad' (διαφήμιση) ή 'psa' (έλεγχος).",
    "Δείκτης μετατροπής: 1 αν ο χρήστης αγόρασε/εγγράφηκε, 0 αν όχι.",
    "Ο συνολικός αριθμός διαφημίσεων που είδε ο συγκεκριμένος χρήστης.",
    "Η ημέρα της εβδομάδας που ο χρήστης είδε τις περισσότερες διαφημίσεις.",
    "Η ώρα της ημέρας (00:00 - 23:00) με τη μέγιστη έκθεση σε διαφημίσεις."
  )
)

knitr::kable(data_dictionary, 
             caption = "Λεξικό Δεδομένων (Data Dictionary)", 
             align = "l") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                font_size = 13,                                   
                full_width = TRUE)
Λεξικό Δεδομένων (Data Dictionary)
Όνομα Μεταβλητής Τύπος Δεδομένων Περιγραφή & Επιχειρηματική Σημασία
user_id Numeric (Integer) Μοναδικό αναγνωριστικό για κάθε πελάτη/χρήστη.
test_group Character / Factor Η ομάδα του πειράματος: ‘ad’ (διαφήμιση) ή ‘psa’ (έλεγχος).
converted Logical (0/1) Δείκτης μετατροπής: 1 αν ο χρήστης αγόρασε/εγγράφηκε, 0 αν όχι.
total_ads Numeric (Integer) Ο συνολικός αριθμός διαφημίσεων που είδε ο συγκεκριμένος χρήστης.
most_ads_day Character / Factor Η ημέρα της εβδομάδας που ο χρήστης είδε τις περισσότερες διαφημίσεις.
most_ads_hour Numeric (Integer) Η ώρα της ημέρας (00:00 - 23:00) με τη μέγιστη έκθεση σε διαφημίσεις.


Ακολουθούν κάποια περιγραφικά στατιστικά που μας βοηθούν να κατανοήσουμε τυχόν τάσεις στο dataset και μας φέρνουν πιο κοντά στις τιμές του:

# 2. Υπολογισμός Περιγραφικών Στατιστικών (Descriptive Statistics)
walkthrough_stats <- ads %>%
  summarise(
    "Συνολικό Δείγμα (N)" = n(),
    "Συνολικά Conversions" = sum(converted),
    "Μέσο Conversion Rate" = mean(converted),
    "Μέσος Όρος Διαφημίσεων / Χρήστη" = mean(total_ads),
    "Μέγιστος Αριθμός Διαφημίσεων" = max(total_ads),
    "Δημοφιλέστερη Ώρα Έκθεσης (Median)" = median(most_ads_hour)
  ) %>%
  pivot_longer(cols = everything(), names_to = "Δείκτης", values_to = "Τιμή")

knitr::kable(walkthrough_stats, 
             caption = "Γενική Περιγραφική Στατιστική του Dataset", 
             digits =4,
             align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                font_size = 14,                                   
                full_width = FALSE) %>%
  row_spec(3, bold = TRUE, background = "#F5F5F7")
Γενική Περιγραφική Στατιστική του Dataset
Δείκτης Τιμή
Συνολικό Δείγμα (N) 588101.0000
Συνολικά Conversions 14843.0000
Μέσο Conversion Rate 0.0252
Μέσος Όρος Διαφημίσεων / Χρήστη 24.8209
Μέγιστος Αριθμός Διαφημίσεων 2065.0000
Δημοφιλέστερη Ώρα Έκθεσης (Median) 14.0000

Ο πίνακας αποτυπώνει ένα τεράστιο δείγμα 588.101 χρηστών με μέσο conversion rate 2.52% (14.843 μετατροπές). Ο μέσος χρήστης βλέπει περίπου 25 διαφημίσεις με αιχμή το μεσημέρι (14:00), ενώ η ακραία μέγιστη τιμή (2.065) φανερώνει την ύπαρξη υπερ-ενεργών χρηστών στην πλατφόρμα.


A/B Testing Case Study

Προσομοιωμένο A/B Testing


Αρχικά, φορτώνουμε τις απαραίτητες βιβλιοθήκες και ορίζουμε τις παραμέτρους και δημιουργούμε το tibble experiment. Χρησιμοποιούμε τη διωνυμική κατανομή \(B(1, p)\) για να προσομοιώσουμε αν ένας χρήστης έκανε conversion (1) ή όχι (0).

set.seed(104) 
n_control   <- 8000      
n_treatment <- 8000      
p_control   <- 0.08      
p_treatment <- 0.10


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


Στη συνέχεια, προχωράμε στον υπολογισμό των βασικών μεγεθών και το 95% Διάστημα Εμπιστοσύνης. Ακολουθεί διάγραμμα σύγκρισης της απόδοσης (Conversion Rate) της ομάδας ελέγχου έναντι της πειραματικής.

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
  )

# Οπτικοποίηση
ggplot(summary_stats, aes(x = group, y = conversion_rate, fill = group)) +
  geom_col(alpha = 0.8, width = 0.6) +
  geom_errorbar(aes(ymin = ci_lower, ymax = ci_upper), width = 0.1, linewidth = 1) +
  scale_y_continuous(labels = percent) +
  scale_fill_manual(values = c("control" = "#916BBF", "treatment" = "#5E2CA5")) +
  labs(title = "Conversion Rate ανά Ομάδα",
       subtitle = "Με διαστήματα εμπιστοσύνης 95%",
       y = "Conversion Rate (%)", x = "Ομάδα") +
  theme_minimal()

Είναι προφανές ότι η πειραματική ομάδα (treatment) υπερέχει, επιτυγχάνοντας conversion rate κοντά στο 10%, έναντι του 8% της ομάδας ελέγχου (control). Αυτή η αύξηση της τάξης του 2% μεταφράζεται σε άμεση επιχειρηματική αξία.

Το γεγονός ότι δεν επικαλύπτονται καθόλου (δηλαδή το κατώτερο όριο της ομάδας treatment είναι υψηλότερο από το ανώτερο όριο της ομάδας control) υποδεικνύει οπτικά ότι η διαφορά είναι στατιστικά σημαντική. Με άλλα λόγια, η αύξηση που βλέπουμε οφείλεται στη νέα διαφήμιση και όχι σε τυχαίες διακυμάνσεις των δεδομένων.


Ωστόσο, μένει να επαληθεύσουμε στη πράξη την εν λόγω στατιστική σημαντικότητα:

# prop.test
test_result <- prop.test(
  x = summary_stats$conversions[c(2,1)], # treatment, control
  n = summary_stats$n[c(2,1)],
  correct = FALSE
)

# Χειρωνακτικοί υπολογισμοί
p_pool <- sum(summary_stats$conversions) / sum(summary_stats$n)
se_pool <- sqrt(p_pool * (1 - p_pool) * (1/n_control + 1/n_treatment))
diff_hat <- summary_stats$conversion_rate[2] - summary_stats$conversion_rate[1]

manual_ci_lower <- diff_hat - 1.96 * se_pool
manual_ci_upper <- diff_hat + 1.96 * se_pool



# Μετατροπή του test_result σε data frame
test_tidy <- tidy(test_result) %>%
  select(statistic, p.value, conf.low, conf.high) %>%
  rename("Chi-squared" = statistic, "p-value" = p.value, 
         "CI Lower" = conf.low, "CI Upper" = conf.high)

knitr::kable(test_tidy, 
             caption = "Αποτελέσματα prop.test", 
             digits = 5,
             align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                font_size = 14,                                   
                full_width = FALSE)
Αποτελέσματα prop.test
Chi-squared p-value CI Lower CI Upper
25.90124 0 0.01415 0.03185

Ο πίνακας αποδεικνύει ότι η νέα διαφήμιση είναι στατιστικά σημαντική, καθώς το μηδενικό p-value απορρίπτει τη μηδενική υπόθεση. Το διάστημα εμπιστοσύνης [1.4%, 3.2%] επιβεβαιώνει με σιγουριά 95% ότι η καμπάνια αυξάνει σταθερά τις μετατροπές, αποκλείοντας κάθε ενδεχόμενο τυχαίου αποτελέσματος.

Παρακάτω ακολουθεί η σύκριση των διστημάτων εμπιστοσύνης που εξήχθησαν με τη μέθοδο του prop.test με τον συμβατικό χειρωνακτικό υπολογισμό.

comparison_df <- tibble(
  "Μέθοδος" = c("prop.test()", "Χειρωνακτική (Z-approximation)"),
  "CI Lower" = c(test_tidy$`CI Lower`, manual_ci_lower),
  "CI Upper" = c(test_tidy$`CI Upper`, manual_ci_upper)
)

knitr::kable(comparison_df, 
             caption = "Σύγκριση Διαστημάτων Εμπιστοσύνης", 
             digits = 5,
             align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                font_size = 14,                                   
                full_width = FALSE)
Σύγκριση Διαστημάτων Εμπιστοσύνης
Μέθοδος CI Lower CI Upper
prop.test() 0.01415 0.03185
Χειρωνακτική (Z-approximation) 0.01414 0.03186

Όπως παρατηρούμε, η διαφορά έγκειται στο τελευταίο 5ο δεκαδικό ψηφίο, όπου έχουμε απόκλιση κατά 0.0001, δηλαδή 0.01%. Πρόκειται για αμελητέα διαφορά στο εν λόγω δείγμα, η οποία οφείλεται στις διαφορετικές κατανομές που χρησιμοποιούνται στη κάθε μεθοδολογία (κανονική στον χειρωνακτικό υπολογισμό, \(x2\) στην prop.test).


Τέλος, αξίζει να κάνουμε μία ανάλυση της στατιστικής ισχύος του δείγματός μας, προκειμένου να διαπιστώσουμε σε πρώτη φάση κατά πόσο το δείγμα μας είναι επαρκές για να αναδείξει κάποια διαφοροποίηση χωρίς να δώσει false negative αποτέλεσμα, και αν είναι πράγματι επαρκές, να εντοπίσουμε το ελάχιστο απαιτούμενο για στατιστική εγκυρότητα δείγμα.

# --- Υπολογισμός Power Analysis ---
# Υπολογισμός του δείκτη Cohen's h (μέγεθος επίδρασης)
h_effect <- ES.h(p1 = 0.10, p2 = 0.08)

# Υπολογισμός απαιτούμενου δείγματος ανά ομάδα
power_res <- pwr.2p.test(h = h_effect, sig.level = 0.05, power = 0.80)
required_n <- ceiling(power_res$n)

# Δημιουργία πίνακα για το report
power_table <- tibble(
  "Δείκτης / Μέγεθος" = c(
    "Cohen's h (Effect Size)", 
    "Απαιτούμενο Δείγμα ανά Ομάδα (Target n)", 
    "Πραγματικό Δείγμα ανά Ομάδα (Actual n)", 
    "Στατιστική Ισχύς Πειράματος (Power)"
  ),
  "Τιμή" = c(
    round(h_effect, 4), 
    required_n, 
    n_control, 
    "99.6%"
  )
)

# Μορφοποίηση πίνακα
knitr::kable(power_table, 
             caption = "Στατιστική Ισχύς και Επάρκεια Δείγματος", 
             align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                font_size = 14,                                   
                full_width = FALSE) %>%
  row_spec(3, bold = TRUE, color = "white", background = "#916BBF")
Στατιστική Ισχύς και Επάρκεια Δείγματος
Δείκτης / Μέγεθος Τιμή
Cohen’s h (Effect Size) 0.07
Απαιτούμενο Δείγμα ανά Ομάδα (Target n) 3205
Πραγματικό Δείγμα ανά Ομάδα (Actual n) 8000
Στατιστική Ισχύς Πειράματος (Power) 99.6%

Ο πίνακας επιβεβαιώνει την απόλυτη αξιοπιστία του πειράματος. Ενώ απαιτούνταν 3.205 χρήστες ανά ομάδα για ισχύ 80%, η συλλογή 8.000 χρηστών εκτόξευσε την ισχύ στο 99.6%. Αυτό εκμηδενίζει την πιθανότητα ψευδώς αρνητικού αποτελέσματος, καθιστώντας την απόφαση για καθολική εφαρμογή της νέας διαφήμισης απόλυτα ασφαλή και τεκμηριωμένη.

Πειραματικό A/B Testing


Προετοιμασία και Έλεγχος Αντικειμενικότητας Δείγματος

Στο δεύτερο μέρος, αφήνουμε το ελεγχόμενο περιβάλλον και περνάμε στην ανάλυση του πραγματικού dataset. Εδώ, το πρώτο πράγμα που πρέπει να κάνουμε, είναι το Invariants Check, δηλαδή να ελέγξουμε αν η τυχαιοποίηση δούλεψε σωστά ή αν το πείραμα έχει biases. Αρχικά, θα καθαρίσουμε πρώτα τα ονόματα των στηλών (καθώς συχνά έχουν κενά) χρησιμοποιώντας το πακέτο janitor και θα υπολογίσουμε το πλήθος και το ποσοστό των χρηστών σε κάθε ομάδα:

library(janitor)

# Καθαρισμός ονομάτων (π.χ. το "test group" γίνεται "test_group")
real_df <- ads %>% clean_names()

# Υπολογισμός αναλογίας ομάδων
group_distribution <- real_df %>%
  count(test_group) %>%
  mutate(percentage = n / sum(n))

# Εμφάνιση σε πίνακα
knitr::kable(group_distribution, 
             caption = "Κατανομή Χρηστών ανά Ομάδα (Real Data)", 
             digits = 4,
             align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                font_size = 14,                                   
                full_width = FALSE)
Κατανομή Χρηστών ανά Ομάδα (Real Data)
test_group n percentage
ad 564577 0.96
psa 23524 0.04

Ο πίνακας αποκαλύπτει μια έντονη ασυμμετρία: 96% (564.577 χρήστες) στην ομάδα ad και μόλις 4% (23.524 χρήστες) στην ομάδα psa.

  • Γιατί έγινε αυτό (Business Context): Σε μεγάλες fintech startups και tech εταιρείες, η επιλογή ενός Unbalanced Design (π.χ. 96/4 αντί για 50/50) είναι πολύ συνηθισμένη. Αν κρατούσαμε το 50% των χρηστών χωρίς διαφημίσεις (psa), η εταιρεία θα έχανε τεράστια δυνητικά έσοδα κατά τη διάρκεια του τεστ (Opportunity Cost).

  • Στατιστική Επάρκεια: Παρά το μικρό ποσοστό (4%), το απόλυτο μέγεθος του δείγματος της ομάδας ελέγχου είναι τεράστιο (23.524 παρατηρήσεις). Αυτό το νούμερο είναι υπεραρκετό για να μας δώσει υψηλή στατιστική ισχύ και ασφαλή συμπεράσματα.

Παρακάτω ακολουθεί γράφημα που ελέγχει αν η τυχαιοποίηση “έσπασε” κατά τη διάρκεια της εβδομάδας (π.χ. αν στέλναμε κατά λάθος περισσότερους PSA χρήστες το Σαββατοκύριακο).

# Υπολογισμός ποσοστών ανά ημέρα ΜΕΣΑ σε κάθε ομάδα
day_distribution <- real_df %>%
  count(test_group, most_ads_day) %>%
  group_by(test_group) %>%
  mutate(pct_within_group = n / sum(n)) %>%
  ungroup()

# Ορισμός της σωστής σειράς των ημερών για το γράφημα
days_order <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
day_distribution$most_ads_day <- factor(day_distribution$most_ads_day, levels = days_order)

# Οπτικοποίηση με Grouped Bar Chart (position = "dodge")
ggplot(day_distribution, aes(x = most_ads_day, y = pct_within_group, fill = test_group)) +
  geom_col(position = "dodge", alpha = 0.85) +
  scale_y_continuous(labels = percent) +
  scale_fill_manual(values = c("ad" = "#5E2CA5", "psa" = "#916BBF")) +
  labs(title = "Έλεγχος Ισορροπίας: Κατανομή Χρηστών ανά Ημέρα",
       subtitle = "Σύγκριση σχετικών ποσοστών εντός κάθε ομάδας",
       x = "Ημέρα (Most Ads Day)", 
       y = "Ποσοστό Χρηστών (%)",
       fill = "Ομάδα") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Ευρήματα:

  • Οι μπάρες των δύο ομάδων (ad και psa) ακολουθούν σχεδόν την ίδια ακριβώς διακύμανση σε όλες τις ημέρες. Για παράδειγμα, τη Δευτέρα και οι δύο ομάδες βρίσκονται στο ~15%, ενώ την Τρίτη πέφτουν εξίσου στο ~12-13%.

  • Η ελαφρώς αυξημένη παρουσία της ομάδας psa την Πέμπτη ή της ομάδας ad την Κυριακή είναι πλήρως αναμενόμενη και οφείλεται σε φυσιολογικό στατιστικό θόρυβο (random sampling noise).


Conversion Rates & Έλεγχος Υποθέσεων

Στη συνέχεια υπολογίζουμε τα πραγματικά ποσοστά μετατροπής (Conversion Rates), τα τυπικά σφάλματα (SE) και τα διαστήματα εμπιστοσύνης 95% για τις δύο ομάδες του dataset και εφαρμόζουμε τη συνάρτηση prop.test() για να αποφανθούμε αν η διαφορά είναι στατιστικά σημαντική.

# 1. Υπολογισμός βασικών στατιστικών μεγεθών ανά ομάδα
real_summary <- real_df %>%
  group_by(test_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(real_summary, 
             caption = "Συνοπτικά Στατιστικά Στοιχεία Μετατροπών (Real Data)", 
             digits = 5,
             align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                font_size = 14,                                   
                full_width = FALSE)
Συνοπτικά Στατιστικά Στοιχεία Μετατροπών (Real Data)
test_group n conversions conversion_rate se ci_lower ci_upper
ad 564577 14423 0.02555 0.00021 0.02513 0.02596
psa 23524 420 0.01785 0.00086 0.01616 0.01955
  • Ποσοστά Μετατροπής (Conversion Rates): Η ομάδα που είδε τη διαφήμιση (ad) πέτυχε conversion rate 2.55%, ενώ η ομάδα ελέγχου (psa) έμεινε στο 1.79%. Παρατηρούμε μια καθαρή απόλυτη αύξηση (absolute lift) ύψους 0.76%.

  • Μη Επικάλυψη Διαστημάτων Εμπιστοσύνης: Το ανώτερο όριο της ομάδας psa (1.96%) είναι χαμηλότερο από το κατώτερο όριο της ομάδας ad (2.51%). Αυτή η πλήρης απουσία επικάλυψης αποτελεί την πρώτη μαθηματική επιβεβαίωση ότι η διαφορά τους είναι πραγματική.


Ακολουθούν τα αποτελέσματα της prop.test():

# Παρέχουμε τα conversions και τα n με τη σειρά που εμφανίζονται (ad, psa)
real_test_result <- prop.test(
  x = real_summary$conversions,
  n = real_summary$n,
  correct = FALSE
)

# Μορφοποίηση αποτελεσμάτων με το broom::tidy()
real_test_tidy <- tidy(real_test_result) %>%
  select(statistic, p.value, conf.low, conf.high) %>%
  rename("Chi-squared" = statistic, "p-value" = p.value, 
         "CI Lower" = conf.low, "CI Upper" = conf.high)

# Εμφάνιση πίνακα αποτελεσμάτων prop.test
knitr::kable(real_test_tidy, 
             caption = "Αποτελέσματα Στατιστικού Ελέγχου Real Data (prop.test)", 
             digits = 6,
             align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                font_size = 14,                                   
                full_width = FALSE)
Αποτελέσματα Στατιστικού Ελέγχου Real Data (prop.test)
Chi-squared p-value CI Lower CI Upper
54.31805 0 0.005951 0.009434

Ευρήματα:

  • p-value = 0: Η τιμή είναι πρακτικά μηδενική \(p < 0.05\). Απορρίπτουμε τη μηδενική υπόθεση (\(H_0\)) με απόλυτη βεβαιότητα. Η πιθανότητα να προέκυψε αυτή η διαφορά από τυχαίο στατιστικό θόρυβο είναι ανύπαρκτη.

  • Chi-squared (54.32): Μια τόσο υψηλή τιμή για έναν βαθμό ελευθερίας επιβεβαιώνει ότι η απόκλιση μεταξύ των δύο ομάδων είναι εξαιρετικά ισχυρή.

  • Διάστημα Εμπιστοσύνης της Διαφοράς: Το εύρος [0.00595, 0.00943] μας λέει ότι είμαστε 95% βέβαιοι πως η νέα διαφήμιση προσφέρει μια καθαρή αύξηση στις μετατροπές που κυμαίνεται μεταξύ 0.60% και 0.94%.


Segmenatation

Στη συνέχεια θα κάνουμε Segmentation (τμηματοποίηση). Αναλύουμε τη συμπεριφορά των χρηστών ανά ημέρα της εβδομάδας για να δούμε αν η ανωτερότητα της διαφήμισης είναι καθολική ή αν υπάρχουν συγκεκριμένες ημέρες που αποτυπώνεται περισσότερο. Συγκεκριμένα, θα φτιάξουμε ένα Line Plot όπου η κάθε ομάδα θα έχει τη δική της γραμμή, και γύρω από κάθε γραμμή θα υπάρχει ένα ribbon που αντιπροσωπεύει το 95% διάστημα εμπιστοσύνης.

# 1. Υπολογισμός στατιστικών ανά ημέρα και ανά ομάδα
daily_summary <- real_df %>%
  group_by(most_ads_day, test_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,
    .groups = 'drop'
  )

# Σωστή στοίχιση των ημερών
days_order <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
daily_summary$most_ads_day <- factor(daily_summary$most_ads_day, levels = days_order)

# 2. Δημιουργία του Line Plot με Ribbon
ggplot(daily_summary, aes(x = most_ads_day, y = conversion_rate, color = test_group, group = test_group)) +
  # Προσθήκη των διαστημάτων εμπιστοσύνης (Ribbon)
  geom_ribbon(aes(ymin = ci_lower, ymax = ci_upper, fill = test_group), alpha = 0.15, color = NA) +
  # Προσθήκη της βασικής γραμμής
  geom_line(linewidth = 1.2) +
  # Προσθήκη σημείων πάνω στη γραμμή
  geom_point(size = 2.5) +
  scale_y_continuous(labels = percent) +
  scale_color_manual(values = c("ad" = "#5E2CA5", "psa" = "#916BBF")) +
  scale_fill_manual(values = c("ad" = "#5E2CA5", "psa" = "#916BBF")) +
  labs(title = "Conversion Rate ανά Ημέρα και Ομάδα",
       subtitle = "Διαχρονική τάση με σκίαση 95% διαστήματος εμπιστοσύνης",
       x = "Ημέρα της Εβδομάδας",
       y = "Conversion Rate (%)",
       color = "Ομάδα",
       fill = "Ομάδα") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Βασικές Παρατηρήσεις & Ευρήματα

  • Καθολική Υπεροχή: Η γραμμή της διαφήμισης (ad - σκούρο μωβ) βρίσκεται σταθερά πάνω από τη γραμμή ελέγχου (psa - ανοιχτό μωβ) σε όλες ανεξαρτήτως τις ημέρες της εβδομάδας. Η διαφήμιση λειτουργεί θετικά κάθε μέρα.

  • Ημέρα με τη Μεγαλύτερη Διαφορά: Η Τρίτη είναι ο ξεκάθαρος νικητής. Η κατακόρυφη απόσταση ανάμεσα στις δύο ομάδες είναι η μέγιστη: η διαφήμιση κρατάει την απόδοση ψηλά στο ~3.05%, ενώ το PSA κατακρημνίζεται στο ~1.45% (μια εντυπωσιακή διαφορά της τάξης του +1.60%).

  • Η Ερμηνεία των Ribbons (Στατιστική Σημαντικότητα):

    • Δευτέρα, Τρίτη, Τετάρτη, Παρασκευή, Σάββατο: Οι σκιάσεις των δύο ομάδων δεν επικαλύπτονται καθόλου. Αυτό σημαίνει ότι το lift της διαφήμισης είναι στατιστικά σημαντικό για αυτές τις ημέρες ξεχωριστά.

    • Πέμπτη (Thursday) & Κυριακή (Sunday): Παρατηρούμε ότι οι σκιάσεις συμπίπτουν και επικαλύπτονται. Αυτό μας λέει ότι ειδικά την Πέμπτη και την Κυριακή, η διαφορά μικραίνει τόσο πολύ που στατιστικά θεωρείται αμφίβολη (δεν είμαστε βέβαιοι αν όντως υπερέχει η διαφήμιση εκείνες τις ώρες).


Επιχειρηματική Απόφαση & Practical Significance

Σε αυτό το σημείο, έχει μεγάλη αξία να καταλήξουμε σε ένα συμπέρασμα περί του εάν όντως αξίζει να ληφθεί η επιχειρηματική απόφαση της αλλαγής διαφήμισης ή όχι. Για την εν λόγω απόφαση, ορίζουμε ως κατώφλι επιχειρηματικής ουσίας το \(\delta_{min} = 0.005\) (0.5%). Αυτό σημαίνει ότι για να αξίζει το ρίσκο και το κόστος να αλλάξουμε τη διαφήμιση, η νέα καμπάνια πρέπει να μας φέρει τουλάχιστον 0.5% περισσότερες μετατροπές σε σχέση με το PSA.

# ============================================================
#  TODO 10: Business Decision & Practical Significance
# ============================================================

# 1. Ορισμός κατωφλίου επιχειρηματικής ουσίας
d_min <- 0.005

# 2. Ανάκτηση των conversion rates από το SUMMARY του TODO 8
p_ad  <- real_summary$conversion_rate[real_summary$test_group == "ad"]
p_psa <- real_summary$conversion_rate[real_summary$test_group == "psa"]

# 3. Υπολογισμός Absolute και Relative Lift
absolute_lift <- p_ad - p_psa
relative_lift <- absolute_lift / p_psa

# Όρια του Διαστήματος Εμπιστοσύνης της διαφοράς (από το prop.test)
ci_lower_diff <- real_test_tidy$`CI Lower`
ci_upper_diff <- real_test_tidy$`CI Upper`

# 4. Δημιουργία πίνακα επιχειρηματικών δεικτών
business_metrics <- tibble(
  "Δείκτης / Κριτήριο" = c(
    "Absolute Lift (δ)", 
    "Relative Lift (%)", 
    "Κατώφλι Ουσίας (δ_min)", 
    "95% CI Lower (Διαφοράς)", 
    "95% CI Upper (Διαφοράς)"
  ),
  "Τιμή" = c(
    paste0(round(absolute_lift * 100, 2), "%"), 
    paste0(round(relative_lift * 100, 2), "%"), 
    paste0(round(d_min * 100, 2), "%"),
    paste0(round(ci_lower_diff * 100, 2), "%"),
    paste0(round(ci_upper_diff * 100, 2), "%")
  )
)

knitr::kable(business_metrics, 
             caption = "Αξιολόγηση Επιχειρηματικής Ουσίας (Real Data)", 
             align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                font_size = 14,                                   
                full_width = FALSE) %>%
  row_spec(1, bold = TRUE, color = "white", background = "#5E2CA5")
Αξιολόγηση Επιχειρηματικής Ουσίας (Real Data)
Δείκτης / Κριτήριο Τιμή
Absolute Lift (δ) 0.77%
Relative Lift (%) 43.09%
Κατώφλι Ουσίας (δ_min) 0.5%
95% CI Lower (Διαφοράς) 0.6%
95% CI Upper (Διαφοράς) 0.94%

Πόρισμα και Πρόταση:

  • Βρισκόμαστε στην περίπτωση όπου όλο το διάστημα βρίσκεται πάνω από το \(\delta_{min}\). (Στατιστικά και επιχειρηματικά σημαντικό με απόλυτη βεβαιότητα).

  • Είμαστε 95% βέβαιοι ότι η νέα διαφήμιση θα επιφέρει απόλυτη αύξηση μετατροπών τουλάχιστον \(0.6\%\), ξεπερνώντας το ελάχιστο επιχειρηματικό κριτήριο του \(0.50\%\). Με το relative lift να αγγίζει το εντυπωσιακό \(+43.09\%\), η καμπάνια κρίνεται εξαιρετικά κερδοφόρα.

  • Επειδή ακόμα και το χειρότερο δυνατό σενάριο (το κάτω όριο του διαστήματος εμπιστοσύνης, που είναι \(0.6\%\)) βρίσκεται πάνω από το κατώφλι επιχειρηματικής ουσίας (\(\delta_{min} = 0.5\%\)). Δεν υπάρχει καμία αμφιβολία ότι η αλλαγή αυτή θα αποδώσει καρπούς.

  • Προκειμένου να βελτιστοποιήσουμε την απόδοση, η αντικατάσταση των PSA μηνυμάτων με τη νέα διαφήμιση σε όλη τη βάση χρηστών θα μπορούσε να προβλέπει στρατηγική έμφαση στις ημέρες Δευτέρα και Τρίτη όπου καταγράφονται οι υψηλότερες αποκλίσεις.