Wprowadzenie
Cafe PG to restauracja przy plaży. Menagerowie próbują ustalić jakie kategorie i produkty przynoszą im największy dochód oraz w jakich okresach czasu sprzedaż osiąga najwyższe wartości.
Wprowadzenie danych do R
cafepgsciezka_do_pliku <- "/Users/Agata/Desktop/uczelnia/nazwa projeku/cafe_pg.xlsx"
Sheet1 <- "Sheet1"
cafe_pg <- read_excel(cafepgsciezka_do_pliku, sheet = Sheet1)
cafe_pg<-data.frame(cafe_pg)
data(cafe_pg)
## Warning in data(cafe_pg): data set 'cafe_pg' not found
head(cafe_pg[1:10],3)
## Date Bill.Number Item.Desc Time Quantity
## 1 2020-01-01 G0470115 MINERAL WATER(1000ML) 1899-12-31 13:15:11 1
## 2 2020-01-01 G0470115 MONSOON MALABAR (AULAIT) 1899-12-31 13:15:11 1
## 3 2020-01-01 G0470116 MASALA CHAI CUTTING 1899-12-31 13:17:35 1
## Rate Tax Discount Total Category
## 1 50 11.88 0 61.88 BEVERAGE
## 2 100 23.75 0 123.75 BEVERAGE
## 3 40 9.50 0 49.50 BEVERAGE
str(cafe_pg)
## 'data.frame': 145830 obs. of 10 variables:
## $ Date : POSIXct, format: "2020-01-01" "2020-01-01" ...
## $ Bill.Number: chr "G0470115" "G0470115" "G0470116" "G0470117" ...
## $ Item.Desc : chr "MINERAL WATER(1000ML)" "MONSOON MALABAR (AULAIT)" "MASALA CHAI CUTTING" "MINERAL WATER(1000ML)" ...
## $ Time : POSIXct, format: "1899-12-31 13:15:11" "1899-12-31 13:15:11" ...
## $ Quantity : num 1 1 1 1 1 1 1 1 1 1 ...
## $ Rate : num 50 100 40 50 45 50 40 45 60 45 ...
## $ Tax : num 11.9 23.8 9.5 11.9 10.7 ...
## $ Discount : num 0 0 0 0 0 0 0 0 0 0 ...
## $ Total : num 61.9 123.8 49.5 61.9 55.7 ...
## $ Category : chr "BEVERAGE" "BEVERAGE" "BEVERAGE" "BEVERAGE" ...
Czyszczenie danych
Dodawanie kolumny z datą i godziną w odpowiednim formacie oraz kolumny z dniem tygodnia.
cafe_pg$Time <- strptime(cafe_pg$Time, format="%Y-%m-%d %H:%M:%S")
cafe_pg$Time <- format(cafe_pg$Time, format="%H:%M:%S")
pełna_data <- as.POSIXct(paste(cafe_pg$Date, cafe_pg$Time), format="%Y-%m-%d %H:%M:%S")
cafe_pg$pełna_data = pełna_data
cafe_pg$DzienTygodnia <- weekdays(cafe_pg$pełna_data)
Dodawanie kolumn pomocniczych ID i Total Netto
cafe_pg <- cafe_pg %>%
mutate(ID = 1:n())
cafe_pg <- cafe_pg %>%
mutate(Total_netto = Quantity * Rate)
Walidacja danych
Tworzenie zasad walidacyjnych
rules <- validator(
Total_netto+Tax-Discount == Total
, Quantity >= 1
, Rate >= 0
, Tax >= 0
, Discount >= 0
, Total_netto >=0
, Total >=0
)
cf <- confront(cafe_pg, rules, key="ID")
summary(cf)
## name items passes fails nNA error warning
## 1 V1 145830 145557 273 0 FALSE FALSE
## 2 V2 145830 145830 0 0 FALSE FALSE
## 3 V3 145830 145830 0 0 FALSE FALSE
## 4 V4 145830 145830 0 0 FALSE FALSE
## 5 V5 145830 145830 0 0 FALSE FALSE
## 6 V6 145830 145830 0 0 FALSE FALSE
## 7 V7 145830 145830 0 0 FALSE FALSE
## expression
## 1 abs(Total_netto + Tax - Discount - Total) <= 1e-08
## 2 Quantity - 1 >= -1e-08
## 3 Rate - 0 >= -1e-08
## 4 Tax - 0 >= -1e-08
## 5 Discount - 0 >= -1e-08
## 6 Total_netto - 0 >= -1e-08
## 7 Total - 0 >= -1e-08
barplot(cf, main="cafe_pg")
## Warning: The 'barplot' method for confrontation objects is deprecated. Use
## 'plot' instead
as.data.frame(cf) %>% head()
## ID name value expression
## 1 1 V1 TRUE abs(Total_netto + Tax - Discount - Total) <= 1e-08
## 2 2 V1 TRUE abs(Total_netto + Tax - Discount - Total) <= 1e-08
## 3 3 V1 TRUE abs(Total_netto + Tax - Discount - Total) <= 1e-08
## 4 4 V1 TRUE abs(Total_netto + Tax - Discount - Total) <= 1e-08
## 5 5 V1 TRUE abs(Total_netto + Tax - Discount - Total) <= 1e-08
## 6 6 V1 TRUE abs(Total_netto + Tax - Discount - Total) <= 1e-08
Lokalizacja błędów
error_locations <- locate_errors(cafe_pg, rules)
summary(error_locations)
## Variable:
## name errors missing
## 14 Total_netto 75 0
## 8 Discount 74 0
## 9 Total 67 0
## 7 Tax 57 0
## 1 Date 0 0
## 2 Bill.Number 0 0
## 3 Item.Desc 0 0
## 4 Time 0 0
## 5 Quantity 0 0
## 6 Rate 0 0
## 10 Category 0 0
## 11 pełna_data 0 0
## 12 DzienTygodnia 0 0
## 13 ID 0 0
## Errors per record:
## errors records
## 1 0 145557
## 2 1 273
Zmiana błędów na NA
fixable_data <- replace_errors(cafe_pg, rules)
NA1 <- sum(is.na(cafe_pg))
NA2 <- sum(is.na(fixable_data))
NA_ <- data.frame(NA1 , NA2)
kable(NA_,format = "html", caption = "Braki danych" ) %>%
kable_styling("striped", "hover", "condensed", full_width = F, position = "left")
| NA1 | NA2 |
|---|---|
| 0 | 273 |
Dedukcyjne czyszczenie danych
lr_imputed <- impute_lr(fixable_data, rules)
cells(start=cafe_pg, fixable=fixable_data, impute_lr=lr_imputed
, compare='sequential')
## Object of class cellComparison:
##
## cells(start = cafe_pg, fixable = fixable_data, impute_lr = lr_imputed, compare = "sequential")
##
## start fixable impute_lr
## cells 2041620 2041620 2041620
## available 2041620 2041347 2041620
## still_available 2041620 2041347 2041347
## unadapted 2041620 2041347 2041347
## adapted 0 0 0
## imputed 0 0 273
## missing 0 273 0
## still_missing 0 0 0
## removed 0 273 0
Konfrontacja danych z zasadami po imputacji
cf_after_imputation <- confront(lr_imputed, rules, key="ID")
summary(cf_after_imputation)
## name items passes fails nNA error warning
## 1 V1 145830 145830 0 0 FALSE FALSE
## 2 V2 145830 145830 0 0 FALSE FALSE
## 3 V3 145830 145830 0 0 FALSE FALSE
## 4 V4 145830 145830 0 0 FALSE FALSE
## 5 V5 145830 145830 0 0 FALSE FALSE
## 6 V6 145830 145830 0 0 FALSE FALSE
## 7 V7 145830 145830 0 0 FALSE FALSE
## expression
## 1 abs(Total_netto + Tax - Discount - Total) <= 1e-08
## 2 Quantity - 1 >= -1e-08
## 3 Rate - 0 >= -1e-08
## 4 Tax - 0 >= -1e-08
## 5 Discount - 0 >= -1e-08
## 6 Total_netto - 0 >= -1e-08
## 7 Total - 0 >= -1e-08
barplot(cf_after_imputation, main="cafe_pg after imputation")
## Warning: The 'barplot' method for confrontation objects is deprecated. Use
## 'plot' instead
Brakujące obserwacje
sum(is.na(lr_imputed))
## [1] 0
Obserwacje odstające
boxplot(lr_imputed$Total, main="Rozkład Wartości Całkowitych Rachunków")
plot(lr_imputed$Total)
Dodawanie kolumny Party dla, która zwraca “yes” dla zamówień gdzie quantity jest większe niż 5
lr_imputed <- lr_imputed %>%
mutate(party = ifelse(Quantity >= 5, "yes", "no"))
boxplot(lr_imputed$Total ~ lr_imputed$party, main="Rozkład Wartości Całkowitych Rachunków w Zależności od 'party'", xlab="party", ylab="Total")
Obserwacja odstająca ID:94769 usuwamy w celu “naprawienia” danych - wg opisu wartość powinna być naliczona jako opłata za imprezę w wysokości 1x 500zł, a została naliczona 23x
lr_imputed[94769, "Quantity"] <- 1
500*23
## [1] 11500
2731.25/11500 #podatek wynosi 23,75%
## [1] 0.2375
500*0.2375
## [1] 118.75
lr_imputed[94769, "Tax"] <- 118.75
cafe_pg_clear <- impute_lr(lr_imputed, rules)
cafe_pg_clear[94769, "Total_netto"] <- 500
cafe_pg_clear[94769, "Total"] <- 618.75
cafe_pg_clear <- cafe_pg_clear %>%
mutate(party = ifelse(Quantity >= 5, "yes", "no"))
boxplot(cafe_pg_clear$Total ~ cafe_pg_clear$party, main="Rozkład Wartości Całkowitych Rachunków w Zależności od 'party'", xlab="party", ylab="Total")
Aby ustalić podatek dla nowej wartości, wyliczamy podatek z pierwotnych danych. Podatek od 500 zł wynosi 118,75 zł. Podmiana total netto i total dla błędnej obserwacji ID:94769. Widzimy, że nawet w kategorii “party” wartości powyżej 2000 są wartościami odstającymi. Sprawdzamy więc jaki stanowią one udział w całkowitym przychodzie baru.
sum(cafe_pg_clear$Total>=2000)/sum(cafe_pg_clear$Total)*100
## [1] 0.0001463965
Przychody z takich paragonów stanowią marginalny ułamek całkowitych przychodów, a przekłamują i utrudniają analizy. W związku z tym do dalszych analiz będziemy się posługiwać zbiorem z paragonami poniżej 2000$.
cafe_do_2000 <- subset(cafe_pg_clear,Total<=2000)
Wizualizacja danych
Najczęściej wybierana kategoria i pozycja
kategorie <- table(cafe_do_2000$Category)
kategoria <- names(kategorie[which.max(kategorie)])
pozycje <- table(cafe_do_2000$Item.Desc)
pozycja <- names(pozycje[which.max(pozycje)])
popular <- data.frame(kategoria, pozycja)
kable(popular,format = "html", caption = "Najpopularniejsze" ) %>%
kable_styling("striped", "hover", "condensed", full_width = F, position = "left")
| kategoria | pozycja |
|---|---|
| FOOD | NIRVANA HOOKAH SINGLE |
Klienci najczęściej kupują w barze Cafe PG jedzenie. Najpopularniejszą pozycją w menu są papierosy “Nirvana Hookah Single”.
Najczęściej wybierane pozycje w danej kategorii
napoje <- subset(cafe_do_2000, Category == "BEVERAGE")
napoje1 <- table(napoje$Item.Desc )
napój <- names(napoje1[which.max(napoje1)])
jedzenie <- subset(cafe_do_2000, Category == "FOOD")
jedzenie1 <- table(jedzenie$Item.Desc)
posiłek <- names(jedzenie1[which.max(jedzenie1)])
alkohol <- subset(cafe_do_2000, Category == "LIQUOR")
alkohol1 <- table(alkohol$Item.Desc)
alkohol <- names(alkohol1[which.max(alkohol1)])
alktyton <- subset(cafe_do_2000, Category == "LIQUOR & TPBACCO")
alktyton1 <- table(alktyton$Item.Desc)
alkohol_tyton <- names(alktyton1[which.max(alktyton1)])
produkty <- subset(cafe_do_2000, Category == "MERCHANDISE")
produkty1 <- table(produkty$Item.Desc)
detaliczne <- names(produkty1[which.max(produkty1)])
inne <- subset(cafe_do_2000, Category == "MISC")
inne1 <- table(inne$Item.Desc)
inny <- names(inne1[which.max(inne1)])
tyton <- subset(cafe_do_2000, Category == "TOBACCO")
tyton1 <- table(tyton$Item.Desc)
papierosy <- names(tyton1[which.max(tyton1)])
wina <- subset(cafe_do_2000, Category == "WINES")
wina1 <- table(wina$Item.Desc)
wino <- names(wina1[which.max(wina1)])
najlepsz_pozycje <- data.frame(napój= napój, posiłek= posiłek, alkohol= alkohol, alkohol_tyton= alkohol_tyton, detaliczne= detaliczne, inny= inny, papierosy= papierosy, wino= wino)
kable(najlepsz_pozycje,format = "html", caption = "Najczęściej wybierana pozycja" ) %>%
kable_styling("striped", "hover", "condensed", full_width = F, position = "left")
| napój | posiłek | alkohol | alkohol_tyton | detaliczne | inny | papierosy | wino |
|---|---|---|---|---|---|---|---|
| CAPPUCCINO | OCEAN SPECIAL SHAKE | CARLSBERG | BEER HOOKAH | OCEAN SPECIAL T-SHIRTS | ADD ON S | NIRVANA HOOKAH SINGLE | VLN CAB SAUV (GLS) |
Przedstawienie najczesciej wybieranych produktów w poszczególnych kategoriach
top_items <- cafe_do_2000 %>%
group_by(Category, Item.Desc) %>%
summarize(Count = n(), .groups = 'drop') %>%
arrange(Category, desc(Count)) %>%
group_by(Category, .drop = TRUE) %>%
slice_max(Count, n = 1) %>%
ungroup()
ggplot(top_items, aes(x = Category, y = Count, fill = Item.Desc)) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = Count), position = position_dodge(width = 0.9), vjust = -0.5, color = "black", size = 3) +
labs(title = "Najczęściej wybierane produkty w podziale na kategorie",
x = "Kategoria",
y = "Liczba wyborów",
fill = "Produkt") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Powyższy wykres przedstawia w wizualny sposób informacje z poprzedniej tabeli z najczęściej wybieranymi produktami w podziale na kategorie.
Przedstawienie najczesciej wybieranych produktów ogółem
top_products <- cafe_do_2000 %>%
group_by(Item.Desc) %>%
summarize(OrderCount = n()) %>%
top_n(10, OrderCount)
ggplot(top_products, aes(x = reorder(Item.Desc, -OrderCount), y = OrderCount)) +
geom_bar(stat = "identity", fill = "orange") +
geom_text(aes(label = OrderCount), vjust = -0.5, color = "black", size = 3) +
labs(title = "Najpopularniejsze produkty",
x = "Produkt",
y = "Liczba zamówień") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Na poprzednim wykresie ukazano najbardziej popularne produkty dla każdej kategorii. Na powyższym wykresie zauważyć można jakie produkty były wybierane najczęściej spośród wszystkich - bez względu na kategorie. Zdecydowanym zwycięzcą jest “Nirvana hookah single” wybierany 8553 razy, na drugim miejscu znalazł się produkt “Mint flavour single” wybrany 5817 razy. 2 pierwsze miejsca na podium należą do kategorii Tobacco, dopiero na trzecim miejscu znajduje się Cappucino, które było zamawiane prawie 5500 razy.
Przedstawienie najdroższych produktów w każdej kategorii
top_expensive_items <- cafe_do_2000 %>%
group_by(Category, Item.Desc) %>%
dplyr::summarize(Max_Rate = max(Rate), .groups = 'drop') %>%
arrange(Category, desc(Max_Rate)) %>%
group_by(Category, .drop = TRUE) %>%
slice_max(Max_Rate, n = 1) %>%
ungroup()
ggplot(top_expensive_items, aes(x = reorder(Category, -Max_Rate), y = Max_Rate, fill = reorder(Item.Desc, -Max_Rate))) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = paste0(Max_Rate, " zł")), vjust = -0.5, color = "black", size = 3) +
labs(title = "Najdroższe produkty w podziale na kategorie",
x = "Kategoria",
y = "Maksymalna cena jednostkowa",
fill = "Produkt") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Ten wykres przedstawia najdroższe produkty w każdej kategorii, gdzie dla każdej kategorii wybierany jest produkt o najwyższej maksymalnej cenie jednostkowej. Jak można zauważyć, najdroższa pozycja znajduje się w kategorii Merchandise i jest to “Flabor 1000 GMS” o cenie 1470, następnie wśród Alkoholi i kategorii inne są pozycje o wartości 1300zł (kolejno “Stella 1ltr 2+1” i “Hoegaarden ltr mugs (2+1)”). Na trzecim miejscu w najdroższych pozycjach znajduje się wino “Sula Brut” o wartości 1200zł.
Analiza popularności kategorii w zależności od liczby osób przy zamówieniu
party_data <- cafe_do_2000 %>%
filter(party %in% c("yes", "no"))
party_data$party_label <- ifelse(party_data$party == "yes", "Grupa imprezowa", "Grupa nie-imprezowa")
ggplot(party_data, aes(x = Category, y = after_stat(count))) +
geom_bar(fill = "blue", stat = "count") +
geom_text(aes(label = after_stat(count)), stat = "count", vjust = -0.3, color = "black", size = 3) +
labs(title = "Najczęściej wybierane kategorie w zależności od ilości osób w grupie",
x = "Kategoria",
y = "Liczba wyborów") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
facet_wrap(~party_label, scales = "free_y", ncol = 1) +
scale_y_continuous(expand = expansion(mult = c(0, 0.2)))
Na powyższych wykresach widoczne jest porównanie najczęściej wybieranych kategorii, w zależności od ilości osób, które przyjmuje się, że uczestniczyły w zamówieniu. Przy 5 osobach i więcej zakłada się, że była to Grupa imprezowa, natomiast przy 4 osobach i mniej - Grupa nie-imprezowa. Widoczne są duże różnice w wyborach zamówieniowych. Wśród grup imprezowych zamawiane były przede wszystkim napoje alkoholowe, na drugim miejscu napoje bezalkoholowe, a na miejscu trzecim jedzenie. Jeśli chodzi o grupy nie-imprezowe, najczęściej zamawiano jedzenie, napoje bezalkoholowe i na trzecim miejscu wyroby tytoniowe. Napoje alkoholowe były na miejscu trzecim.
Na podstawie tego zestawienia, możliwą sugestią jest wprowadzenie promocji na napoje alkoholowe, lub godzin “Happy Hour” - promocje typu 3 za 2, 4+1 gratis, mogłoby przyczynić się do wzrostu przychodów z napojów alkoholowych. Kolejną propozycją dla tego obiektu, byłoby wprowadzenie promocji na jedzenie przy zakupie alkoholi - np. Frytki za pół ceny przy wydaniu 100 zł na barze - co mogłoby poprawić sprzedaż jedzenia przy grupach imprezowych.
Wykres prezentujący liczbę zamówień w posczególnych dniach tygodnia według kategorii produktów
orders_by_day <- cafe_do_2000 %>%
group_by(DzienTygodnia, Category) %>%
summarize(OrderCount = n()) %>%
mutate(DzienTygodnia = factor(DzienTygodnia, levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")))
## `summarise()` has grouped output by 'DzienTygodnia'. You can override using the
## `.groups` argument.
ggplot(orders_by_day, aes(x = DzienTygodnia, y = OrderCount, fill = Category)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Liczba zamówień w danym dniu tygodnia",
x = "Dzień tygodnia",
y = "Liczba zamówień",
fill = "Kategoria") +
theme_minimal()
Wykres słupkowy ilustruje liczbę zamówień w Cafe PG dla różnych kategorii produktów w zależności od dnia tygodnia, od poniedziałku do niedzieli. Wykres pokazuje, że niektóre dni tygodnia charakteryzują się większą liczbą zamówień niż inne, z czwartkiem i piątkiem jako najbardziej ruchliwymi dniami, co sugeruje wzmożoną aktywność klientów na początku weekendów. Porównując popularność poszczególnych kategorii można zauważyć proporcjonarne różnice w liczbie zamówień na przestrzeni tygodnia.
Wykres przedstawiający gęstość rozkładu wysokości rachunków pod względem kategorii kupowanych pozycji
ggplot(cafe_do_2000, aes(x= Total, fill=Category)) +
geom_density(alpha=.25) +
labs(title = 'Gęstość rozkładu rachunków pod względem kategorii')
Prezentowany wykres gęstości pokazuje rozkład wysokości rachunków w Cafe PG dla różnych kategorii produktów. Każda kategoria jest oznaczona unikalnym kolorem i reprezentuje gęstość rozkładu całkowitej wartości rachunków związanych z daną kategorią. Obszary, gdzie krzywe są wyższe, wskazują na większą częstość występowania rachunków o określonej wysokości. Na przykład, szerokie bazy krzywych dla kategorii takich jak “Alkohole & Tytoń”, “Wina” mogą wskazywać na większą zmienność w wartościach rachunków, podczas gdy węższe szczyty dla kategorii “Napoje”, “Jedzenie”, “Tytoń” czy “Inne” mogą sugerować, że rachunki dla danej kategorii są bardziej skoncentrowane wokół konkretnej kwoty. Zgodnie z wykresem można powiedzieć, że “Napoje” kumulują się wokół wartości 0-120zł, kategoria “Jedzenie” skupia się głównie wokół wartości 50-250zł, “Alkohole” - są bardziej rozproszone na osi, ale największy sczyt znajduje się w okolicy 200zł, “Alholol&Tytoń” jest jedną z najbardziej rozproszonych kategorii, z największym skupieniem w okolicy wartości 500-900zł. Kategoria “Inne” charakteryzuje się największą gęstością dla wartości między 0-50zł. Dla kategorii “Tytyoń” obserwowane jest kilka wzrostów w częstości występowania rachunków - największy dla wartości w okolicach 300zł, następnie dla ok 370zł i 450 zł. Dla kategorii “Wina” rachunki rozkładają się najczęściej wokół wartości 200-250 zł, 400zł oraz 1000zł.
Rozkład skumulowanych przychodów według dni tygodnia
total_by_day <- cafe_do_2000 %>%
mutate(DzienTygodnia = factor(DzienTygodnia, levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))) %>%
group_by(DzienTygodnia) %>%
dplyr::summarize(TotalSum = sum(Total, na.rm = TRUE))
ggplot(total_by_day, aes(x = DzienTygodnia, y = TotalSum)) +
geom_bar(stat = "identity", fill = "blue") +
geom_text(aes(label = paste0(TotalSum, " zł")), vjust = -0.5, color = "black", size = 3) + # Dodanie etykiet w zł
labs(title = "Suma przychodów w zależności od dnia tygodnia",
x = "Dzień tygodnia",
y = "Suma wartości 'Total'")
Cafe PG najwyższe przychodzy osiągała kolejno w czwartki, piątki, soboty. Najniższe za to we wtorki i poniedziałki.
Sprzedaż w cafe pg według godzin i dni tygodnia
heatmap_data <- cafe_do_2000 %>%
mutate(Hour = as.numeric(format(strptime(Time, "%H:%M:%S"), "%H")),
DzienTygodnia = factor(DzienTygodnia, levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))) %>%
group_by(DzienTygodnia, Hour) %>%
dplyr::summarize(TotalSales = sum(Total, na.rm = TRUE), .groups = 'drop')
# Tworzenie mapy cieplnej
ggplot(heatmap_data, aes(x = Hour, y = DzienTygodnia, fill = TotalSales)) +
geom_tile() +
labs(title = "Analiza sprzedaży w posczególnych godzinach",
x = "Godzina",
y = "Dzień tygodnia",
fill = "Suma sprzedaży") +
scale_fill_viridis_c() +
theme_minimal()
Powyższy wykres mapy cieplnej prezentuje przedstawia analizę sprzedaży w poszczególnych godzinach w zależności od dnia tygodnia. Im jaśniejszy kolor tym większa suma sprzedaży występowała w danym dniu tygodnia o danej godzinie. Na wykresie widać, że największe obroty finansowe występowały w godzinach wieczornych i nocnych. Najwyższe wartości sprzedaży kawiarnia osiągała w czwartki w godzinach 23:00-24:00, w środy między godziną 22:00 i 1:00 (noce ze środy na czwartki), a także w piątki między godzinami 17:00-24:00. Najniższa sprzedaż miała miejsce w godzinach porannych (9:00 do 12:00) oraz w godzinach nocnych (2:00-6:00). Patrząc na wykres widać wzmożoną sprzedaż w godzinach nocnych (3:00-5:00) w czwartki, co może świadczyć o stałych imprezach w noc ze środy na czwartki.
Analiza opisowa
Tabela rozkładu przychodów wg kategorii
cafe_do_2000 %>%
select(Total,Category) %>%
tbl_summary(
by=Category,
type = all_continuous() ~ "continuous2",
statistic = all_continuous() ~ c(
"{N_nonmiss}","{mean}","{sd}",
"{median} ({p25}, {p75})",
"{min}, {max}"),
missing = "no",
label = Total ~ "Przychód") %>%
modify_header(label ~ "**Kategoria**") %>%
modify_caption("**Tabela 1. Rozkład przychodu wg kategorii**") %>%
bold_labels() %>%
add_p(pvalue_fun = ~ style_pvalue(.x, digits = 2))
| Kategoria | BEVERAGE, N = 43,572 | FOOD, N = 57,018 | LIQUOR, N = 6,177 | LIQUOR & TPBACCO, N = 54 | MERCHANDISE, N = 487 | MISC, N = 1,187 | TOBACCO, N = 36,490 | WINES, N = 797 | p-value1 |
|---|---|---|---|---|---|---|---|---|---|
| Przychód | <0.001 | ||||||||
| N | 43,572 | 57,018 | 6,177 | 54 | 487 | 1,187 | 36,490 | 797 | |
| Mean | 126 | 174 | 331 | 788 | 246 | 163 | 397 | 402 | |
| SD | 86 | 77 | 248 | 281 | 204 | 219 | 118 | 295 | |
| Median (IQR) | 105 (74, 142) | 167 (136, 198) | 295 (158, 394) | 660 (660, 660) | 225 (100, 295) | 111 (19, 161) | 330 (323, 462) | 221 (221, 441) | |
| Range | 0, 1,485 | 0, 1,856 | 92, 1,969 | 644, 1,980 | 0, 1,654 | 19, 1,782 | 88, 1,980 | 0, 1,512 | |
| 1 Kruskal-Wallis rank sum test | |||||||||
Na podstawie wykorzystanego testu Kruskal-Wallis wynik P-value mniejsze niż 0.001 oznacza, że różnice w przychodach między kategoriami są statystycznie znaczące, co oznacza, że prawdopodobnie różnice te nie są przypadkowe.
• Kategoria “LIQUOR & TOBACCO” ma najwyższą średnią przychodów, co sugeruje, że rachunki w tej kategorii są zazwyczaj wyższe niż w innych kategoriach. • Kategoria “WINES” ma największe odchylenie standardowe oraz największy zakres, co wskazuje na dużą różnorodność w przychodach z rachunków - od bardzo niskich do bardzo wysokich. • Kategoria “FOOD” ma najwięcej transakcji (N), ale jej mediana przychodów jest niższa niż w kategorii “LIQUOR & TOBACCO”, co może wskazywać, że jedzenie jest częściej kupowane, ale pojedyncze rachunki są zazwyczaj niższe. • Istotne różnice w medianach, wsparte przez bardzo niską wartość p-value w teście Kruskala-Wallisa, sugerują, że różne kategorie produktów generują różne poziomy przychodów.
Wnioskowanie i testy statystyczne
Sprawdzenie czy w poszczególnych dniach tygodnia udział konkretnych kategorii w sprzedaży istotnie się różni.
H0: Udział kategorii nie różni się istotnie w poszczególnych dniach tygodnia. H1: Udział kategorii istotnie różni się w poszczególnych dniach tygodnia
alfa = 0.05
ggbarstats(
data = cafe_do_2000,
x = Category,
y = DzienTygodnia,
title = "Udział kategorii produktów w danym dniu",
xlab = "kategoria",
legend.title = "Kategoria",
ggplot.component = list(ggplot2::scale_x_discrete(guide = ggplot2::guide_axis(n.dodge = 2))),
palette = "Set2"
)
## Number of labels is greater than default palette color count.
## • Select another color `palette` (and/or `package`).
Interpretacja: Wartość p jest mniejsza od założonego poziomu istotności w każdym przypadku, należy więc odrzucić H0 na korzyść hipotezy alternatywnej. Udział kategorii różni się istotnie w poszczególnych dniach tygodnia.
Czy wysokość rachunków różni się istotnie w grupach “party” i wśród regularnych klientów?
H0: Wysokość rachunków nie różni się istotnie w grupach “party” H1: Wysokość rachunków różni się istotnie w grupach “party”
alfa = 0.05
ggbetweenstats(
data = cafe_do_2000,
x = party,
y = Total,
title = "Wysokość rachunków z imprez i pojedynczych klientów"
)
Interpretacja: Wartość p jest mniejsza od założonego poziomu istotności, należy więc odrzucić H0 na korzyść hipotezy alternatywnej. Wysokość rachunków różni się istotnie w grupach “party”.
Czy wysokość rachunków ma rozkład normalny?
H0: Wysokość rachunków ma rozkład normalny H1: Wysokość rachunków nie ma rozkładu normalnego
alfa = 0.05
gghistostats(
data = cafe_do_2000,
x = Total,
title = "Wysokość rachunków",
test.value = 224,
binwidth = 50,
normal.curve = TRUE,
normal.curve.args = list(color = "red", size = 1),
)
Interpretacja: Wartość p jest większa od założonego poziomu istotności, nie ma więc podstaw do odrzucenia H0. Wysokość rachunków ma rozkład normalny.
Czy między kategoriami są istotne statystycznie różnice w wysokości rachunków?
H0: Nie ma istotnej statystycznie różnicy w wysokościach rachunków między różnymi kategoriami produktów. H1: Jest istotna statystycznie różnica w wysokościach rachunków między różnymi kategoriami produktów.
alfa = 0.05
ggdotplotstats(
data = cafe_do_2000,
y = Category,
x = Total,
test.value = 224,
type = "robust",
title = "Rozkład wysokości rachunków we wszystkich kategoriach",
xlab = "Kategorie"
)
Interpretacja: Wartość p jest większa od założonego poziomu istotności, nie ma więc podstaw do odrzucenia H0. Nie ma istotnej statystycznie różnicy w wysokościach rachunków między różnymi kategoriami produktów.
Wnioski Końcowe
Cafe PG to lokal gastronomiczny charakteryzujacy się zróżnicowanym menu. Na podstawie przeprowadzonych wykresów i testów statystycznych można przedstawić temu lokalowi kilka rekomendacji.
- W celu ograniczenia kosztów i czasu pracy proponuje się skrócenie menu i skupienie się na sprzedaży produktów najczęściej wybieranych, które są jednocześnie najbardziej dochodowe.
- Sprzedaż jest niższa w godzinach porannych. Propozycją jest ograniczenie godzin otwarcia w najmniej dochodowych dniach tygodnia (poniedziałek i wtorek). Lokal mógłby się otwierać po godzinie 15, co pozwoliłoby na minimalizację kosztów eksploatowania lokalu.
- W godzinach wieczornych i nocnych, gdy odnotowywana jest wzmożona sprzedaż, można rozważyć wprowadzenie promocji, happy hour czy specjalnych zestawów na wieczór. To może dodatkowo zachęcić klientów do odwiedzania lokalu w tych porach. Dobrym pomysłem byłoby dobranie w zestawy produktów sezonowych, co będzie skutecznym posunięciem marketingowym - promocja czasowa odziałuje psychologicznie i zachęca klientów do skorzystania z oferty.
- Weekend oraz dni poprzedzające weekend są najbardziej dochodowe. Aby maksymalizować zyski w inne dni tygodnia (np. poniedziałek lub wtorek) proponuje się wprowadzenie wieczorków tematycznych np.: koncerty z muzyką na żywo, quizy i gry zespołowe, degustacja potraw.
- Należy pamiętać o dostosowywaniu liczby personelu do zapotrzebowania. Zaleca się mniej osób na zmianach w godzinach porannych i więcej w godzinach wieczornych.
- Dostępność różnorodnych kategorii produktów jest dobra dla lokalu, zainteresowanie bogatą ofertą wśród klientów widać w analizach sprzedaży poszczególnych kategorii. Rekomendujemy zachowanie obecnej struktury, przy zmniejszeniu liczby produktów dostępnych w danej kategorii.