The NYC Flight Explorer was designed to allow users to explore how different flight characteristics relate to delays while giving them meaningful control over the data being displayed. The app includes five different forms of user input: a select menu for choosing an airline, a slider for selecting a departure-delay range, radio buttons for choosing whether flight distance or air time appears on the scatterplot’s x-axis, a checkbox for adding or removing a trend line, and a numeric input for setting a minimum flight distance. These inputs meaningfully affect the app by filtering the data or changing how the visualization is displayed. The app also includes three different forms of output: an interactive scatterplot, a text summary of the filtered flights, and a table showing the most delayed flights that meet the selected criteria. The scatterplot follows visualization principles discussed in STAT 3280 by using a clear title and subtitle, descriptive axis and legend labels, transparency to reduce overplotting, and color only to distinguish the three NYC departure airports. The app also limits the number of plotted observations when necessary so that the visualization remains readable. Together, these features allow users to explore the data interactively while keeping the presentation clear, organized, and focused on meaningful comparisons.
library(shiny)
library(ggplot2)
library(dplyr)
library(nycflights13)
flight_data <- flights %>%
left_join(airlines, by = "carrier") %>%
filter(
!is.na(dep_delay),
!is.na(arr_delay),
!is.na(air_time)
)
ui <- fluidPage(
titlePanel("NYC Flight Explorer"),
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "airline",
label = "Choose an airline:",
choices = c(
"All Airlines",
sort(unique(flight_data$name))
),
selected = "All Airlines"
),
sliderInput(
inputId = "delay_range",
label = "Departure delay range (minutes):",
min = -30,
max = 300,
value = c(-10, 120),
step = 5
),
radioButtons(
inputId = "x_variable",
label = "Choose the scatterplot x-variable:",
choices = c(
"Flight Distance" = "distance",
"Air Time" = "air_time"
),
selected = "distance"
),
checkboxInput(
inputId = "trendline",
label = "Show trend line",
value = FALSE
),
numericInput(
inputId = "min_distance",
label = "Minimum flight distance (miles):",
value = 0,
min = 0,
max = max(flight_data$distance),
step = 100
)
),
mainPanel(
h3("Flight Delay Visualization"),
plotOutput("delay_plot"),
hr(),
h3("Summary"),
verbatimTextOutput("summary"),
hr(),
h3("Most Delayed Flights"),
tableOutput("flight_table")
)
)
)
server <- function(input, output, session) {
filtered <- reactive({
data <- flight_data %>%
filter(
dep_delay >= input$delay_range[1],
dep_delay <= input$delay_range[2],
distance >= input$min_distance
)
if (input$airline != "All Airlines") {
data <- data %>%
filter(name == input$airline)
}
data
})
output$delay_plot <- renderPlot({
plot_data <- filtered()
if (nrow(plot_data) > 5000) {
plot_data <- plot_data %>%
slice_sample(n = 5000)
}
x_label <- if (input$x_variable == "distance") {
"Flight Distance (miles)"
} else {
"Air Time (minutes)"
}
p <- ggplot(
plot_data,
aes(
x = .data[[input$x_variable]],
y = arr_delay
)
) +
geom_point(
aes(color = origin),
alpha = 0.4
) +
labs(
title = "What Factors Are Related to Arrival Delays?",
subtitle = "Each point represents one NYC flight",
x = x_label,
y = "Arrival Delay (minutes)",
color = "NYC Airport"
) +
theme_minimal()
if (input$trendline) {
p <- p +
geom_smooth(
method = "lm",
se = FALSE,
color = "black"
)
}
p
})
output$summary <- renderPrint({
data <- filtered()
cat("Number of flights:", nrow(data), "\n")
if (nrow(data) > 0) {
cat(
"Average departure delay:",
round(mean(data$dep_delay), 1),
"minutes\n"
)
cat(
"Average arrival delay:",
round(mean(data$arr_delay), 1),
"minutes\n"
)
cat(
"Average flight distance:",
round(mean(data$distance), 1),
"miles\n"
)
}
})
output$flight_table <- renderTable({
filtered() %>%
arrange(desc(arr_delay)) %>%
select(
Airline = name,
Origin = origin,
Destination = dest,
`Departure Delay` = dep_delay,
`Arrival Delay` = arr_delay,
`Distance (miles)` = distance
) %>%
head(10)
})
}
shinyApp(ui = ui, server = server)
I chose the Hotshot Racing Dashboard, which is an interactive Shiny app that summarizes statistics from a racing-game competition. One thing the app does well is organize a large amount of information into separate sections instead of trying to display everything at once. The dashboard uses summary statistics, animated visualizations, and interactive tables, which gives users multiple ways to explore the results. I also think the visual styling works well because the app has a clear theme that matches the racing topic and makes it feel more polished than a basic data dashboard.
One weakness is that the amount of information can initially feel overwhelming. There are several tabs, statistics, and interactive elements, so it is not immediately obvious where a new user should begin. Some of the tables also contain many variables, which makes them useful for detailed exploration but harder to quickly interpret. The most confusing part for me was understanding exactly what some of the racing statistics represented without already knowing the competition or game. I think the app could improve by including a short explanation of the most important statistics and clearer guidance about what users should explore first.
My NYC Flight Explorer and the Hotshot Racing Dashboard are both interactive Shiny apps that allow users to explore data rather than only viewing a static graph. My app is more focused and simple, with controls for filtering flights by airline, departure delay, minimum distance, and the variable displayed on the scatterplot. It also provides three types of output: a scatterplot, summary statistics, and a table of the most delayed flights. The Hotshot Racing Dashboard is more complex and visually polished. It contains multiple sections and allows users to explore a wider range of statistics, tables, and visualizations. It also uses a consistent racing-car theme throughout the app, which makes the design feel more cohesive and connected to the subject. This makes it more detailed, but it also makes the app somewhat more difficult to understand at first because there are more elements competing for the user’s attention.
One improvement I could make to the NYC Flight Explorer would be to add an interactive map showing the destinations of the currently filtered flights. The map could use the NYC airport as the starting point and display destination airports, which would make the geographic patterns in the data easier to understand. A second addition would be a second visualization comparing delay patterns by airline or departure airport, such as a boxplot of arrival delay by airline or by EWR, JFK, and LGA. This would give users a clearer way to compare groups rather than only examining individual flights in a scatterplot. A third improvement would be to make the flight table interactive so users could sort columns, search for specific destinations, and move through more than the ten most delayed flights. These additions would make the app more useful while still keeping the overall layout relatively simple and focused.