Data Description

The data for this analysis was obtained from the Kaggle Adidas Webstore Shoe Data. The shoes_fact dataset contains webstore shoe sales data from Germany, Belgium, the UK, and the US, covering the period from January 6 to January 16, 2025. The shoes_dim dataset provides detailed descriptions of each shoe, including color, gender, best use cases, and more. I will combine these two datasets to investigate whether there are differences in shoe purchase preferences among these four countries. The table below lists all the variables after combining the two datasets.

Variable Description
TID There are 299,156 transcations in this data.
id There are 2010 different shoe SKU in this data.
price The price of shoes are in euro, range from 16 to 500.
category There are 11 categories for different occation.
size The size is in different scale, need to do some transformation.
availability The amount of inventory level, range from 0(low) to 15(high).
date The transcation date.
country_code The two uppercase letters represent the following four countries, DE – Germany;BE – Belgium;UK – United Kingdom;US – United States .
best_for_wear The description of when the shoe is best worn, for example: Racing, City,Everyday and so on.
gender There are 4 categories, including U - unisex; M - men; W - women; K - kids.
dominant_color The dominant color for the shoes. Already transformed.

Data Cleaning

#packages
library(tidyverse)
library(ggplot2)
library(dplyr)
library(reshape2)
library(RColorBrewer)
library(plotly)
library(stringr)
library(scales)
shoes_fact <- read.csv("data/shoes_fact.csv")
shoes_dim <- read.csv("data/shoes_dim.csv")

#EDA
duplicated_rows <- shoes_dim %>% group_by(id) %>% filter(n() > 1) %>% arrange(id) ##there are 1,102 ids with more than one description and most of them are because of that they have description in 2 languages.

#clean data
exception_ids <- c("GW9284","HP8607","HQ8399","JI1690") #these 4 ids only have german description

processed_duplicates <- duplicated_rows %>%
  group_by(id) %>%
  filter(id %in% exception_ids | (!grepl("schuh", name, ignore.case = TRUE) & !grepl("Stiefel", name, ignore.case = TRUE))) %>%
  group_by(id) %>%
  mutate(has_U = any(gender == "U")) %>%  
  filter(if_else(has_U, gender == "U", TRUE)) %>%  
  slice_sample(n = 1) %>%  
  ungroup() %>%
  select(-has_U)

unique_rows <- shoes_dim %>%
  group_by(id) %>%
  filter(n() == 1) %>%
  ungroup()

shoes_map <- bind_rows(processed_duplicates, unique_rows)
shoes_map_subset <- shoes_map[, names(shoes_map) %in% c("id","name", "best_for_wear","gender","dominant_color")]
#shoes_map_subset %>% group_by(id) %>% filter(n() > 1) %>% arrange(id) 

combined_data <- left_join(shoes_fact, shoes_map_subset, by = "id")
head(combined_data)
##     TID     id price category   size availability     date country_code
## 1 63575 HP9426    60 sneakers 36.00             0 2025/1/7           DE
## 2 63576 HP9426    60 sneakers 36.67             0 2025/1/7           DE
## 3 63577 HP9426    60 sneakers 37.33             0 2025/1/7           DE
## 4 63578 HP9426    60 sneakers 38.00             0 2025/1/7           DE
## 5 63579 HP9426    60 sneakers 38.67             1 2025/1/7           DE
## 6 63580 HP9426    60 sneakers 39.33            15 2025/1/7           DE
##                 name best_for_wear gender dominant_color
## 1 Breaknet 2.0 Shoes          City      U          White
## 2 Breaknet 2.0 Shoes          City      U          White
## 3 Breaknet 2.0 Shoes          City      U          White
## 4 Breaknet 2.0 Shoes          City      U          White
## 5 Breaknet 2.0 Shoes          City      U          White
## 6 Breaknet 2.0 Shoes          City      U          White

Data Visualization

Shoe Price

#draft plot
ggplot(combined_data, aes(x = country_code, y = price, color = country_code)) +
  geom_boxplot() +
  labs(title = "Price Distribution by Country",
       x = "Country Code",
       y = "Price")

#alternative plot
ggplot(combined_data,aes(x = price , y =  country_code, color = country_code)) + 
  geom_point(alpha = 0.8) + 
  stat_summary(geom = "point", 
               fun = "median",   
               color = "darkgray", size = 5, 
               pch = 4, stroke = 2)

#final plot
#p<-ggplot(combined_data, aes(x = price, y = country_code, fill = gender)) +
 # geom_boxplot(show.legend = TRUE) +
  #theme_minimal() +
  #labs(title = "Distribution of Adult Shoe Prices by Country",
   #    x = "Price",
    #   y = "Country Code")

#ggplotly(p)

continuous_data <- combined_data %>%
  mutate(country_gender = paste(country_code, gender, sep = " - "))

color_mapping <- c(
  "Women" = "lightpink",     
  "Men" = "lightblue",     
  "Unisex" = "gray",
  "Kids" = "#FFD700")

continuous_data  <- continuous_data  %>%
  mutate(country_name = recode(country_code,
                          DE = "Germany",
                          UK = "United Kingdom",
                          BE = "Belgium",
                          US = "United States"))%>%
  mutate(gender = recode(gender, 
                         "W" = "Women", 
                         "M" = "Men", 
                         "U" = "Unisex",
                         "K" = "Kids")) %>%
  mutate(gender = factor(gender, levels = names(color_mapping))) 



plotly_box <- plot_ly(
  continuous_data, 
  x = ~price,
  y = ~reorder(country_name, country_code),
  color = ~gender,
  colors = color_mapping,
  type = "box",
  showlegend = TRUE ) %>%
  layout(title = list(text = "Distribution of Shoes Prices by Country", x = 0.05),
          xaxis = list(title = "Price (Euro)"), 
          yaxis = list(title = " "),  #because they will overlap each other, and I think it is clear they are countries so I leave blank here
          boxmode = "group", 
          legend = list(title = list(text = "Gender"),orientation = "h", x = 0.3, y = -0.2))

plotly_box <- plotly_box %>%
  layout(
    legend = list(traceorder = "normal",
                  itemsizing = "constant",
                  itemclick = FALSE,
                  itemdoubleclick = FALSE)) %>%
  style(text = list("Women", "Men", "Unisex","Kids"), 
        traces = c(1,2,3,4))

plotly_box

I wanted to explore the distribution of shoe prices in different countries, so I first used a box plot. The draft plot looked okay, but it was not very informative to me. I could only see that the UK has a lower average price. Also, I noticed that the number of data points for each country is not even. Most of the data in this dataset comes from Germany (about 72%) and the most of data in UK are from kids category, which could cause some issues, because kids shoes are cheaper than adults shoes in general.

To get more insights, I tried using a scatter plot to display every data point. In my setup, the x-axis represented countries, and the y-axis represented price. However, I did not use “gender” to differentiate the data, so the scatter plot only showed the overall price distribution in different countries. Unfortunately, this did not provide useful insights.

After trying different types of plots, I found that a box plot was the best way to show the price distribution across countries by adding extra variable. To make the analysis more detailed and truthful, I also separated the prices by category. From the final plot, I noticed that men in the UK tend to buy cheaper shoes than in other countries. Also, kids’ shoe prices in the UK have less variation compared to other countries.

For better visualization, I switched the x-axis and y-axis to make it easier to compare prices across countries. I also adjusted the labels and colors so the audience could understand the plot more easily.

Shoe Color

#draft plot
ggplot(combined_data, aes(x = dominant_color)) +
  geom_bar(aes(fill = country_code), 
           position = position_dodge(preserve = "single"),
           show.legend = TRUE) +
  labs(y = "Frequency", 
       title = "Shoes Color Selection by Country") +
  scale_y_continuous(expand = expansion(mult = c(0, 0.1))) 

#alternative plot
plot_data <- combined_data %>%
  group_by(country_code, dominant_color) %>%
  summarise(count = n(), .groups = 'drop') %>%
  mutate(percentage = count / sum(count))

ggplot(plot_data, aes(x = country_code, y = percentage, fill = dominant_color)) +
  geom_bar(stat = "identity", position = "fill") +  
  scale_y_continuous(labels = scales::percent) + 
  labs(title = "Shoes Color Selection by Country",
       x = "Country",
       y = "Percentage",
       fill = "Category") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) 

#final plot
color_mapping <- c(
  "Beige" = "#F5F5DC",  
  "Black" = "#000000",  
  "Blue" = "#0000FF",  
  "Brown" = "#A52A2A", 
  "Gold" = "#FFD700",  
  "Green" = "#008000",
  "Grey" = "#808080",
  "Orange" = "#FFA500", 
  "Pink" = "#FFC0CB", 
  "Purple" = "#800080", 
  "Red" = "#FF0000", 
  "Silver" = "#C0C0C0", 
  "White" = "#FFFFFF",
  "Yellow" = "#FFFF00" 
)

color_pct_data <- combined_data %>%
  group_by(country_code, dominant_color) %>%
  summarise(count = n(), .groups = 'drop') %>%
  group_by(country_code) %>% 
  mutate( percentage = count / sum(count), 
          country = recode(country_code, 
                     DE = "Germany",
                     UK = "United Kingdom",
                     BE = "Belgium",
                     US = "United States")) %>%
  ungroup()%>%
  mutate(dominant_color = fct_reorder(dominant_color, percentage))

p <- ggplot(color_pct_data, aes(x = country, y = percentage, fill = dominant_color, 
                                text = paste("Country:", country,
                                             "Color:", dominant_color, 
                                             "Percentage:", round(percentage * 100, 1), "%"))) +
  geom_bar(stat = "identity", position = "fill", color = "black",size = 0.2) +  
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) + 
  scale_fill_manual(values = color_mapping) +
  labs(title = "Shoes Color Selection by Country",
       x = "Country",
       y = "Percentage",
       fill = "Color") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

plotly_plot <- ggplotly(p, tooltip = "text")
plotly_plot

For numerical and categorical variables, I want to know whether different countries have preferences for different color shoes. I created a bar chart comparing the number of different shoe colors in each country. After making the first draft, I noticed a problem: since Germany has the most data, for me, when I first see the chat, I can only see that Germany buys a lot of black and white shoes, but it was difficult to compare with other countries. Because of this, I realized that showing the total number of shoes was not a good idea. It did not clearly show how different countries prefer different colors.

To solve this, I decided to use percentages instead of totals. I cared more about how much each country prefers a certain color, rather than the total number of shoes sold. However, after plotting the chart using ggplot’s default settings, I found some issues. The colors were too similar, making them hard to tell apart. Also, since the chart was stacked, it was difficult to compare colors without labeling each one.

A good solution to this problem was using Plotly, which allows for interactive visualization. This made it easier to see and compare the percentages directly. In the final version, I sorted the colors by proportion, so it was quicker to compare. I also added color labels and adjusted the colors, making the plot more intuitive and easy to understand. From the chart, we can see that people in the UK do not have a strong preference for colorful shoes, while Belgium and Germany have a similar color preference ranking and proportion.