knitr::opts_chunk$set(echo = TRUE)
library(tidyverse)
library(shiny)
library(shinydashboard)
library(DT)
library(ggplot2)
save(mlws_clean, file = "mlws_clea.RData")
load("mlws.RData")
Checking name of each variables
ls(mlws)
[1] "Band Width (mm)" "Brand" "Case Diameter (mm)" "Case Material" "Case Thickness (mm)" "Complications"
[7] "Crystal Material" "Dial Color" "Model" "Movement Type" "Power Reserve" "Price (USD)"
[13] "Strap Material" "Water Resistance"
Checking name of each variables
variable_names <- colnames(mlws)
print(variable_names)
[1] "Brand" "Model" "Case Material" "Strap Material" "Movement Type" "Water Resistance"
[7] "Case Diameter (mm)" "Case Thickness (mm)" "Band Width (mm)" "Dial Color" "Crystal Material" "Complications"
[13] "Power Reserve" "Price (USD)"
Checking type of each variables
str(mlws)
spc_tbl_ [507 × 14] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ Brand : chr [1:507] "Rolex" "Omega" "Tag Heuer" "Breitling" ...
$ Model : chr [1:507] "Submariner" "Seamaster" "Carrera" "Navitimer" ...
$ Case Material : chr [1:507] "Stainless Steel" "Titanium" "Stainless Steel" "Stainless Steel" ...
$ Strap Material : chr [1:507] "Stainless Steel" "Rubber" "Leather" "Stainless Steel" ...
$ Movement Type : chr [1:507] "Automatic" "Automatic" "Automatic" "Automatic" ...
$ Water Resistance : chr [1:507] "300 meters" "600 meters" "100 meters" "30 meters" ...
$ Case Diameter (mm) : num [1:507] 40 43.5 41 43 31 42.9 44.3 42 39.3 38 ...
$ Case Thickness (mm): num [1:507] 13 14.47 13 14.25 6.05 ...
$ Band Width (mm) : num [1:507] 20 21 20 22 20 20 20 22 19 20 ...
$ Dial Color : chr [1:507] "Black" "Blue" "White" "Black" ...
$ Crystal Material : chr [1:507] "Sapphire" "Sapphire" "Sapphire" "Sapphire" ...
$ Complications : chr [1:507] "Date" "Date" "Chronograph" "Chronograph" ...
$ Power Reserve : chr [1:507] "48 hours" "60 hours" "42 hours" "70 hours" ...
$ Price (USD) : num [1:507] 9500 5800 4200 7900 2800 5500 1400 1200 650 495 ...
- attr(*, "spec")=
.. cols(
.. Brand = col_character(),
.. Model = col_character(),
.. `Case Material` = col_character(),
.. `Strap Material` = col_character(),
.. `Movement Type` = col_character(),
.. `Water Resistance` = col_character(),
.. `Case Diameter (mm)` = col_double(),
.. `Case Thickness (mm)` = col_double(),
.. `Band Width (mm)` = col_double(),
.. `Dial Color` = col_character(),
.. `Crystal Material` = col_character(),
.. Complications = col_character(),
.. `Power Reserve` = col_character(),
.. `Price (USD)` = col_number()
.. )
- attr(*, "problems")=<externalptr>
removing non significant variables
mlws_clean <- mlws %>%
select(-`Case Thickness (mm)`, -`Band Width (mm)`, -`Crystal Material`, -Complications, -`Power Reserve`)
str(mlws_clean)
tibble [507 × 9] (S3: tbl_df/tbl/data.frame)
$ Brand : chr [1:507] "Rolex" "Omega" "Tag Heuer" "Breitling" ...
$ Model : chr [1:507] "Submariner" "Seamaster" "Carrera" "Navitimer" ...
$ Case Material : chr [1:507] "Stainless Steel" "Titanium" "Stainless Steel" "Stainless Steel" ...
$ Strap Material : chr [1:507] "Stainless Steel" "Rubber" "Leather" "Stainless Steel" ...
$ Movement Type : chr [1:507] "Automatic" "Automatic" "Automatic" "Automatic" ...
$ Water Resistance : chr [1:507] "300 meters" "600 meters" "100 meters" "30 meters" ...
$ Case Diameter (mm): num [1:507] 40 43.5 41 43 31 42.9 44.3 42 39.3 38 ...
$ Dial Color : chr [1:507] "Black" "Blue" "White" "Black" ...
$ Price (USD) : num [1:507] 9500 5800 4200 7900 2800 5500 1400 1200 650 495 ...
identifying how many categories per variable
categories_per_column <- sapply(mlws_clean, n_distinct)
print(categories_per_column)
Brand Model Case Material Strap Material Movement Type Water Resistance Case Diameter (mm)
39 95 17 14 4 12 37
Dial Color Price (USD)
7 183
Checking the different values within variables
unique(mlws_clean$`Movement Type`)
[1] "Automatic" "Quartz" "Manual" "Eco-Drive"
unique(mlws_clean$`Case Material`)
[1] "Stainless Steel" "Titanium" "18k White Gold" "Ceramic" "18K Rose Gold"
[6] "Rose Gold" "White Gold" "18K White Gold" "18k Rose Gold" "High-Tech Ceramic"
[11] "German Submarine Steel" "Bronze" "Carbon Fiber" "Yellow Gold" "18k King Gold"
[16] "18k Yellow Gold" "18K Yellow Gold"
unique(mlws_clean$`Strap Material`)
[1] "Stainless Steel" "Rubber" "Leather" "Textile" "Jubilee" "Alligator"
[7] "Canvas" "Fabric" "Silicone" "Titanium" "Rose Gold" "NATO Strap"
[13] "Jubilee Bracelet" "NATO strap"
unique(mlws_clean$`Water Resistance`)
[1] "300 meters" "600 meters" "100 meters" "30 meters" "200 meters" "50 meters" "150 meters" "500 meters" "60 meters"
[10] "120 meters" "1000 meters" "2000 meters"
Identifying missing values
mlws_clean$RowIndex <- seq_len(nrow(mlws_clean))
mlws_clean_filtered <- na.omit(mlws_clean)
mlws_clean_filtered <- mlws_clean %>%
filter(!if_any(everything(), is.na))
removed_rows <- setdiff(mlws_clean$RowIndex, mlws_clean_filtered$RowIndex)
print(removed_rows)
[1] 25
Checking the dataset with the removed rows (1 row deleted from process)
mlws_clean <- mlws_clean %>%
filter(!if_any(everything(), is.na))
str(mlws_clean)
tibble [506 × 10] (S3: tbl_df/tbl/data.frame)
$ Brand : chr [1:506] "Rolex" "Omega" "Tag Heuer" "Breitling" ...
$ Model : chr [1:506] "Submariner" "Seamaster" "Carrera" "Navitimer" ...
$ Case Material : chr [1:506] "Stainless Steel" "Titanium" "Stainless Steel" "Stainless Steel" ...
$ Strap Material : chr [1:506] "Stainless Steel" "Rubber" "Leather" "Stainless Steel" ...
$ Movement Type : chr [1:506] "Automatic" "Automatic" "Automatic" "Automatic" ...
$ Water Resistance : chr [1:506] "300 meters" "600 meters" "100 meters" "30 meters" ...
$ Case Diameter (mm): num [1:506] 40 43.5 41 43 31 42.9 44.3 42 39.3 38 ...
$ Dial Color : chr [1:506] "Black" "Blue" "White" "Black" ...
$ Price (USD) : num [1:506] 9500 5800 4200 7900 2800 5500 1400 1200 650 495 ...
$ RowIndex : int [1:506] 1 2 3 4 5 6 7 8 9 10 ...
deleting the index row and renaming the dataset mlws_clean instead of mlws_clean_filtered
mlws_clean_filtered$RowIndex <- NULL
mlws_clean <- mlws_clean_filtered
rm(mlws_clean_filtered)
str(mlws_clean)
tibble [506 × 9] (S3: tbl_df/tbl/data.frame)
$ Brand : chr [1:506] "Rolex" "Omega" "Tag Heuer" "Breitling" ...
$ Model : chr [1:506] "Submariner" "Seamaster" "Carrera" "Navitimer" ...
$ Case Material : chr [1:506] "Stainless Steel" "Titanium" "Stainless Steel" "Stainless Steel" ...
$ Strap Material : chr [1:506] "Stainless Steel" "Rubber" "Leather" "Stainless Steel" ...
$ Movement Type : chr [1:506] "Automatic" "Automatic" "Automatic" "Automatic" ...
$ Water Resistance : chr [1:506] "300 meters" "600 meters" "100 meters" "30 meters" ...
$ Case Diameter (mm): num [1:506] 40 43.5 41 43 31 42.9 44.3 42 39.3 38 ...
$ Dial Color : chr [1:506] "Black" "Blue" "White" "Black" ...
$ Price (USD) : num [1:506] 9500 5800 4200 7900 2800 5500 1400 1200 650 495 ...
Replacing the incorrect speeling of A. Lange & Sohne to A. Lange & Söhne
mlws_clean$Brand <- gsub("A. Lange & Sohne", "A. Lange & Söhne", mlws_clean$Brand)
table(mlws_clean$Brand)
A. Lange & Söhne Audemars Piguet Baume & Mercier Bell & Ross Blancpain Breguet
3 38 1 3 29 26
Breitling Bulgari Bulova Cartier Chopard Citizen
16 3 1 19 14 2
Frederique Constant Girard-Perregaux Glashütte Original Hamilton Hublot IWC
2 20 1 4 26 39
Jaeger-LeCoultre Jaquet Droz Longines Montblanc Omega Oris
16 5 10 9 23 2
Panerai Patek Philippe Piaget Rado Rolex Seiko
25 33 6 1 27 5
Sinn Tag Heuer TAG Heuer Tissot Tudor Ulysse Nardin
3 11 2 2 14 10
Vacheron Constantin Zenith
25 30
library(ggplot2)
ggplot(mlws_clean, aes(x = Brand, y = `Price (USD)`, fill = "Brand")) +
geom_boxplot() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
labs(title = "Price Distribution by Brand", x = "Brand", y = "Price (USD)")
NA
NA
ggplot(mlws_clean, aes(x = `Case Material`, fill = `Case Material`)) +
geom_bar() +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none") +
labs(title = "Frequency of Watches by Case Material", x = "Case Material", y = "Count")
ggplot(mlws_clean, aes(x = `Case Diameter (mm)`)) +
geom_histogram(binwidth = 1, fill = "black", color = "white") +
labs(title = "Distribution of Case Diameters", x = "Case Diameter (mm)", y = "Frequency") +
theme_dark()
average_price_per_brand <- mlws_clean %>%
group_by(Brand) %>%
summarise(Average_Price = mean(`Price (USD)`, na.rm = TRUE)) %>%
arrange(Average_Price)
ggplot(average_price_per_brand, aes(x = reorder(Brand, Average_Price), y = Average_Price, fill = Brand)) +
geom_col() +
coord_flip() +
theme(
axis.text.x = element_text(angle = 0, hjust = 1),
legend.position = "none"
) +
labs(title = "Average Watch Price per Brand in USD", x = "Average Price (USD)", y = "Brand")
# Library imports
library(tidyverse)
library(shiny)
library(shinydashboard)
library(DT)
library(ggplot2)
library(rsconnect)
# Load data
load("mlws.RData")
# Preprocessing data
mlws_clean <- mlws %>%
select(-`Case Thickness (mm)`, -`Band Width (mm)`, -`Crystal Material`, -Complications, -`Power Reserve`) %>%
mutate(Brand = gsub("A. Lange & Sohne", "A. Lange & Söhne", Brand)) %>%
filter(!if_any(everything(), is.na)) %>%
mutate(RowIndex = row_number()) %>%
filter(!is.na(`Price (USD)`)) %>%
arrange(desc(`Price (USD)`))
# UI definition
ui <- fluidPage(
titlePanel("My Luxury Watch Shopper"),
sidebarLayout(
sidebarPanel(
selectInput("brand", "Brand:", choices = c("All", unique(mlws_clean$Brand))),
sliderInput("priceRange", "Price Range:", min = min(mlws_clean$`Price (USD)`, na.rm = TRUE),
max = max(mlws_clean$`Price (USD)`, na.rm = TRUE),
value = c(min(mlws_clean$`Price (USD)`, na.rm = TRUE), max(mlws_clean$`Price (USD)`, na.rm = TRUE))),
selectInput("caseMaterial", "Case Material:", choices = c("All", unique(mlws_clean$`Case Material`))),
selectInput("strapMaterial", "Strap Material:", choices = c("All", unique(mlws_clean$`Strap Material`))),
actionButton("goButton", "Find Watches")
),
mainPanel(
tabsetPanel(
tabPanel("Recommendations",
DTOutput("table"),
h2("Top 5 Best Watches for You", align = "center"),
DTOutput("topTierTable") # Changed to DTOutput for DataTables integration
),
tabPanel("Price Distribution",
plotOutput("pricePlot"),
plotOutput("avgPricePlot")
),
tabPanel("Case Material Distribution", plotOutput("caseMaterialPlot")),
tabPanel("Recommendation Details", textOutput("recText"), plotOutput("radarChart"))
)
)
)
)
server <- function(input, output) {
# Reactive expression for filtered data
dataFiltered <- reactive({
req(input$goButton)
mlws_clean %>%
filter((Brand == input$brand | input$brand == "All") &
`Price (USD)` >= input$priceRange[1] &
`Price (USD)` <= input$priceRange[2] &
(`Case Material` == input$caseMaterial | input$caseMaterial == "All") &
(`Strap Material` == input$strapMaterial | input$strapMaterial == "All"))
})
# Rendering the DataTable
output$table <- renderDT({
dataFiltered()
}, options = list(pageLength = 5))
# Rendering the price distribution plot
output$pricePlot <- renderPlot({
ggplot(dataFiltered(), aes(x = `Price (USD)`)) +
geom_histogram(binwidth = 500, fill = "blue") +
labs(title = "Price Distribution", x = "Price (USD)", y = "Count") +
theme_minimal(base_size = 14) +
theme(panel.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
})
# Rendering the average price per case material plot
output$avgPricePlot <- renderPlot({
avg_price_data <- mlws_clean %>%
group_by(`Case Material`) %>%
summarize(AvgPrice = mean(`Price (USD)`, na.rm = TRUE)) %>%
ungroup() %>%
arrange(desc(AvgPrice))
ggplot(avg_price_data, aes(x = reorder(`Case Material`, AvgPrice), y = AvgPrice, fill = `Case Material`)) +
geom_col() +
theme_minimal(base_size = 14) +
theme(panel.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.position = "none") +
labs(title = "Average Price per Case Material", x = "Case Material", y = "Average Price (USD)") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
})
# Rendering the case material distribution plot
output$caseMaterialPlot <- renderPlot({
ggplot(dataFiltered(), aes(x = `Case Material`, fill = `Case Material`)) +
geom_bar(stat = "count") +
theme_minimal(base_size = 14) +
theme(panel.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()) +
labs(title = "Case Material Distribution", x = "Case Material", y = "Count") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
})
# This is the function to get top tier watches
getTopTierWatches <- function(data, n = 5) {
data %>%
arrange(desc(`Price (USD)`)) %>%
slice_head(n = n)
}
# Reactivity expression for top 5 watches
topTierWatches <- reactive({
req(input$goButton)
getTopTierWatches(dataFiltered())
})
output$topTierTable <- renderDT({
topTierWatches() %>%
select(Brand, Model, `Price (USD)`)
}, options = list(pageLength = 5, searching = FALSE, paging = FALSE, info = FALSE))
# Placeholder for recommendation text
output$recText <- renderText({
rec <- recommendedWatch()
if(nrow(rec) > 0) {
paste("We recommend the", rec$Brand, "watch with a price of", rec$`Price (USD)`, "USD.")
} else {
"No recommendation available based on the current criteria."
}
})
# Generating a recommendation based on filtered data
recommendedWatch <- reactive({
data <- dataFiltered()
if(nrow(data) > 0) {
data[which.max(data$`Price (USD)`), ]
} else {
data.frame()
}
})
}
# Run the application
shinyApp(ui, server)
Listening on http://127.0.0.1:7806
NA