Ο πρωτεύων στόχος αυτού του Case Study είναι η αξιολόγηση μιας νέας διαφημιστικής καμπάνιας για μια fintech startup. Καλούμαστε να αποφανθούμε με στατιστική αυστηρότητα αν η νέα διαφήμιση αυξάνει πραγματικά και ουσιαστικά τις μετατροπές (conversions) των χρηστών ή αν η όποια διαφορά οφείλεται στην τύχη, παραδίδοντας μια τεκμηριωμένη επιχειρηματική σύσταση.
Η προσέγγιση δομήθηκε σε δύο διακριτά μέρη, συνδυάζοντας τη θεωρητική επαλήθευση με την ανάλυση πραγματικών δεδομένων μεγάλης κλίμακας:
Μέρος Α: Προσομοίωση και Στατιστική Επαλήθευση
Δημιουργία Ελεγχόμενου Περιβάλλοντος
Μαθηματικό Sanity Check
Ανάλυση Ισχύος (Power Analysis)
Μέρος Β: Ανάλυση Πραγματικών Δεδομένων (Kaggle Marketing AB)
Έλεγχος Αξιοπιστίας Δείγαμτος
Ανάλυση Conversion Rates & Έλεγχος Υποθέσεων
Τμηματοποίηση (Segmentation)
Επιχειρηματική Ουσία (Practical Significance)
Σε πρώτη φάση, ας δούμε τι είδους μεταβλητές περιλαμβάνει το 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)| Όνομα Μεταβλητής | Τύπος Δεδομένων | Περιγραφή & Επιχειρηματική Σημασία |
|---|---|---|
| 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")| Δείκτης | Τιμή |
|---|---|
| Συνολικό Δείγμα (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) φανερώνει την ύπαρξη υπερ-ενεργών χρηστών στην πλατφόρμα.
Αρχικά, φορτώνουμε τις απαραίτητες βιβλιοθήκες και ορίζουμε τις παραμέτρους και δημιουργούμε το 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)| 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%. Αυτό εκμηδενίζει την πιθανότητα ψευδώς αρνητικού αποτελέσματος, καθιστώντας την απόφαση για καθολική εφαρμογή της νέας διαφήμισης απόλυτα ασφαλή και τεκμηριωμένη.
Στο δεύτερο μέρος, αφήνουμε το ελεγχόμενο περιβάλλον και περνάμε στην
ανάλυση του πραγματικού 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)| 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), τα τυπικά σφάλματα
(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)| 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)| 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%.
Στη συνέχεια θα κάνουμε 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): Παρατηρούμε ότι οι σκιάσεις συμπίπτουν και επικαλύπτονται. Αυτό μας λέει ότι ειδικά την Πέμπτη και την Κυριακή, η διαφορά μικραίνει τόσο πολύ που στατιστικά θεωρείται αμφίβολη (δεν είμαστε βέβαιοι αν όντως υπερέχει η διαφήμιση εκείνες τις ώρες).
Σε αυτό το σημείο, έχει μεγάλη αξία να καταλήξουμε σε ένα συμπέρασμα περί του εάν όντως αξίζει να ληφθεί η επιχειρηματική απόφαση της αλλαγής διαφήμισης ή όχι. Για την εν λόγω απόφαση, ορίζουμε ως κατώφλι επιχειρηματικής ουσίας το \(\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")| Δείκτης / Κριτήριο | Τιμή |
|---|---|
| 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 μηνυμάτων με τη νέα διαφήμιση σε όλη τη βάση χρηστών θα μπορούσε να προβλέπει στρατηγική έμφαση στις ημέρες Δευτέρα και Τρίτη όπου καταγράφονται οι υψηλότερες αποκλίσεις.