library(tidyr)
library(tidyverse)
library(lubridate)
library(highcharter)
library(ggplot2)
library(scales)
library(plotly)

I use several libraries to support data visualization process of FnB Retail Company.
note: By considering the depth of analytical process, I will interpret and explain the plots in Bahasa Indonesia.

Case 1 : Retail Company Analysis

Weekly Sales

weekly_graph <-
case1 %>% 
  mutate(dayname = wday(tanggal, label = TRUE),
         date = day(tanggal),
         week = as.factor(week(tanggal)),
         numweek = as.factor(case_when(
           week == "36" ~ "1",
           week == "37" ~ "2",
           week == "38" ~ "3",
           week == "39" ~ "4",
           TRUE ~ "5"
         ))) %>% 
  select(-c(transaction_id, user_id)) %>% 
  arrange(numweek) %>% 
  group_by(numweek,level_member) %>% 
  summarise(weekly_sales = sum(payment_amount),
            qty = sum(quantity)) %>% 
  ungroup() %>%
  group_by(numweek) %>%
  mutate(mean = mean(weekly_sales)) %>% 
  mutate(text = paste(comma(weekly_sales), " | ",qty, "pcs", sep = ""),
         text2 = paste("Sales Avr =",(comma(mean))))

weekly_graph_plot <-
ggplot(weekly_graph, aes(numweek, weekly_sales, fill = level_member, text = text))+
  geom_col(position = "stack", alpha = 0.9) +
  scale_fill_manual(values = c("#FFD700", "#80653d", "#99958e"))+
  # geom_point(aes(y = mean, x = numweek), size = 4, color = "white") +
  scale_y_continuous(limits = c(0, 1000000)) +
  labs(title = "Weekly Sales in September 2020",
       x = "Number of Week",
       y = "Sales Amount",
       fill = "Level Member") +
  theme_classic() +
  theme(legend.position = "right")

weekly_graph_plotly <- ggplotly(weekly_graph_plot, tooltip = "text") %>%
    config(displayModeBar = F)
weekly_graph_plotly

Pertama, melalui plot ini saya ingin menampilkan rekaman data aktivitas perusahaan retail tersebut secara keseluruhan, yaitu pergerakan angka penjualan selama bulan September. Jumlah sales paling tinggi selama bulan September 2020 adalah di minggu ketiga, dimana member level Gold berkontribusi 9 dari 11 satuan total penjualan, lebih dari 80% dari total penjualan keseluruhan di minggu itu. Jika dilihat secara kasat mata, member Gold (kuning) mendominasi angka penjualan pada grafik di atas, dimana waktu intens-nya ada di minggu kedua dan tiga (tengah bulan). Sedangkan level lainnya (Silver dan Non-Member) tidak terlalu banyak, hanya di minggu kelima member dari level Silver yang melakukan pembelian di perusahaan retail ini.

Untuk melihat proporsi pembeli (level member) selama bulan September, kita bisa mengacu pada plot di bawah. Plot pie ini membuktikan bahwa dari rata-rata penjualan secara keseluruhan, member Gold memang berkontribusi paling banyak (>50%) terhadap nilai rata-rata penjualan yaitu senilai 199.215 dari total 385.093 kemudian disusul oleh member Silver dan Non-Member. Sampai di sini, kita bisa menyusun prioritas customer sebagai pemilihan target marketing yang tepat, yaitu dimulai dari Gold - Silver - NonMember.

mem_prop_hchart


Customer Activity

Berikutnya saya mencoba untuk memahami perilaku member dari tiap level dengan melihat hari dan waktu pembelian mereka. Level Non-Member (2 data) tercatat melakukan pembelian pada Minggu malam, serupa dengan kebanyakan member Silver (9 data) yang juga membeli pada malam hari. Bedanya member Silver cenderung memilih hari-hari weekdays seperti Senin, Rabu, dan Kamis.

Member Silver juga tercatat melakukan pembelian di siang hari (9AM-2PM) pada jam makan siang, di hari Jumat dan Minggu. Jam makan siang ini menjadi peak hour bagi member Gold karena kebanyakan membeli di hari weekdays pula (Selasa-Kamis), beberapa di antaranya kerap membeli di waktu petang weekend, serta satu observasi yang membeli sarapan di hari Rabu.

member_activity <-
case1 %>% 
  mutate(hour = hour(tanggal),
         dayname = wday(tanggal, label = TRUE),
         date = day(tanggal),
         hour_label = as.factor(case_when(
           hour %in% c(21:23,0:2) ~ "9PM-2AM",
           hour %in% 9:14 ~ "9AM-2PM",
           hour %in% 15:20 ~ "3PM-8PM",
           TRUE ~ "3AM-8AM"
         )),
         minute = minute(tanggal),
         text = paste(text = paste(comma(payment_amount)," | ", hour,":",minute," ", dayname,", ", date, sep = ""))) %>%
  select(-c(transaction_id, kode_item, user_id))

member_activity$hour_label <- ordered(member_activity$hour_label, levels = c("3AM-8AM", "9AM-2PM", "3PM-8PM", "9PM-2AM"))

member_activity_plot <- ggplot(member_activity, aes(y = hour_label, x = level_member, text = text, color = level_member)) +
  geom_jitter(alpha= 0.6, aes(size = payment_amount)) +
  scale_fill_manual(values = c("#FFD700", "#99958e", "#80653d"))+
    labs(title = paste("Customer Activities in September 2020"),
       y = NULL,
       x = NULL, size = NULL, color = NULL
       ) +
 theme_bw()


member_activity_plotly <- ggplotly(member_activity_plot, tooltip = "text") %>%
    config(displayModeBar = F)
member_activity_plotly

Dari visualisasi di atas, informasi menarik yang bisa diambil adalah tiap level member memiliki perilaku yang berbeda, khususnya Gold dan Silver. Umumnya, member Gold lebih sering membeli di hari weekdays (Selasa-Kamis) saat jam makan siang, hanya beberapa yang membeli di jam sore/malam, itu pun paling telat sekitar jam 5 sore. Sedangkan member Silver tercatat aktif di sore hari dari weekdays tersebut, sisanya memilih having lunch saat menjelang akhir minggu saja.

Hal menarik lainnya yang bisa diinterpretasikan adalah beberapa kemungkinan berikut:

  1. Member Gold memiliki daya beli yang tinggi, punya keberanian untuk menghabiskan uang beberapa kali untuk beristirahat dan makan siang di retail ini. Kemungkinan member dari level ini adalah pekerja kantoran yang sudah cukup mapan, pengusaha/pebisnis, dan sekelasnya yang sudah berkeluarga. Kenapa? Kemungkinan ini muncul karena analisis aktivitas waktu yang terekam, paling malam sekitar pukul 5 sore. Logisnya mereka harus segera sampai rumah setelah selesai bekerja.
  2. Kedua, mengingat member Silver lebih sering membeli di jam malam (sekitar jam 7-8) selama weekdays, hal ini menghasilkan kemungkinan bahwa member Silver terdiri dari orang-orang dewasa muda yang senang menghabiskan waktu di luar rumah hingga malam hari, atau memilih waktu weekend. Daya belinya memang tidak setinggi member Gold, namun kecenderungan mereka memilih malam hari dan weekend bisa jadi didukung oleh promo yang disediakan retail.


Store Performance

Pada plot selanjutnya, saya mencoba untuk menganalisis performa penjualan tiap store selama bulan September. Plot berikut bukan sekedar menampilkan store mana yang memiliki performa paling baik (jika diukur dari angka sales-nya), tetapi juga kita bisa melihat bahwa ternyata target market dari setiap store berbeda. Beberapa store hanya dikunjungi oleh member Gold, store lainnya hanya member Silver, dan hanya satu yang dikunjungi 2 level member.

store_sales <-
  case1 %>% 
  group_by(kode_store, level_member, quantity) %>% 
  summarise(mean = mean(payment_amount)) %>% 
  ungroup() %>% 
  mutate(text = paste(comma(mean), " | ", quantity, "pcs", sep = ""))

store_sales_plot <-
ggplot(store_sales, aes(x = mean, y = kode_store, fill = level_member, text = text)) +
  geom_col(aes(x = mean, y = kode_store)) +
  scale_fill_manual(values = c("#FFD700", "#99958e", "#80653d"))+
  scale_x_continuous(limits = c(0,300000, 50000))+
  labs(title = "Store Performance in September 2020", x = "Mean Sales", y = "Store", fill = "Level Member") +
  theme_classic()

store_sales_plotly <- ggplotly(store_sales_plot, tooltip = "text") %>%
    config(displayModeBar = F)
## Warning: Removed 1 rows containing missing values (position_stack).
store_sales_plotly

Plot ini memberi insight bahwa setiap store punya target market sekaligus metode pemasaran yang (seharusnya) berbeda juga. Jika mengacu pada beberapa plot sebelumnya–dimana kita berupaya untuk memamahi perilaku member dari tiap kelas–penerapan strategi marketing perlu disesuaikan dengan target marketnya masing-masing.


Kesimpulan dan Saran:

Berdasarkan visualisasi data dan interpretasi dari tiap plot, dapat simpulkan bahwa penjualan dari perusahaan retail dipengaruhi oleh karakteristik tertentu dari customer. Dimana dalam hal ini, customer terbagi menjadi 3 kategori: Gold, Silver, dan Non-Member. Walau tidak semua level bisa ditafsirkan karakteristiknya, tapi dengan “mengenal” seperti apa karakteristik customer dari suatu perusahaan dapat sangat membantu meningkatkan angka penjualan di waktu mendatang.

Salah satunya dengan menerapkan strategi marketing dan maintainance yang tepat, misalnya:

  • Untuk store dengan target market level Gold, dapat berfokus dengan menjaga kualitas pelayanan dan produk yang ditawarkan. Hal ini untuk menjamin loyalitas customer terhadap perusahaan. Selain itu juga harus terus menawarkan inovasi berkelas yang sesuai dengan target pasarnya, misalnya promo di jam makan siang (weekdays). Pemilihan waktu promo juga bisa mempertimbangkan kapan angka penjualan dari kelas ini paling tinggi, yaitu minggu ke 2 dan 3, alias tengah bulan.
  • Berkaitan dengan promo pada poin pertama, strategi ini juga bisa menggaet member Silver di jam makan siang (weekdays), sehingga angka penjualan di kloter siang bisa meningkat. Promo lain yang bisa dilakukan adalah dengan modifikasi menu seperti buy1 get1 free di jam malam untuk memberi kesan nyaman dan bersahabat saat mereka berkumpul. Apabila mengacu pada plot pertama, pemilihan waktu promosi yang tepat bisa diterapkan di akhir bulan hingga awal bulan berikutnya: saat-saat subur baru menerima gaji.


Demikian interpretasi dari visualisasi data retail suatu perusahaan. Jika ada masukan atau insight menarik yang belum dikupas, I’ll be so glad to have a discussion with you via email:


Case 2 : Rewarded Customer


How to get transaction data which are rewarded:

  1. First, I make sure that each columns in the table has the right data type. While for this case, I want to extract information from column time in Table 2 (Payment). So I have to make sure that this column in appropriate data type.
  2. I use a function from library(lubridate) to extract the date information–declare it as date column. Later this information will help me to filter and subset the rewarded transaction.
  3. As the conditional statement has two conditions, therefore I subset data that only happen in Oct 14, 2020. After that I set the second condition: price amount must be minimal (grater than or equal to) 75000.
  4. Last, I try to compare–make it apple to apple–the result of subsetted Table 2 with the real rewarded transaction data.
glimpse(raw)
## Rows: 14
## Columns: 4
## $ transaction_id  <chr> "06D3BB5A-48DA-423A", "24117FBC-F869-4B07", "3E703C...
## $ payment_amount  <dbl> 42000, 91500, 55000, 100000, 90000, 40600, 38500, 3...
## $ time            <dttm> 2020-10-01 12:10:06, 2020-10-14 06:28:37, 2020-10-...
## $ raw_customer_id <chr> "87d53c8d-d5a3-11e8", "87d53c8d-d5a3-11e8", "87d53c...
raw <-
raw %>% 
  mutate(date = date(time)) %>% 
  filter(date == "2020-10-14",
         payment_amount >= 75000) %>% 
  arrange(desc(payment_amount))

as.data.frame(c(raw[4],rewarded[3]))
##      raw_customer_id rewarded_customer_id
## 1 9ab86ccb-d59d-11e8   5037794a-d5a1-11e8
## 2 5037794a-d5a1-11e8   9ab86ccb-d59d-11e8
## 3 87d53c8d-d5a3-11e8   cfd27a0e-409f-11e9
## 4 cfd27a0e-409f-11e9   87d53c8d-d5a3-11e8

Thank you :)