Skip to content
New Workbook
Sign up
Duplicate of Competition - Meal Delivery Service

How can a meal delivery service improve operations?

📖 Background

Your client is a food company in India that provides meal delivery services to thousands of customers per week. They operate in 51 cities inside eight different regions of the country. To better serve their customers, they deliver meals from 77 fulfillment centers.

The company wants you to help them understand their customer base, meal preferences, and fluctuations in demand. They want to investigate ways to improve operations and reduce food waste.

💾 The data

Your client has provided you with almost three years of order information for each of their 77 fulfillment centers:

  • "week" - week (1 - 145)
  • "center_id" - id of the fulfillment center
  • "city_code" - city identifier
  • "region_code" - region identifier
  • "center_type" - there are three types of centers: A, B, and C
  • "op_area" - the size of the area serviced by the fulfillment center
  • "meal_id" - each meal has a unique id
  • "category" - food category (beverages, pasta, rice, etc.)
  • "cuisine" - the type of cuisine for each meal
  • "checkout_price" - the price the customer pays (including discounts, promotions, etc.)
  • "base_price" - list price for the meal
  • "emailer" - 1 if there was an email promotion for the meal, 0 otherwise
  • "featured" - 1 if the website featured the meal, 0 otherwise
  • "orders" - number of orders for that week for that meal id
Hidden code
meals <- deliveries %>%
  mutate(
    center_id = factor(center_id),
    center_type = factor(center_type, levels = c("A", "B", "C")),
    city_code = factor(city_code),
    emailer = as.logical(emailer),
    featured = as.logical(featured),
    meal_id = factor(meal_id),
    cuisine = factor(cuisine)
  )

theme_set(theme_linedraw(base_size = 15))

Question 1

In terms of total orders, in all regions except 93, Italian was the most popular cuisine. In region 93, the most popular cuisine was Thai. Figure 1 and Table 1 summarize these results.

Figure 1

top_cuisines_per_region <- meals %>%
  group_by(region_code, cuisine) %>%
  summarize(region_cuisine_orders = sum(orders), .groups = "drop_last") %>%
  arrange(region_code, desc(region_cuisine_orders))

top_cuisines_per_region %>%
  mutate(
    region_name = str_c("Region ", region_code),
    bar_fill = case_when(
      region_code == 93 & cuisine == "Thai" ~ "blue",
      cuisine == "Italian" & region_code != 93 ~ "red",
      TRUE ~ "gray"
    ),
    cuisine = fct_reorder(cuisine, cuisine, sort, .desc = TRUE)
  ) %>%
  ggplot(aes(x = cuisine, y = region_cuisine_orders, fill = bar_fill)) +
  geom_col() +
  scale_fill_identity(labels = c("Thai", "Other", "Italian"), guide = "legend") +
  guides(fill = guide_legend(title = "Cuisine")) +
  coord_flip() +
  facet_wrap(~ region_name, scales = "free_x") +
  labs(
    title = "Fig. 1: Cuisine Popularity by Region",
    x = "Orders for Cuisine",
    y = "Cuisine"
  ) +
  theme(axis.text.x=element_blank(),
        axis.ticks.x=element_blank())

Table 1

top_1_cuisine_per_region <- meals %>%
  group_by(region_code, cuisine) %>%
  summarize(region_cusine_orders = sum(orders), .groups = "drop_last") %>%
  slice_max(region_cusine_orders, n = 1) %>%
  arrange(region_code)

top_1_cuisine_per_region

Question 2

Average Revenue Per Center Type

Before asking about which changes to make to the centers in big cities, let's look at the average revenue per center type across India. Each center type has differing mean revenues, as shown in Figure 2 and Table 2. Hence, when comparing the performance of centers, each center should be rated against other centers of the same type.

mean_center_revenue_per_type <- meals %>%
  group_by(center_type, center_id) %>%
  summarize(total_center_revenue = sum(orders * checkout_price), .groups = "drop_last") %>%
  summarize(mean_center_revenue = mean(total_center_revenue))

mean_center_revenue_per_type %>%
  ggplot(aes(x = center_type, y = mean_center_revenue)) +
  geom_col() +
  labs(
    title = "Fig. 2: Mean Total Revenue by Center Type",
    x = "Center Type",
    y = "Mean Total Revenue"
  )

Table 2

Hidden code

Recommendations for Centers in Large Cities

City 526 has eight centers, and three of these centers underperform relative to centers of the same type nationally by at least -20% or more: 81, 88, and 162. Therefore, consider consolidating these centers into a central location that creates the revenue of these three centers combined.

City 590 has nine centers, and three of these centers underperform relative to centers of the same type nationally by at least -20% or more: 41, 53, and 91. Therefore, as in City 526, I recommend consolidating these centers in City 590 into a central location that creates the revenue of these three centers combined.

Figure 3 and Table 3 summarize these results.

Figure 3

revenue_diff_from_mean <- meals %>%
  group_by(center_type, city_code, center_id) %>%
  summarize(center_revenue = sum(orders * checkout_price), .groups = "drop") %>%
  inner_join(mean_center_revenue_per_type, by = "center_type") %>%
  mutate(pct_diff = (center_revenue / mean_center_revenue - 1) * 100) %>%
  select(city_code, center_id, pct_diff)

big_cities <- revenue_diff_from_mean %>%
  group_by(city_code) %>%
  summarize(count_centers = n()) %>%
  filter(count_centers > 3)

revenue_diff_from_mean %>%
  filter(city_code %in% big_cities$city_code) %>%
  mutate(
    bar_color = ifelse(pct_diff >= 0, "blue", "red"),
    city_name = str_c("City ", city_code)
  ) %>%
  ggplot(aes(x = center_id, y = pct_diff, fill = bar_color)) +
  geom_col() +
  scale_fill_identity(labels = c("above average", "below average"), guide = "legend") +
  facet_wrap(~ city_name, scales = "free_x", nrow = 2) +
  guides(fill = guide_legend(title = "% Difference")) +
  labs(
    x = "Center ID",
    y = "% Difference from National Average",
    title = "Fig. 3: Center Revenue Relative to National Mean",
    subtitle = "Centers are comapred to other centers of the same type."
  )

Table 3

‌
‌
‌