The business problem I am trying to address is how can we potentially sell more products with the highest sales volume per unit as we currently do not sell many of these units. This can be accomplished by first identifying these products then further analyzing to find trends and patterns that can help us achieve more sales.
Offering deals as well as campaigning for these high value items may potentially draw customers away from competitors that are selling the same products but at a higher volume than us.
suppressWarnings(suppressMessages(library(tidyverse)))
suppressWarnings(suppressMessages(library(completejourney)))
suppressWarnings(suppressMessages(library(dplyr)))
suppressWarnings(suppressMessages(library(purrr)))
suppressWarnings(suppressMessages(library(ggplot2)))
suppressWarnings(suppressMessages(library(rmarkdown)))
suppressWarnings(suppressMessages(library(lubridate)))
completejourney::campaign_descriptions
## # A tibble: 27 × 4
## campaign_id campaign_type start_date end_date
## <chr> <ord> <date> <date>
## 1 1 Type B 2017-03-03 2017-04-09
## 2 2 Type B 2017-03-08 2017-04-09
## 3 3 Type C 2017-03-13 2017-05-08
## 4 4 Type B 2017-03-29 2017-04-30
## 5 5 Type B 2017-04-03 2017-05-07
## 6 6 Type C 2017-04-19 2017-05-21
## 7 7 Type B 2017-04-24 2017-05-28
## 8 8 Type A 2017-05-08 2017-06-25
## 9 9 Type B 2017-05-31 2017-07-02
## 10 10 Type B 2017-06-28 2017-07-30
## # ℹ 17 more rows
completejourney::get_transactions()
## # A tibble: 1,469,307 × 11
## household_id store_id basket_id product_id quantity sales_value retail_disc
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 900 330 31198570044 1095275 1 0.5 0
## 2 900 330 31198570047 9878513 1 0.99 0.1
## 3 1228 406 31198655051 1041453 1 1.43 0.15
## 4 906 319 31198705046 1020156 1 1.5 0.29
## 5 906 319 31198705046 1053875 2 2.78 0.8
## 6 906 319 31198705046 1060312 1 5.49 0.5
## 7 906 319 31198705046 1075313 1 1.5 0.29
## 8 1058 381 31198676055 985893 1 1.88 0.21
## 9 1058 381 31198676055 988791 1 1.5 1.29
## 10 1058 381 31198676055 9297106 1 2.69 0
## # ℹ 1,469,297 more rows
## # ℹ 4 more variables: coupon_disc <dbl>, coupon_match_disc <dbl>, week <int>,
## # transaction_timestamp <dttm>
completejourney::products
## # A tibble: 92,331 × 7
## product_id manufacturer_id department brand product_category product_type
## <chr> <chr> <chr> <fct> <chr> <chr>
## 1 25671 2 GROCERY Natio… FRZN ICE ICE - CRUSH…
## 2 26081 2 MISCELLANEOUS Natio… <NA> <NA>
## 3 26093 69 PASTRY Priva… BREAD BREAD:ITALI…
## 4 26190 69 GROCERY Priva… FRUIT - SHELF S… APPLE SAUCE
## 5 26355 69 GROCERY Priva… COOKIES/CONES SPECIALTY C…
## 6 26426 69 GROCERY Priva… SPICES & EXTRAC… SPICES & SE…
## 7 26540 69 GROCERY Priva… COOKIES/CONES TRAY PACK/C…
## 8 26601 69 DRUG GM Priva… VITAMINS VITAMIN - M…
## 9 26636 69 PASTRY Priva… BREAKFAST SWEETS SW GDS: SW …
## 10 26691 16 GROCERY Priva… PNT BTR/JELLY/J… HONEY
## # ℹ 92,321 more rows
## # ℹ 1 more variable: package_size <chr>
completejourney::demographics
## # A tibble: 801 × 8
## household_id age income home_ownership marital_status household_size
## <chr> <ord> <ord> <ord> <ord> <ord>
## 1 1 65+ 35-49K Homeowner Married 2
## 2 1001 45-54 50-74K Homeowner Unmarried 1
## 3 1003 35-44 25-34K <NA> Unmarried 1
## 4 1004 25-34 15-24K <NA> Unmarried 1
## 5 101 45-54 Under 15K Homeowner Married 4
## 6 1012 35-44 35-49K <NA> Married 5+
## 7 1014 45-54 15-24K <NA> Married 4
## 8 1015 45-54 50-74K Homeowner Unmarried 1
## 9 1018 45-54 35-49K Homeowner Married 5+
## 10 1020 45-54 25-34K Homeowner Married 2
## # ℹ 791 more rows
## # ℹ 2 more variables: household_comp <ord>, kids_count <ord>
completejourney::campaigns
## # A tibble: 6,589 × 2
## campaign_id household_id
## <chr> <chr>
## 1 1 105
## 2 1 1238
## 3 1 1258
## 4 1 1483
## 5 1 2200
## 6 1 293
## 7 1 529
## 8 1 536
## 9 1 568
## 10 1 630
## # ℹ 6,579 more rows
completejourney::coupons
## # A tibble: 116,204 × 3
## coupon_upc product_id campaign_id
## <chr> <chr> <chr>
## 1 10000085207 9676830 26
## 2 10000085207 9676943 26
## 3 10000085207 9676944 26
## 4 10000085207 9676947 26
## 5 10000085207 9677008 26
## 6 10000085207 9677052 26
## 7 10000085207 9677385 26
## 8 10000085207 9677479 26
## 9 10000085207 9677791 26
## 10 10000085207 9677878 26
## # ℹ 116,194 more rows
completejourney::coupon_redemptions
## # A tibble: 2,102 × 4
## household_id coupon_upc campaign_id redemption_date
## <chr> <chr> <chr> <date>
## 1 1029 51380041013 26 2017-01-01
## 2 1029 51380041313 26 2017-01-01
## 3 165 53377610033 26 2017-01-03
## 4 712 51380041013 26 2017-01-07
## 5 712 54300016033 26 2017-01-07
## 6 2488 51200092776 26 2017-01-10
## 7 2488 51410010050 26 2017-01-10
## 8 1923 53000012033 26 2017-01-14
## 9 1923 54300021057 26 2017-01-14
## 10 1923 57047091041 26 2017-01-14
## # ℹ 2,092 more rows
gett <- get_transactions()
product_summary <- gett %>%
group_by(product_id) %>%
summarize(total_sales_value = sum(sales_value),
total_quantity = sum(quantity))
product_summary <- product_summary %>%
filter(total_quantity > 0)
product_summary <- product_summary %>%
mutate(sales_value_per_unit = total_sales_value / total_quantity)
product_summary <- product_summary %>%
arrange(desc(sales_value_per_unit))
top_products_with_names <- product_summary %>%
left_join(products, by = "product_id")
top_products_with_names <- head(top_products_with_names, 25)
TPN <- top_products_with_names
# The plot ----------------------------------------------------------------
average_sales_per_type <- TPN %>%
group_by(product_type) %>%
summarize(average_sales_value_per_unit = mean(sales_value_per_unit))
average_sales_per_type <- average_sales_per_type %>%
mutate(product_type = reorder(product_type, -average_sales_value_per_unit))
ploths <- ggplot(average_sales_per_type, aes(x = product_type, y = average_sales_value_per_unit)) +
geom_bar(stat = "identity", fill = "lightblue") +
labs(title = "Average Sales Value Per Unit by Product Type",
x = "Product Type",
y = "Average Sales Value Per Unit") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
print(ploths)
It is apparent the top 3 most expensive items we sell are roll up blinds, patio sets, as well as artificial trees.
Digging further into the data we can assess when these products are sold, as well as the amount sold throughout the year.
patio_setid <- TPN %>%
filter(product_type == "PATIO SETS")
print(patio_setid$product_id)
## [1] "13506935" "13213287" "13381576" "13213237" "12607736" "13381271" "13381195"
## [8] "13212980"
##"13506935" "13213287" "13381576" "13213237" "12607736" "13381271" "13381195" "13212980"
rollupid <- TPN %>%
filter(product_type == "OUTDOOR ROLL-UP BLINDS")
##"13212959"
treeid <- TPN %>%
filter(product_type == "ARTIFICIAL TREES")
##"17242362"
patio_set_ids <- c(13212959, 13506935, 13213287, 13381576, 13213237, 12607736, 13381271, 13381195, 13212980)
artificial_tree_ids <- c(17242362)
roll_up_blind_ids <- c(13212959)
patio_set_transactions <- gett[gett$product_id %in% patio_set_ids, ]
artificial_tree_transactions <- gett[gett$product_id %in% artificial_tree_ids, ]
roll_up_blind_transactions <- gett[gett$product_id %in% roll_up_blind_ids, ]
ggplot() +
geom_point(data = patio_set_transactions, aes(x = transaction_timestamp, y = sales_value, color = "Patio Sets")) +
geom_point(data = artificial_tree_transactions, aes(x = transaction_timestamp, y = sales_value, color = "Artificial Trees")) +
geom_point(data = roll_up_blind_transactions, aes(x = transaction_timestamp, y = sales_value, color = "Roll-up Blinds")) +
scale_color_manual(values = c("Patio Sets" = "blue", "Artificial Trees" = "red", "Roll-up Blinds" = "green")) +
labs(title = "Transactions by Date",
x = "Date",
y = "Sales Value",
color = "Product Type") +
theme_minimal()
After analyzing this data I decided to spend most of my effort on patio set product types as they have more potential for sale throughout the year compared to Artificial trees and Roll-up blinds. As an analyst I would personally discourage the sale of roll up blinds and artificial trees as they are expensive products that are nearly never purchased.
First I select basket id’s that contain patio set product types
patio_set_ids <- c(13212959, 13506935, 13213287, 13381576, 13213237, 12607736, 13381271, 13381195, 13212980)
patio_set_basket_ids <- gett %>%
filter(product_id %in% patio_set_ids) %>%
select(basket_id) %>%
distinct()
selected_product_ids <- c(13212959, 13506935, 13213287, 13381576, 13213237, 12607736, 13381271, 13381195, 13212980)
selected_basket_ids <- gett %>%
filter(product_id %in% selected_product_ids) %>%
select(basket_id)
selected_basket_ids <- c(32187143334, 32589406457, 32715766842, 32872550792, 32932606815, 33028063235, 33192060862, 33192060862, 33240466943, 33348172087, 33421338406, 33493612750, 33657746421, 33769986599, 33848336516)
selected_baskets <- gett %>%
filter(basket_id %in% selected_basket_ids)
patio_set_ids <- c(13212959, 13506935, 13213287, 13381576, 13213237, 12607736, 13381271, 13381195, 13212980)
patio_set_transactions <- gett[gett$product_id %in% patio_set_ids, ]
patio_set_basket_ids <- unique(patio_set_transactions$basket_id)
products_bought_with_patio_sets <- gett[gett$basket_id %in% patio_set_basket_ids & !gett$product_id %in% patio_set_ids, ]
product_ids <- products_bought_with_patio_sets$product_id
products_bought_with_patio_sets_names <- products %>%
filter(product_id %in% product_ids)
product_counts <- products_bought_with_patio_sets %>%
group_by(product_id) %>%
summarise(count = n())
products_bought_with_patio_sets_counts <- merge(products_bought_with_patio_sets_names, product_counts, by = "product_id")
products_bought_with_patio_sets_counts <- products_bought_with_patio_sets_counts %>%
arrange(desc(count))
print(products_bought_with_patio_sets_counts[, c("product_type", "count", "product_id")])
## product_type count product_id
## 1 BANANAS 2 1082185
## 2 CHEESE: NATURAL BULK 1 1040894
## 3 CHEWING GUM 1 1054123
## 4 HANGING BASKETS (BLOOMING) 1 1066095
## 5 TOILET TISSUE 1 1075214
## 6 MARGARINE: TUBS AND BOWLS 1 1083840
## 7 SPECIALTY CRACKERS 1 1098286
## 8 BLENDED JUICE&COMBINATIONS (50 1 12604479
## 9 PLSTC CTLRYTBLCLTHSTTHPKSST 1 12670635
## 10 MINTS CANDY & BREATH (NOT LIF 1 12731667
## 11 BEERALEMALT LIQUORS 1 1277455
## 12 CARDS SEASONAL 1 13159269
## 13 CATSUP 1 6391436
## 14 PREMIUM ANNUALS (OUTDOOR) 1 848059
## 15 FRZN CONC ALLIEDS OVER 50% JUI 1 898023
## 16 COOKING CHOCOLATE (EX SMI-SWT) 1 923005
## 17 CANDY BARS (MULTI PACK) 1 9245417
## 18 SWEET GOODS - FULL SIZE 1 925437
## 19 APPLES OTHER (BULK&BAG) 1 927263
## 20 CANDY BARS (MULTI PACK) 1 954966
## 21 SFT DRNK 2 LITER BTL CARB INCL 1 989157
Given this information, I believe it could potentially be beneficial to offer some of these products for free or for a discount along with our patio sets to encourage the sale of these high value items. Items often purchased with patio sets are relatively cheap food items, so offering these items for free or for a discount could potentially increase revenue when purchased with the more expensive patio sets.
I believe it is a good idea to further examine products that are often purchased with patio sets to see which ones would draw the most attention from a customer when offered a deal.
topsellid <- c(
1082185,
1040894,
1054123,
1066095,
1075214,
1083840,
1098286,
12604479,
12670635,
12731667,
1277455,
13159269,
6391436,
848059,
898023,
923005,
9245417,
925437,
927263,
954966,
989157)
top_selling_transactions <- gett %>%
filter(product_id %in% topsellid)
top_selling_summary <- top_selling_transactions %>%
group_by(product_id) %>%
summarize(total_sales_value = sum(sales_value))
top_selling_summary <- top_selling_summary %>%
arrange(desc(total_sales_value))
top_selling_summary_with_type <- top_selling_summary %>%
left_join(products, by = "product_id")
ggplot(top_selling_summary_with_type, aes(x = reorder(product_type, -total_sales_value), y = total_sales_value)) +
geom_point() +
labs(title = "Top Selling Products by Product Type", x = "Product Type", y = "Total Sales Value") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Bananas and toilet paper seem to be a high selling products throughout the year and are unlikely to attract customers to our patio set products
product_transactions <- gett[gett$product_id %in% topsellid , ]
product_transactions <- product_transactions %>%
left_join(products, by = "product_id")
product_transactions$transaction_timestamp <- as.Date(product_transactions$transaction_timestamp)
product_transactions$month <- format(product_transactions$transaction_timestamp, "%Y-%m")
product_transactions_filtered <- product_transactions %>%
filter(month >= "2017-04" & month <= "2017-07")
average_transactions_per_month <- product_transactions_filtered %>%
group_by(product_type, month) %>%
summarise(average_transactions = n())
## `summarise()` has grouped output by 'product_type'. You can override using the
## `.groups` argument.
average_transactions_per_month$product_type <- reorder(average_transactions_per_month$product_type, average_transactions_per_month$average_transactions)
my_colors <- c("lightblue", "blue", "darkblue", "black")
ggplot(average_transactions_per_month, aes(x = month, y = product_type, fill = average_transactions)) +
geom_tile() +
labs(title = "Average Number of Transactions Per Month by Product Type",
x = "Month",
y = "Product Type",
fill = "Average Transactions") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_fill_gradientn(colors = my_colors, limits = c(0, 200), breaks = c(50, 100, 150, 200))
Looking at this data for these 4 months it is apparent that certain
seasonal items including candy bars, cards, and hanging baskets are sold
more in these months compared to the rest of the year. I believe it
would be greatly beneficial to sell one or more of these products for a
discount when basketed with patio sets. This would greatly encourage the
sale of our patio sets, one of our best selling and most expensive
items.