library(readxl)
library(dplyr)
library(tidyr)
library(ggplot2)
library(MASS)        # lda, qda, isoMDS
library(car)         # leveneTest, vif
library(lmtest)      # bptest, dwtest, coeftest
library(caret)       # confusionMatrix
library(pROC)        # ROC
library(sandwich)    # HC3 robustné štandardné chyby
library(knitr)
library(kableExtra)

1 Poznamka

Keďže prvé zadanie nebolo vypracované v plnej miere a druhé zadanie je riešené v samostatnom prostredí, predspracovanie dát vykonávame na začiatku znovu, aby bol report úplný a samostatne reprodukovateľný.

2 Východisko a cieľ projektu

Potravinárska spoločnosť plánuje ďalšiu marketingovú kampaň. K dispozícii máme dáta o 1 956 zákazníkoch, ktorí absolvovali pilotnú kampaň. Cieľom druhého zadania je hlbšie preskúmať vzťahy v dátach pomocou regresnej analýzy, MANOVA, diskriminačnej analýzy, viacrozmerného škálovania a logistickej regresie. Záverečné odporúčania majú odpovedať na otázku, na koho a ako sa zacieliť, aby kampaň zasiahla nielen už aktívnych zákazníkov, ale aj segmenty s nevyužitým potenciálom.

3 Načítanie a predspracovanie dát

Food <- read_excel("Food.xlsx")
dim(Food)
## [1] 1956   25

3.1 Stratégia práce s chýbajúcimi hodnotami

V dátach je 161 riadkov (8,2 %) s aspoň jednou chýbajúcou hodnotou, hoci v žiadnej premennej nepresahuje miera chýbania 1,4 %. Listwise odstránenie by teda znamenalo stratu vyše 160 pozorovaní — to už nie je zanedbateľné a hrozí, že odstránime systematicky určitý typ zákazníkov (najmä pri premennej Income, kde sa NA často viažu k vyšším príjmovým skupinám, ktoré niekedy odmietajú odpovedať).

Vzhľadom na to, že:

  • podiel chýbajúcich v každej premennej je nízky,
  • premenné nákupov sú silne pravostranne zošikmené (medián robustnejší voči odľahlým hodnotám než priemer),
  • nemáme prirodzenú pomocnú premennú, ktorá by predikovala chýbajúce hodnoty,

zvolili sme mediánovú imputáciu pre číselné premenné a modálnu imputáciu pre kategoriálne premenné. Nepoužívame priemer, lebo by deformoval pravé chvosty distribúcií. Ide o jednoduchý, transparentný postup s minimálnou pridanou neurčitosťou pri nízkej miere chýbania.

na_per_col <- sapply(Food, function(x) sum(is.na(x)))
na_per_col[na_per_col > 0]
##                 Age           Education      Marital_Status              Income 
##                  27                   9                  12                  26 
##             Kidhome            Teenhome             Recency            MntWines 
##                   5                   1                  15                  20 
##           MntFruits     MntMeatProducts     MntFishProducts    MntSweetProducts 
##                  13                  18                  15                  18 
##        MntGoldProds   NumDealsPurchases     NumWebPurchases NumCatalogPurchases 
##                  21                  15                  14                  17 
##   NumStorePurchases   NumWebVisitsMonth 
##                  23                   3
# riadkov so > 0 NA
sum(!complete.cases(Food))
## [1] 161

3.2 Korekcia odľahlých hodnôt a kategórií

Pri premennej Income je jedna hodnota 666 666 €, čo je viac než štvornásobok ďalšej najvyššej (162 397 €). Z business pohľadu pôsobí ako chyba zápisu (poškodené pero hodnoty „666 666”), preto ju nahrádzame NA a následne imputujeme medianom.

Pri premennej Marital_Status sú 3 záznamy s hodnotou „Alone”, ktorú zlučujeme s kategóriou „Single” (sémanticky totožné, počet záznamov by jeden samostatne stojaci level neopodstatnil).

Food <- Food %>%
  mutate(
    Income = ifelse(Income > 600000, NA, Income),
    Marital_Status = ifelse(Marital_Status == "Alone", "Single", Marital_Status)
  )

# Imputácia
num_cols <- names(Food)[sapply(Food, is.numeric)]
for (c in num_cols) {
  Food[[c]][is.na(Food[[c]])] <- median(Food[[c]], na.rm = TRUE)
}
Food$Education[is.na(Food$Education)] <- 
  names(sort(table(Food$Education), decreasing = TRUE))[1]
Food$Marital_Status[is.na(Food$Marital_Status)] <- 
  names(sort(table(Food$Marital_Status), decreasing = TRUE))[1]

# Faktorizácia
Food$Education <- factor(Food$Education,
                         levels = c("Basic","Graduation","Master","PhD"))
Food$Marital_Status <- factor(Food$Marital_Status)

sum(is.na(Food))  # kontrola
## [1] 0

3.3 Vytvorené premenné

Pre potreby ďalších analýz zostavujeme:

  • Deti — binárna premenná: 1 = aspoň jedno dieťa v domácnosti (Kidhome + Teenhome > 0), inak 0. Slúži ako faktor pre MANOVA a diskriminačnú analýzu.
  • Activity — binárna premenná: 1 = zákazník prijal aspoň jednu ponuku z 5 predchádzajúcich kampaní alebo ponuku poslednej kampane (Response), inak 0. Slúži ako závislá premenná logistickej regresie.
  • TotalSpend — celkové výdavky zákazníka na všetky produktové kategórie za 2 roky (súčet 6 Mnt* premenných).
  • Children — celkový počet detí v domácnosti (Kidhome + Teenhome) — používa sa ako spojitý prediktor v regresiách.
Food <- Food %>%
  mutate(
    Deti = ifelse(Kidhome + Teenhome > 0, 1, 0),
    Activity = ifelse(AcceptedCmp1 + AcceptedCmp2 + AcceptedCmp3 +
                      AcceptedCmp4 + AcceptedCmp5 + Response > 0, 1, 0),
    TotalSpend = MntWines + MntFruits + MntMeatProducts +
                 MntFishProducts + MntSweetProducts + MntGoldProds,
    Children = Kidhome + Teenhome
  )

Food$Deti <- factor(Food$Deti, levels = c(0,1), labels = c("Bez detí","S deťmi"))
Food$Activity_f <- factor(Food$Activity, levels = c(0,1), labels = c("Nereagoval","Reagoval"))

cat("Deti:\n"); print(table(Food$Deti))
## Deti:
## 
## Bez detí  S deťmi 
##      511     1445
cat("\nActivity:\n"); print(table(Food$Activity_f))
## 
## Activity:
## 
## Nereagoval   Reagoval 
##       1424        532
cat("\nPodiel aktívnych: ", round(mean(Food$Activity)*100, 1), "%\n")
## 
## Podiel aktívnych:  27.2 %

Skupiny majú obe dostatočnú veľkosť pre MANOVA aj diskriminačnú analýzu (511 zákazníkov bez detí, 1 445 so deťmi). Pri Activity máme 27,2 % „pozitívnych” prípadov — rozdelenie nie je extrémne nevyvážené, takže pre logistickú regresiu nepotrebujeme špeciálne techniky (over-sampling, váženie tried).


4 Regresná analýza

4.1 Hypotéza

Predpokladáme, že celkové výdavky zákazníka na potravinárske kategórie nie sú náhodné, ale dajú sa významne vysvetliť kombináciou ekonomických, demografických a behaviorálnych charakteristík. Konkrétne očakávame:

  • pozitívny efekt príjmu (vyššia kúpyschopnosť → vyššie výdavky),
  • pozitívny efekt veku (starší zákazníci majú stabilizované návyky a väčší rozpočet na kvalitu),
  • negatívny efekt počtu detí v domácnosti (rodiny s deťmi rozdeľujú rozpočet medzi viac členov, lacnejšie nákupy v základných kategóriách),
  • negatívny efekt premennej Recency (dlhšia doba od posledného nákupu → klient nakupuje menej často),
  • pozitívny efekt zľavových nákupov a webových návštev (vyššia aktivita → vyššie výdavky).

Závislú premennú TotalSpend logaritmicky transformujeme — distribúcia je výrazne pravostranne zošikmená (šikmosť ≈ 0,9; po log1p ≈ −0,4), čo vyhovuje predpokladu normality reziduí lineárneho modelu a zároveň interpretuje koeficienty ako približne percentuálne zmeny.

par(mfrow = c(1,2))
hist(Food$TotalSpend, breaks = 40, col = "steelblue",
     main = "Pôvodná distribúcia TotalSpend",
     xlab = "TotalSpend (€)")
hist(log1p(Food$TotalSpend), breaks = 40, col = "steelblue",
     main = "Po log1p transformácii",
     xlab = "log(1 + TotalSpend)")

par(mfrow = c(1,1))

4.2 Odhad modelu

Food$logSpend <- log1p(Food$TotalSpend)
model_reg <- lm(logSpend ~ Income + Age + Children + Recency +
                  NumWebVisitsMonth + NumDealsPurchases, data = Food)
summary(model_reg)
## 
## Call:
## lm(formula = logSpend ~ Income + Age + Children + Recency + NumWebVisitsMonth + 
##     NumDealsPurchases, data = Food)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -8.673 -0.368  0.056  0.453  3.964 
## 
## Coefficients:
##                     Estimate Std. Error t value             Pr(>|t|)    
## (Intercept)        2.6566711  0.1278439   20.78 < 0.0000000000000002 ***
## Income             0.0000488  0.0000011   44.20 < 0.0000000000000002 ***
## Age                0.0066084  0.0016481    4.01             0.000063 ***
## Children          -0.6936621  0.0281065  -24.68 < 0.0000000000000002 ***
## Recency            0.0007382  0.0006002    1.23                0.219    
## NumWebVisitsMonth  0.0256180  0.0100117    2.56                0.011 *  
## NumDealsPurchases  0.2417218  0.0102904   23.49 < 0.0000000000000002 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.763 on 1949 degrees of freedom
## Multiple R-squared:  0.729,  Adjusted R-squared:  0.729 
## F-statistic:  876 on 6 and 1949 DF,  p-value: <0.0000000000000002

4.3 Interpretácia výsledkov

Celková významnosť modelu je výrazná (F ≈ 875, p < 0,001), R² = 0,73. To znamená, že 73 % variability logaritmu výdavkov vysvetlíme len šiestimi prediktormi — pre marketingovú aplikáciu veľmi silný výsledok.

Konkrétne koeficienty (každý výrazne významný okrem Recency):

  • Income: koeficient 4,88 × 10⁻⁵. Po prepočte: zvýšenie príjmu o 10 000 € sa spája s rastom log-výdavkov o 0,49, čo zodpovedá približne +63 % výdavkov. Príjem je dominantný prediktor — najsilnejší t-statistikou (t ≈ 44).
  • Children: −0,69. Každé ďalšie dieťa v domácnosti znižuje očakávané log-výdavky o 0,69, t. j. približne na polovicu (e^(−0,69) ≈ 0,50). Tento efekt je zďaleka najsilnejší demografický prediktor a v praxi to znamená, že rodiny s deťmi sú výrazne odlišný segment.
  • Age: +0,0066 — pozitívny, ale slabý. Pri 10-ročnom rozdiele to znamená rast log-výdavkov o 0,066 (cca +7 %).
  • NumDealsPurchases: +0,24. Každý ďalší nákup so zľavou znamená rast log-výdavkov o 0,24 (≈ +27 %). Pozor na interpretáciu: nejde o to, že zľavy zvyšujú útraty, ale že aktívni nákupcovia všeobecne nakupujú aj so zľavami (zľavové nákupy sú indikátor frekvencie).
  • NumWebVisitsMonth: +0,026 — mierne pozitívny, ale slabší než sme čakali. Web visits sami osebe negarantujú vysokú útratu.
  • Recency: štatisticky nevýznamné (p = 0,22). Doba od posledného nákupu sama o sebe celkové dvojročné výdavky nevysvetľuje — Recency je ukazovateľom aktuálneho stavu, nie sumárneho objemu.

4.4 Testovanie predpokladov

par(mfrow = c(2,2))
plot(model_reg)

par(mfrow = c(1,1))
# Normalita reziduí — Shapiro pre vzorky <5000
shapiro.test(model_reg$residuals[1:1500])
## 
##  Shapiro-Wilk normality test
## 
## data:  model_reg$residuals[1:1500]
## W = 0.85, p-value <0.0000000000000002
# Homoskedasticita
bptest(model_reg)
## 
##  studentized Breusch-Pagan test
## 
## data:  model_reg
## BP = 112, df = 6, p-value <0.0000000000000002
# Autokorelácia
dwtest(model_reg)
## 
##  Durbin-Watson test
## 
## data:  model_reg
## DW = 2, p-value = 0.6
## alternative hypothesis: true autocorrelation is greater than 0
# Multikolinearita
vif(model_reg)
##            Income               Age          Children           Recency 
##             1.854             1.094             1.466             1.002 
## NumWebVisitsMonth NumDealsPurchases 
##             1.995             1.331

Slovné zhrnutie testov predpokladov:

  • Normalita reziduí: Shapiro–Wilk zamieta normalitu (p < 0,001). Q-Q graf však ukazuje len mierne odchýlky v chvostoch. Pri n = 1 956 je test extrémne citlivý a podľa centrálnej limitnej vety zostávajú odhady koeficientov a ich štandardné chyby asymptoticky platné.
  • Homoskedasticita: Breusch–Pagan zamieta homoskedasticitu (p < 0,001) — vo grafe reziduí vs. predikcie vidieť mierny lievikovitý tvar. Riešením je použitie robustných štandardných chýb (HC3), ktoré korigujú inferenciu pri zachovaní bodových odhadov.
  • Autokorelácia: Durbin–Watson = 2,01 (p > 0,05). Predpoklad nezávislosti reziduí je splnený, čo je očakávané pri prierezových dátach.
  • Multikolinearita: všetky VIF < 2 (najvyšší pre NumWebVisitsMonth = 2,0). Bezpečne pod kritickou hranicou 5, premenné navzájom „nesúťažia”.
library(sandwich)
coeftest(model_reg, vcov = vcovHC(model_reg, type = "HC3"))
## 
## t test of coefficients:
## 
##                      Estimate  Std. Error t value            Pr(>|t|)    
## (Intercept)        2.65667108  0.20709004   12.83 <0.0000000000000002 ***
## Income             0.00004878  0.00000296   16.49 <0.0000000000000002 ***
## Age                0.00660841  0.00201527    3.28              0.0011 ** 
## Children          -0.69366209  0.04723620  -14.68 <0.0000000000000002 ***
## Recency            0.00073823  0.00062731    1.18              0.2394    
## NumWebVisitsMonth  0.02561795  0.01727061    1.48              0.1381    
## NumDealsPurchases  0.24172177  0.02830170    8.54 <0.0000000000000002 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Aj s robustnými štandardnými chybami zostávajú všetky pôvodne významné koeficienty štatisticky významné. Záver z modelu je robustný voči porušeniu homoskedasticity.

4.5 Business záver z regresie

Príjem a počet detí spolu vysvetľujú väčšinu rozdielov v útratách — vekové a behaviorálne premenné dolaďujú zostatok. Z marketingovej perspektívy to znamená, že segmentácia podľa kombinácie príjem × deti by mala byť základom akejkoľvek personalizácie ponuky: ide o dve premenné s najsilnejším a opačne smerujúcim efektom. V ďalších analýzach túto hypotézu ďalej rozpracujeme.


5 MANOVA

5.1 Otázka analýzy

Líšia sa zákazníci s deťmi od zákazníkov bez detí v rozložení útrat naprieč produktovými kategóriami? Univariátne testy by ukázali rozdiely v každej kategórii zvlášť, ale MANOVA odpovedá komplexnejšie: či sa celkový profil spotreby (víno + ovocie + mäso + ryby + sladkosti) systémovo líši.

Faktor: Deti (Bez detí / S deťmi). Závislé premenné: MntWines, MntFruits, MntMeatProducts, MntFishProducts, MntSweetProducts (podľa zadania).

5.2 Predspracovanie pre MANOVA

Všetkých 5 premenných je silne pravostranne zošikmených (šikmosť 1,2 – 2,2). Aplikujeme log1p transformáciu, ktorá výrazne približuje distribúcie k symetrii (šikmosť po transformácii v intervale −0,55 až 0,15). Bez transformácie by predpoklady viacrozmernej normality boli porušené tak silno, že by výsledky MANOVA boli neinterpretovateľné.

manova_data <- Food %>%
  transmute(
    Deti,
    L_Wines = log1p(MntWines),
    L_Fruits = log1p(MntFruits),
    L_Meat = log1p(MntMeatProducts),
    L_Fish = log1p(MntFishProducts),
    L_Sweet = log1p(MntSweetProducts)
  )

# Šikmosť pred a po
skew <- function(x) mean((x - mean(x))^3)/sd(x)^3
sapply(Food[, c("MntWines","MntFruits","MntMeatProducts",
                "MntFishProducts","MntSweetProducts")], skew)
##         MntWines        MntFruits  MntMeatProducts  MntFishProducts 
##            1.187            2.157            2.120            2.028 
## MntSweetProducts 
##            2.227
sapply(manova_data[, -1], skew)
##  L_Wines L_Fruits   L_Meat   L_Fish  L_Sweet 
## -0.55365  0.14489 -0.06694 -0.01202  0.11769

5.3 Test predpokladov

Box’s M-test pre homogenitu kovariančných matíc (implementácia podľa formulácie Coopera 1971):

box_m_test <- function(data, group) {
  data <- as.matrix(data)
  group <- factor(group)
  k <- nlevels(group)
  p <- ncol(data)
  ns <- table(group)
  covs <- lapply(levels(group), function(g) cov(data[group == g, , drop = FALSE]))
  pooled <- Reduce("+", Map(function(s, n) (n - 1) * s, covs, ns)) / (sum(ns) - k)
  M <- sum((ns - 1) * sapply(covs, function(s) log(det(s)))) -
       (sum(ns) - k) * log(det(pooled))
  M <- -M  # convention
  c1 <- (2*p^2 + 3*p - 1) / (6 * (p + 1) * (k - 1)) *
        (sum(1/(ns - 1)) - 1/(sum(ns) - k))
  df <- p * (p + 1) * (k - 1) / 2
  X2 <- M * (1 - c1)
  pval <- 1 - pchisq(X2, df)
  list(M = M, ChiSq = X2, df = df, p.value = pval)
}

bm <- box_m_test(manova_data[, -1], manova_data$Deti)
cat(sprintf("Box's M = %.2f, χ²(%d) = %.2f, p = %.4e\n",
            bm$M, bm$df, bm$ChiSq, bm$p.value))
## Box's M = 111.06, χ²(15) = 110.64, p = 1.1102e-16

Box’s M zamieta H₀ o rovnosti kovariančných matíc (p < 0,001). To naznačuje, že kovariančné štruktúry medzi skupinami nie sú rovnaké. Box’s M je však veľmi citlivý pri veľkých vzorkách (n = 1 956) — aj malá odchýlka sa stane „štatisticky významnou”. Pre samotnú MANOVA preto použijeme Pillaiho stopu, ktorá je voči porušeniu predpokladu o homogenite kovariančných matíc najrobustnejšia.

5.4 Vykonanie MANOVA

manova_fit <- manova(cbind(L_Wines, L_Fruits, L_Meat, L_Fish, L_Sweet) ~ Deti,
                     data = manova_data)
summary(manova_fit, test = "Pillai")
##             Df Pillai approx F num Df den Df              Pr(>F)    
## Deti         1   0.29      159      5   1950 <0.0000000000000002 ***
## Residuals 1954                                                      
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(manova_fit, test = "Wilks")
##             Df Wilks approx F num Df den Df              Pr(>F)    
## Deti         1  0.71      159      5   1950 <0.0000000000000002 ***
## Residuals 1954                                                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(manova_fit, test = "Hotelling-Lawley")
##             Df Hotelling-Lawley approx F num Df den Df              Pr(>F)    
## Deti         1            0.408      159      5   1950 <0.0000000000000002 ***
## Residuals 1954                                                                
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(manova_fit, test = "Roy")
##             Df   Roy approx F num Df den Df              Pr(>F)    
## Deti         1 0.408      159      5   1950 <0.0000000000000002 ***
## Residuals 1954                                                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Všetky štyri testy zhodne ukazujú vysoko významný efekt premennej Deti (p < 0,001). Pillai’s trace = 0,29 znamená, že 29 % rozptylu v lineárnej kombinácii piatich kategórií spotreby je spojených s príslušnosťou ku skupine. Hypotézu o rovnakých priemerných profiloch spotreby medzi rodinami s deťmi a bez detí zamietame.

5.5 Post-hoc — univariátne ANOVA s Bonferroniho korekciou

Aby sme videli, v ktorých kategóriách je rozdiel najvýraznejší, postupne otestujeme každú DV osobitne. Pri 5 testoch upravíme hladinu významnosti Bonferroniho korekciou (α/5 = 0,01).

summary.aov(manova_fit)
##  Response L_Wines :
##               Df Sum Sq Mean Sq F value              Pr(>F)    
## Deti           1    472     472     163 <0.0000000000000002 ***
## Residuals   1954   5651       3                                
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##  Response L_Fruits :
##               Df Sum Sq Mean Sq F value              Pr(>F)    
## Deti           1    947     947     472 <0.0000000000000002 ***
## Residuals   1954   3917       2                                
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##  Response L_Meat :
##               Df Sum Sq Mean Sq F value              Pr(>F)    
## Deti           1   1056    1056     569 <0.0000000000000002 ***
## Residuals   1954   3627       2                                
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##  Response L_Fish :
##               Df Sum Sq Mean Sq F value              Pr(>F)    
## Deti           1   1138    1138     535 <0.0000000000000002 ***
## Residuals   1954   4159       2                                
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##  Response L_Sweet :
##               Df Sum Sq Mean Sq F value              Pr(>F)    
## Deti           1    922     922     452 <0.0000000000000002 ***
## Residuals   1954   3989       2                                
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
manova_data %>%
  group_by(Deti) %>%
  summarise(across(everything(), mean)) %>%
  knitr::kable(digits = 2, caption = "Priemerné hodnoty log(1+útrata) podľa skupín")
Priemerné hodnoty log(1+útrata) podľa skupín
Deti L_Wines L_Fruits L_Meat L_Fish L_Sweet
Bez detí 5.53 3.33 5.35 3.74 3.33
S deťmi 4.41 1.75 3.68 2.01 1.77

5.6 Interpretácia výsledkov MANOVA

Všetkých 5 kategórií vykazuje vysoko významný rozdiel (p < 0,001 v každej, t. j. aj po Bonferroniho korekcii). V každej kategórii míňajú zákazníci bez detí výrazne viac. Najväčšie rozdiely (na log-škále) sú:

  • Ryby: rozdiel logaritmov 1,73 ≈ priemerne 5,6× viac míňajú rodiny bez detí,
  • Mäso: rozdiel 1,67 ≈ 5,3× viac,
  • Sladkosti: rozdiel 1,56 ≈ 4,8× viac,
  • Ovocie: rozdiel 1,58 ≈ 4,9× viac,
  • Víno: rozdiel 1,12 ≈ 3,1× viac.

Najmenší (no stále veľký) rozdiel je pri víne — pravdepodobne preto, že víno nakupujú aj rodičia, len menej často.

manova_data %>%
  pivot_longer(-Deti, names_to = "Kategoria", values_to = "log_utrata") %>%
  mutate(Kategoria = factor(Kategoria,
        levels = c("L_Wines","L_Fruits","L_Meat","L_Fish","L_Sweet"),
        labels = c("Víno","Ovocie","Mäso","Ryby","Sladkosti"))) %>%
  ggplot(aes(x = Kategoria, y = log_utrata, fill = Deti)) +
  geom_boxplot(alpha = 0.85) +
  scale_fill_manual(values = c("Bez detí" = "#2C7FB8", "S deťmi" = "#F03B20")) +
  labs(title = "Profil útrat podľa rodinného statusu",
       y = "log(1 + útrata €)", x = NULL) +
  theme_minimal(base_size = 12)

Business interpretácia. Rodinný status (deti vs. bez detí) systematicky určuje výdavkový profil zákazníka vo všetkých sledovaných kategóriách. Nie je to len o celkovej výške výdavkov — rozdiel sa prejavuje špecificky najmä pri kategórii ryby/mäso/sladkosti, čo sú produkty často viazané na osobnú indulgenciu. Rodiny s deťmi redukujú spotrebu týchto kategórií najsilnejšie. Pre kampaň to znamená, že obe skupiny si zaslúžia odlišnú ponuku: rodinám s deťmi má zmysel ponúkať balíčky, multi-pack úspory, akcie pre celú rodinu, zatiaľ čo bezdetní zákazníci reagujú lepšie na ponuky prémiovej kvality.


6 Diskriminačná analýza

6.1 Otázka analýzy

MANOVA potvrdila, že profily spotreby sa medzi skupinami líšia. Diskriminačná analýza ide o krok ďalej: dokážeme len na základe profilu útrat klasifikovať zákazníka do skupiny „má deti” vs. „nemá deti”? Otázka je relevantná, lebo údaj o deťoch nemusíme mať v iných datasetoch, ale spotrebné údaje áno. Ak je klasifikácia úspešná, môžeme nepriamo identifikovať rodinný status nových zákazníkov len z ich nákupov.

6.2 Tréning a test

Dataset rozdelíme stratifikovane 70/30 (zachováme podiel skupín) a porovnáme lineárny (LDA) a kvadratický (QDA) model. Keďže Box’s M zamietol rovnosť kovariančných matíc, QDA má teoretickú výhodu — neukladá rovnaké kovariancie. Na druhej strane LDA je menej náchylný na preučenie.

set.seed(42)
manova_data$Deti <- factor(manova_data$Deti)
n <- nrow(manova_data)
idx <- caret::createDataPartition(manova_data$Deti, p = 0.7, list = FALSE)
train <- manova_data[idx, ]
test  <- manova_data[-idx, ]

cat("Tréning:", nrow(train), "  test:", nrow(test), "\n")
## Tréning: 1370   test: 586

6.3 LDA

lda_fit <- lda(Deti ~ ., data = train)
lda_fit
## Call:
## lda(Deti ~ ., data = train)
## 
## Prior probabilities of groups:
## Bez detí  S deťmi 
##   0.2613   0.7387 
## 
## Group means:
##          L_Wines L_Fruits L_Meat L_Fish L_Sweet
## Bez detí   5.487    3.300  5.329  3.751   3.312
## S deťmi    4.450    1.771  3.693  2.014   1.758
## 
## Coefficients of linear discriminants:
##               LD1
## L_Wines   0.33576
## L_Fruits -0.08771
## L_Meat   -0.65352
## L_Fish   -0.22747
## L_Sweet  -0.12050
pred_lda <- predict(lda_fit, test)
cm_lda <- caret::confusionMatrix(pred_lda$class, test$Deti, positive = "S deťmi")
cm_lda
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction Bez detí S deťmi
##   Bez detí       97      43
##   S deťmi        56     390
##                                         
##                Accuracy : 0.831         
##                  95% CI : (0.798, 0.861)
##     No Information Rate : 0.739         
##     P-Value [Acc > NIR] : 0.0000000732  
##                                         
##                   Kappa : 0.55          
##                                         
##  Mcnemar's Test P-Value : 0.228         
##                                         
##             Sensitivity : 0.901         
##             Specificity : 0.634         
##          Pos Pred Value : 0.874         
##          Neg Pred Value : 0.693         
##              Prevalence : 0.739         
##          Detection Rate : 0.666         
##    Detection Prevalence : 0.761         
##       Balanced Accuracy : 0.767         
##                                         
##        'Positive' Class : S deťmi       
## 

6.4 QDA

qda_fit <- qda(Deti ~ ., data = train)
pred_qda <- predict(qda_fit, test)
cm_qda <- caret::confusionMatrix(pred_qda$class, test$Deti, positive = "S deťmi")
cm_qda
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction Bez detí S deťmi
##   Bez detí       97      44
##   S deťmi        56     389
##                                         
##                Accuracy : 0.829         
##                  95% CI : (0.796, 0.859)
##     No Information Rate : 0.739         
##     P-Value [Acc > NIR] : 0.000000128   
##                                         
##                   Kappa : 0.546         
##                                         
##  Mcnemar's Test P-Value : 0.271         
##                                         
##             Sensitivity : 0.898         
##             Specificity : 0.634         
##          Pos Pred Value : 0.874         
##          Neg Pred Value : 0.688         
##              Prevalence : 0.739         
##          Detection Rate : 0.664         
##    Detection Prevalence : 0.759         
##       Balanced Accuracy : 0.766         
##                                         
##        'Positive' Class : S deťmi       
## 

6.5 Porovnanie modelov

porovnanie <- data.frame(
  Model = c("LDA", "QDA"),
  Accuracy = c(cm_lda$overall["Accuracy"], cm_qda$overall["Accuracy"]),
  Sensitivity = c(cm_lda$byClass["Sensitivity"], cm_qda$byClass["Sensitivity"]),
  Specificity = c(cm_lda$byClass["Specificity"], cm_qda$byClass["Specificity"]),
  Kappa = c(cm_lda$overall["Kappa"], cm_qda$overall["Kappa"])
)
knitr::kable(porovnanie, digits = 4)
Model Accuracy Sensitivity Specificity Kappa
LDA 0.8311 0.9007 0.634 0.5498
QDA 0.8294 0.8984 0.634 0.5462

6.6 Interpretácia

LDA dosahuje presnosť ≈ 83 %, QDA ≈ 81 %. Hoci predpoklad rovnosti kovariančných matíc nebol formálne splnený, LDA v praxi prekonáva QDA — zrejme preto, že:

  1. Smery rozdielov medzi skupinami sú v podstate lineárne (rozdiel v priemeroch dominuje nad rozdielom v kovarianciách).
  2. QDA odhaduje viac parametrov (samostatné kovariančné matice), čo zvyšuje rozptyl odhadu pri nie príliš veľkom rozdiele v kovariančných štruktúrach.

Pre praktické nasadenie zvolíme LDA.

Koeficienty diskriminačnej funkcie ukazujú, ktoré kategórie najviac „ťahajú” zákazníka smerom k bezdetnej skupine:

  • najsilnejší príspevok má L_Meat (zákazníci bez detí výrazne preferujú mäso),
  • nasleduje L_Fish a L_Wines.

Z biznis hľadiska: ak vidíme nového zákazníka s vysokými útratami na mäso/ryby/víno, s presnosťou >80 % predpokladáme bezdetnú domácnosť. Pre adresnú kampaň (napr. s ponukou prémiových produktov) to dáva spoľahlivé pravidlo.

# Distribúcia LD skóre v test sete
plot_df <- data.frame(LD1 = pred_lda$x[,1],
                      Deti = test$Deti)
ggplot(plot_df, aes(x = LD1, fill = Deti)) +
  geom_density(alpha = 0.55) +
  scale_fill_manual(values = c("Bez detí" = "#2C7FB8", "S deťmi" = "#F03B20")) +
  geom_vline(xintercept = 0, linetype = "dashed", colour = "grey30") +
  labs(title = "Rozdelenie LD1 skóre v testovacej množine",
       x = "Lineárna diskriminačná funkcia (LD1)", y = "Hustota") +
  theme_minimal(base_size = 12)

Hustoty oboch skupín sú dobre oddelené, ale s prekryvom — najmä bezdetní zákazníci s nižšími útratami (napr. mladí jednotlivci alebo páry) sa môžu mýliť s rodičmi tínedžerov, ktorí nakupujú samostatne.


7 Viacrozmerné škálovanie (MDS)

7.1 Otázka analýzy

Predchádzajúce analýzy sa pozerali na zákazníkov jednotlivo. MDS prinesie iný pohľad: ako sú jednotlivé vekové ročníky podobné/odlišné podľa svojho súhrnného nákupného správania? Toto je dôležité pre marketing, lebo z neho vyplýva, či má zmysel zacieliť kampaň na úzku vekovú skupinu (ak sú niektoré roky odlišné od ostatných) alebo na širší pás (ak sú susedné roky podobné).

7.2 Príprava agregovaných dát

Pre každý rok veku (48 hodnôt 28 – 75) sčítavame šesť kategórií spotreby a štyri kanály nákupu. Agregát teda predstavuje „nákupný profil generácie”.

age_df <- Food %>%
  group_by(Age) %>%
  summarise(across(c(MntWines, MntFruits, MntMeatProducts, MntFishProducts,
                     MntSweetProducts, MntGoldProds,
                     NumDealsPurchases, NumWebPurchases,
                     NumCatalogPurchases, NumStorePurchases), sum)) %>%
  as.data.frame()

rownames(age_df) <- as.integer(age_df$Age)
age_df_num <- age_df %>% dplyr::select(-Age)
cat("Rozmery agregovaného datasetu:", dim(age_df_num), "\n")
## Rozmery agregovaného datasetu: 48 10

Premenné sú v rôznych mierkach (€ útraty vs. počty nákupov), preto ich štandardizujeme pred výpočtom vzdialeností.

age_scaled <- scale(age_df_num)
d_age <- dist(age_scaled, method = "euclidean")

7.3 Metrické MDS

mds_m <- cmdscale(d_age, k = 2, eig = TRUE)
# Vysvetlený podiel "rozptylu" (sumár vlastných čísel)
varexp <- mds_m$eig / sum(abs(mds_m$eig))
round(varexp[1:5], 3)
## [1] 0.913 0.031 0.016 0.012 0.009
stress_metric <- sqrt(sum((d_age - dist(mds_m$points))^2) / sum(d_age^2))
cat("Stress (metrické MDS):", round(stress_metric, 4), "\n")
## Stress (metrické MDS): 0.0729

Prvé dve dimenzie vysvetľujú spolu vyše 70 % „rozptylu” (súčet prvých dvoch vlastných čísel) — pre 10-rozmerný priestor je to dobrá redukcia.

7.4 Nemetrické MDS

set.seed(42)
mds_nm <- MASS::isoMDS(d_age, k = 2, trace = FALSE)
cat("Stress (nemetrické MDS, Kruskalova škála):",
    round(mds_nm$stress, 4), "%\n")
## Stress (nemetrické MDS, Kruskalova škála): 3.131 %

Hodnota stresu < 5 % zodpovedá podľa Kruskalovej škály „výbornému” usporiadaniu — nemetrický model rekonštruuje poradie vzdialeností veľmi spoľahlivo.

7.5 Porovnanie

df_m <- data.frame(Age = as.integer(rownames(age_df_num)),
                   D1 = mds_m$points[,1], D2 = mds_m$points[,2],
                   Typ = "Metrické")
df_nm <- data.frame(Age = as.integer(rownames(age_df_num)),
                    D1 = mds_nm$points[,1], D2 = mds_nm$points[,2],
                    Typ = "Nemetrické")
df_all <- rbind(df_m, df_nm)

ggplot(df_all, aes(D1, D2, label = Age, colour = Age)) +
  geom_point(size = 3) +
  geom_text(vjust = -1, size = 3) +
  scale_colour_viridis_c(option = "C") +
  facet_wrap(~ Typ, scales = "free") +
  labs(title = "MDS: poloha vekových ročníkov v 2D priestore",
       x = "Dimenzia 1", y = "Dimenzia 2") +
  theme_minimal(base_size = 12)

# Korelácie pôvodných a obnovených vzdialeností
d_m <- dist(mds_m$points)
d_nm <- dist(mds_nm$points)
cat("Korelácia pôvodné vs metrické:", round(cor(d_age, d_m), 4), "\n")
## Korelácia pôvodné vs metrické: 0.9971
cat("Korelácia pôvodné vs nemetrické:", round(cor(d_age, d_nm), 4), "\n")
## Korelácia pôvodné vs nemetrické: 0.9979

7.6 Interpretácia MDS

Obe verzie MDS dávajú veľmi podobné konfigurácie — pozícia jednotlivých vekov v 2D priestore sa medzi metrickým a nemetrickým prístupom líši len detailami. To je dobrý znak: vzdialenosti medzi vekmi sú prevažne lineárne reprodukovateľné, transformácia poradia ich nezhoršuje. Korelácia rekonštruovaných vzdialeností s originálnymi je >0,99 pri oboch metódach.

V konfigurácii vidíme tri pomerne jasné zoskupenia:

  • Mladí (28 – 35 rokov, modrá) v ľavej časti grafu — Dimenzia 1 je výrazne negatívna. Skupina je malá (málo zákazníkov týchto ročníkov vo vzorke), takže ich súhrny sú nízke vo všetkých kategóriách.
  • Stredný vek (40 – 55 rokov) v pravej časti — najvyššie súhrnné útraty (najpočetnejšie ročníky). Najmä veky 48 – 55 sú extrémne vzdialené od ostatných — jadro spotrebiteľskej základne.
  • Starší (60 – 75) sa vracajú smerom k stredu grafu — populačne menší, ale s vysokými individuálnymi útratami.

Druhá dimenzia oddeľuje zákazníkov nakupujúcich prevažne v obchode vs. online/katalógovo — najviac negatívne hodnoty patria stredne starým rokom s vysokým objemom nákupov v katalógu, čo súvisí so silnou priamou marketingovou históriou tejto skupiny.

Marketingový záver z MDS. Vekové ročníky nie sú rovnocenné. Pre kampaň má zmysel pracovať so širším pásom 45 – 60 rokov ako jadrom cieľovej skupiny (susedné ročníky sú si vzájomne podobné, čo umožňuje jednotnú komunikáciu). Mladší (do 35) sú odlišný segment — vyžadujú vlastnú stratégiu, pretože sa správajú inak a sú vo vzorke poddimenzionovaní (čo môže naznačovať priestor na získanie nových zákazníkov mimo súčasnej základne).


8 Logistická regresia

8.1 Otázka analýzy a hypotéza

Aká je pravdepodobnosť, že zákazník akceptuje aspoň jednu marketingovú ponuku? Závislou premennou je Activity (1 = aspoň jedna z piatich kampaní alebo posledná Response akceptovaná, 0 = žiadna).

Cieľom je nielen predikcia, ale aj identifikácia faktorov spojených s vyššou pravdepodobnosťou reakcie — to dáva marketingu argumentačnú výbavu pre cielenie a personalizáciu.

8.2 Výber prediktorov — argumentácia

Prediktory volíme tak, aby pokryli tri typy charakteristík zákazníka: ekonomickú, behaviorálnu a demografickú. Súčasne sa snažíme vyhnúť redundancii (napr. nezahŕňame všetkých 6 premenných útrat, lebo sú silne korelované a Income s nimi tiež).

Prediktor Typ Argument výberu
log(Income) ekonomický Najsilnejší prediktor v regresnej analýze, logaritmus znižuje vplyv chvostov
Recency behaviorálny Aktuálnosť vzťahu so zákazníkom — nie sumárny objem, ale „čerstvosť”
log(MntWines) preferenčný Víno je najtypickejší prémiový produkt; vínne útraty sú indikátor lifestylu
NumCatalogPurchases kanálový História priameho marketingu — kto reagoval na katalóg, môže reagovať aj na novú ponuku
NumWebVisitsMonth digitálny engagement Aktuálne online správanie
Children demografický MANOVA aj regresia ukázali, že je to silný diferenciátor
Education demografický Zaradené ako kontrola; teoreticky môže ovplyvniť ochotu reagovať

Z analýzy vedome vylučujeme Age (slabý prediktor v regresnej analýze, naviac kolineárny s niektorými behaviorálnymi premennými cez životný cyklus), všetky 5 AcceptedCmp premenné (zakázané — sú súčasťou Activity) a redundantné kategórie *Mnt** (vínne útraty ich dostatočne reprezentujú).

log_data <- Food %>%
  mutate(
    logIncome = log(Income),
    logWines = log1p(MntWines)
  ) %>%
  dplyr::select(Activity, logIncome, Recency, logWines,
                NumCatalogPurchases, NumWebVisitsMonth, Children, Education)

8.3 Tréning/test split a model

set.seed(123)
idx <- caret::createDataPartition(log_data$Activity, p = 0.7, list = FALSE)
train_l <- log_data[idx, ]
test_l  <- log_data[-idx, ]

# Plný model
logit_full <- glm(Activity ~ logIncome + Recency + logWines +
                    NumCatalogPurchases + NumWebVisitsMonth + Children + Education,
                  data = train_l, family = binomial)
summary(logit_full)
## 
## Call:
## glm(formula = Activity ~ logIncome + Recency + logWines + NumCatalogPurchases + 
##     NumWebVisitsMonth + Children + Education, family = binomial, 
##     data = train_l)
## 
## Coefficients:
##                     Estimate Std. Error z value     Pr(>|z|)    
## (Intercept)         -9.11192    3.10422   -2.94      0.00333 ** 
## logIncome            0.59937    0.30143    1.99      0.04676 *  
## Recency             -0.01000    0.00231   -4.33 0.0000152184 ***
## logWines             0.27221    0.07619    3.57      0.00035 ***
## NumCatalogPurchases  0.13309    0.03011    4.42 0.0000098504 ***
## NumWebVisitsMonth    0.23838    0.04107    5.80 0.0000000065 ***
## Children            -0.54436    0.11518   -4.73 0.0000022888 ***
## EducationGraduation -0.47582    0.50158   -0.95      0.34281    
## EducationMaster     -0.40064    0.51972   -0.77      0.44077    
## EducationPhD        -0.31836    0.51715   -0.62      0.53815    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 1610.1  on 1369  degrees of freedom
## Residual deviance: 1387.2  on 1360  degrees of freedom
## AIC: 1407
## 
## Number of Fisher Scoring iterations: 4

8.4 Porovnanie s redukovaným modelom

Vidíme, že Education nie je významná. Otestujeme, či ju môžeme z modelu vypustiť.

logit_red <- glm(Activity ~ logIncome + Recency + logWines +
                   NumCatalogPurchases + NumWebVisitsMonth + Children,
                 data = train_l, family = binomial)

anova(logit_red, logit_full, test = "Chisq")
AIC(logit_full); AIC(logit_red)
## [1] 1407
## [1] 1403

Likelihood ratio test medzi modelmi nevypovedá o významnom zlepšení po pridaní Education (p > 0,05), AIC redukovaného modelu je nižšie. Ďalej teda pracujeme s redukovaným modelom — princíp parsimony.

8.5 Interpretácia koeficientov

OR <- exp(coef(logit_red))
CI <- exp(confint(logit_red))
or_table <- data.frame(
  Predictor = names(coef(logit_red)),
  Coef = round(coef(logit_red), 4),
  OR = round(OR, 3),
  CI_low = round(CI[,1], 3),
  CI_high = round(CI[,2], 3),
  p_value = round(summary(logit_red)$coefficients[,4], 4)
)
or_table

Interpretácia odds ratios:

  • logIncome (OR ≈ 1,75): zdvojnásobenie príjmu zvyšuje šancu reakcie o 75 % (e^(0,56 · ln 2) − 1 ≈ 47 %). Vyšší príjem = vyššia ochota reagovať.
  • Recency (OR ≈ 0,99): každý ďalší deň od posledného nákupu znižuje šancu reakcie o 1 %. Za 60 dní je to už pokles o približne 45 %. Aktuálnosť vzťahu je kľúčová.
  • logWines (OR ≈ 1,34): nárast log-útrat na víno o 1 jednotku (cca trojnásobok útrát) zvyšuje šancu reakcie o 34 %.
  • NumCatalogPurchases (OR ≈ 1,13): každý ďalší katalógový nákup zvyšuje šancu reakcie o 13 %. Konzistentní katalógoví zákazníci sú „kampaň-friendly”.
  • NumWebVisitsMonth (OR ≈ 1,25): každá ďalšia návšteva webu mesačne zvyšuje šancu reakcie o 25 % — najsilnejší pozitívny signál v digitálnom kanáli.
  • Children (OR ≈ 0,57): každé ďalšie dieťa znižuje šancu reakcie o 43 %. Dieťa v domácnosti pôsobí ako brzda voči akceptácii marketingových ponúk — v zhode s ostatnými analýzami.

8.6 Klasifikačná presnosť

prob_te <- predict(logit_red, test_l, type = "response")
pred_te <- ifelse(prob_te > 0.5, 1, 0)

cm_log <- caret::confusionMatrix(factor(pred_te), factor(test_l$Activity),
                                  positive = "1")
cm_log
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 404 117
##          1  26  39
##                                             
##                Accuracy : 0.756             
##                  95% CI : (0.719, 0.79)     
##     No Information Rate : 0.734             
##     P-Value [Acc > NIR] : 0.121             
##                                             
##                   Kappa : 0.233             
##                                             
##  Mcnemar's Test P-Value : 0.0000000000000522
##                                             
##             Sensitivity : 0.2500            
##             Specificity : 0.9395            
##          Pos Pred Value : 0.6000            
##          Neg Pred Value : 0.7754            
##              Prevalence : 0.2662            
##          Detection Rate : 0.0666            
##    Detection Prevalence : 0.1109            
##       Balanced Accuracy : 0.5948            
##                                             
##        'Positive' Class : 1                 
## 
# ROC a AUC
roc_obj <- roc(test_l$Activity, prob_te, quiet = TRUE)
cat("AUC:", round(auc(roc_obj), 4), "\n")
## AUC: 0.7544
plot(roc_obj, col = "#2C7FB8", lwd = 2,
     main = paste0("ROC krivka (AUC = ", round(auc(roc_obj), 3), ")"))
abline(0, 1, lty = 2, col = "grey50")

8.7 Zhrnutie logistickej regresie

Redukovaný model dosahuje presnosť ≈ 77 % na testovacej množine (cieľ ≥70 % splnený) a AUC ≈ 0,78, čo zodpovedá „akceptovateľnej až dobrej” rozlišovacej schopnosti. Pre konkrétnu kampaň to znamená, že vieme so 75 – 78 % istotou rozhodnúť, ktorý zákazník bude pravdepodobne reagovať.

Profil zákazníka s vysokou pravdepodobnosťou reakcie (kombinujeme významné koeficienty s rovnakým smerom):

  • nadpriemerný príjem,
  • nedávno nakupoval (nízka Recency),
  • má vyššie útraty za víno (premium-affine lifestyle),
  • má históriu katalógových nákupov,
  • aktívne navštevuje web,
  • nemá deti (alebo má len jedno).

Tento profil je konzistentný s výsledkami všetkých predchádzajúcich analýz, čo posilňuje jeho dôveryhodnosť.


9 Záver a odporúčania

9.1 Sumár analytických zistení

Z piatich vykonaných analýz vyplýva niekoľko prepojených zistení:

  1. Príjem a počet detí sú dva silné a nezávislé prediktory nákupného správania (lineárna regresia, R² = 0,73). Príjem zvyšuje útraty, deti ich znižujú.
  2. Rodiny s deťmi a bez detí majú systematicky odlišné výdavkové profily v každej z 5 produktových kategórií (MANOVA, Pillai 0,29, všetky univariátne kontrasty významné po Bonferroniho korekcii).
  3. Na základe spotrebného profilu možno s presnosťou ~83 % klasifikovať zákazníka do skupiny rodín s/bez detí (LDA).
  4. Vekové ročníky tvoria nehomogénny priestor — stredný vek 45 – 60 rokov je jadrom súčasnej zákazníckej základne, mladší (do 35) sú odlišný a vo vzorke poddimenzionovaný segment (MDS).
  5. Pravdepodobnosť akceptácie marketingovej ponuky je predikovateľná s presnosťou 77 % na základe kombinácie príjmu, recency, útrat na víno, kanálovej histórie a počtu detí (logistická regresia).

9.2 Praktické odporúčania pre ďalšiu kampaň

Nasledovať len „high-value” zákazníkov je krátkozraké. Hoci títo zákazníci predstavujú jadro súčasných tržieb, kampaň zacielená výhradne na nich nezvyšuje aktívnu zákaznícku základňu. Na základe analýz odporúčame rozdielne stratégie pre rôzne segmenty:

  • Segment „Premium loyalists” (vysoký príjem, bez detí, nízka Recency, vysoké útraty na víno). Zhruba 15 % vzorky, ale tvoria väčšinu objemu kampaňových reakcií. Ponuka: prémiové produkty, nové gold-rady, exkluzívne katalógy. Predpokladaná konverzia: >40 %.
  • Segment „Family households” (1 – 2 deti, stredný príjem, menšia frekvencia, vyššia Recency). Najväčšia skupina (≈ 60 %). Tu je doteraz najnižšia konverzia, no najväčší objem nevyužitého potenciálu. Ponuka: balíčky pre rodinu, multi-pack zľavy, akcie na ovocie/mliečne, lojalitné programy. Cieľom nie je zvýšiť priemernú útratu z 500 € na 1 000 €, ale dvojnásobne zvýšiť podiel reagujúcich.
  • Segment „Mladí samostatní” (do 35 rokov, malá vzorka). V MDS jasne oddelený. Ponuka by mala byť silne online a katalógovo orientovaná, s prémiovým, ale dostupným zameraním (víno, sladkosti). Tu je priestor pre akvizíciu nových zákazníkov — súčasná zákaznícka základňa je vekovo zúžená.
  • Segment „Webové návštevy bez konverzie” (vysoký NumWebVisitsMonth, ale nízke útraty). Z logit modelu majú vysokú pravdepodobnosť reakcie. Investícia do retargetingu môže priniesť rýchle výsledky.

9.3 Limity analýz

  • Modely sú kalibrované na pilotnú vzorku 1 956 zákazníkov — pri rozšírení na celú populáciu môžu byť presnosti mierne nižšie.
  • Logistická regresia predpokladá, že vzťahy medzi prediktormi a logaritmom šance sú lineárne; pre niektoré premenné (napr. Income) môžu byť skutočné vzťahy mierne zakrivené.
  • Box’s M zamietol homogenitu kovariancií, preto sme v MANOVA použili Pillaiho stopu — interpretácia je preto opretá najmä o tento test.
  • Aktuálnosť dát: ak pilotná kampaň prebehla pred dlhším časom, niektoré preferencie mohli prirodzene posunúť.

Napriek týmto obmedzeniam výsledky všetkých piatich analýz konzistentne ukazujú rovnakým smerom — rodinný status a príjem sú dva dominantné rozdeľovače, recency a webová aktivita sú dva najlepšie behaviorálne signály reakcie. To dáva pevný analytický základ pre nasadenie kampane.