RollerCoasters Project1

Project 1 - RollerCoasters.CSV Visualization Analysis

Project Intro

The dataset contains a list of scrapped rollercoaster Wikipedia pages that include, but not limited to, the roller coaster’s name, length in meters, speed in mph, general location, opening date, composition, maufacturer, cost to build, and height. There are several missing entries for multiple variables that will be filtered out. The variables in the focus in the analysis of this dataset will include: rollercoaster’s length, speed, name, and age. These variables will be visually analyzed to understand any associations between rollercoaster variables.

The creator for the dataset is Rob Mulla, who used code to scrap rollercoaster wikipedia articles to create this dataset. The code can be found here: https://github.com/RobMulla/twitch-stream-projects/tree/main/001-rollercoaster-dataset

Load Libraries

library("tidyverse")
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.1     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.3     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)

Load Dataset & Check Dataset Characteristics

Raw_RollerCoasters <- read_csv("roller_coasters.csv")
Rows: 1087 Columns: 56
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (46): coaster_name, Length, Speed, Location, Status, Opening date, Type,...
dbl (10): Inversions, year_introduced, latitude, longitude, speed1_value, sp...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(Raw_RollerCoasters)
# A tibble: 6 × 56
  coaster_name    Length Speed Location Status `Opening date` Type  Manufacturer
  <chr>           <chr>  <chr> <chr>    <chr>  <chr>          <chr> <chr>       
1 Switchback Rai… 600 f… 6 mp… Coney I… Remov… June 16, 1884  Wood  LaMarcus Ad…
2 Flip Flap Rail… <NA>   <NA>  Sea Lio… Remov… 1895           Wood  Lina Beecher
3 Switchback Rai… <NA>   <NA>  Clevela… Closed <NA>           Other <NA>        
4 Loop the Loop … <NA>   <NA>  Other    Remov… 1901           Steel Edwin Presc…
5 Loop the Loop … <NA>   <NA>  Other    Remov… 1901           Steel Edwin Presc…
6 Cannon Coaster  <NA>   <NA>  Coney I… Remov… 1902           Wood  George Fran…
# ℹ 48 more variables: `Height restriction` <chr>, Model <chr>, Height <chr>,
#   Inversions <dbl>, `Lift/launch system` <chr>, Cost <chr>, Trains <chr>,
#   `Park section` <chr>, Duration <chr>, Capacity <chr>, `G-force` <chr>,
#   Designer <chr>, `Max vertical angle` <chr>, Drop <chr>,
#   `Soft opening date` <chr>, `Fast Lane available` <chr>, Replaced <chr>,
#   `Track layout` <chr>, `Fastrack available` <chr>,
#   `Soft opening date.1` <chr>, `Closing date` <chr>, Opened <chr>, …
dim(Raw_RollerCoasters)
[1] 1087   56
summary(Raw_RollerCoasters)
    coaster_name        Length           Speed           Location   
 Length   :1087   Length   :1087   Length   :1087   Length   :1087  
 N.unique : 990   N.unique : 569   N.unique : 243   N.unique : 280  
 N.blank  :   0   N.blank  :   0   N.blank  :   0   N.blank  :   0  
 Min.nchar:   4   Min.nchar:   9   Min.nchar:   8   Min.nchar:   5  
 Max.nchar:  57   Max.nchar:  66   Max.nchar:  23   Max.nchar:  68  
                  NAs      : 134   NAs      : 150                   
                                                                    
       Status        Opening date         Type         Manufacturer 
 Length   :1087   Length   :1087   Length   :1087   Length   :1087  
 N.unique :  15   N.unique : 655   N.unique :  98   N.unique : 102  
 N.blank  :   0   N.blank  :   0   N.blank  :   0   N.blank  :   0  
 Min.nchar:   6   Min.nchar:   4   Min.nchar:   4   Min.nchar:   3  
 Max.nchar:  62   Max.nchar:  75   Max.nchar:  36   Max.nchar: 107  
 NAs      : 213   NAs      : 250                    NAs      :  59  
                                                                    
 Height restriction       Model            Height       Inversions    
 Length   :1087     Length   :1087   Length   :1087   Min.   : 0.000  
 N.unique : 100     N.unique : 317   N.unique : 382   1st Qu.: 0.000  
 N.blank  :   0     N.blank  :   0   N.blank  :   0   Median : 0.000  
 Min.nchar:  13     Min.nchar:   3   Min.nchar:   8   Mean   : 1.547  
 Max.nchar:  54     Max.nchar:  67   Max.nchar:  26   3rd Qu.: 3.000  
 NAs      : 256     NAs      : 343   NAs      : 122   Max.   :14.000  
                                                      NAs    :155     
 Lift/launch system        Cost            Trains        Park section 
 Length   :1087     Length   :1087   Length   :1087   Length   :1087  
 N.unique : 116     N.unique : 272   N.unique : 221   N.unique : 271  
 N.blank  :   0     N.blank  :   0   N.blank  :   0   N.blank  :   0  
 Min.nchar:   3     Min.nchar:   4   Min.nchar:  47   Min.nchar:   3  
 Max.nchar: 168     Max.nchar:  82   Max.nchar: 282   Max.nchar:  59  
 NAs      : 292     NAs      : 705   NAs      : 369   NAs      : 600  
                                                                      
      Duration         Capacity         G-force          Designer   
 Length   :1087   Length   :1087   Length   :1087   Length   :1087  
 N.unique : 207   N.unique : 160   N.unique :  66   N.unique : 153  
 N.blank  :   0   N.blank  :   0   N.blank  :   0   N.blank  :   0  
 Min.nchar:   2   Min.nchar:   6   Min.nchar:   1   Min.nchar:   4  
 Max.nchar:  51   Max.nchar:  71   Max.nchar:  31   Max.nchar:  85  
 NAs      : 322   NAs      : 512   NAs      : 725   NAs      : 509  
                                                                    
 Max vertical angle        Drop      Soft opening date Fast Lane available
 Length   :1087     Length   :1087   Length   :1087    Length   :1087     
 N.unique :  83     N.unique : 235   N.unique :  84    N.unique :   1     
 N.blank  :   0     N.blank  :   0   N.blank  :   0    N.blank  :   0     
 Min.nchar:   2     Min.nchar:   8   Min.nchar:   4    Min.nchar:  19     
 Max.nchar:  35     Max.nchar:  19   Max.nchar:  27    Max.nchar:  19     
 NAs      : 730     NAs      : 593   NAs      : 991    NAs      :1018     
                                                                          
      Replaced       Track layout  Fastrack available Soft opening date.1
 Length   :1087   Length   :1087   Length   :1087     Length   :1087     
 N.unique : 140   N.unique :  95   N.unique :   1     N.unique :  84     
 N.blank  :   0   N.blank  :   0   N.blank  :   0     N.blank  :   0     
 Min.nchar:   4   Min.nchar:   4   Min.nchar:  18     Min.nchar:   4     
 Max.nchar: 118   Max.nchar:  73   Max.nchar:  18     Max.nchar:  27     
 NAs      : 914   NAs      : 752   NAs      :1068     NAs      : 991     
                                                                         
    Closing date        Opened        Replaced by        Website    
 Length   :1087   Length   :1087   Length   :1087   Length   :1087  
 N.unique : 144   N.unique :  16   N.unique :  67   N.unique :  16  
 N.blank  :   0   N.blank  :   0   N.blank  :   0   N.blank  :   0  
 Min.nchar:   4   Min.nchar:   4   Min.nchar:   5   Min.nchar:  12  
 Max.nchar:  39   Max.nchar:  88   Max.nchar:  52   Max.nchar:  46  
 NAs      : 851   NAs      :1060   NAs      : 999   NAs      :1000  
                                                                    
 Flash Pass Available Must transfer from wheelchair       Theme     
 Length   :1087       Length   :1087                Length   :1087  
 N.unique :   1       N.unique :   1                N.unique :  33  
 N.blank  :   0       N.blank  :   0                N.blank  :   0  
 Min.nchar:  20       Min.nchar:  29                Min.nchar:   4  
 Max.nchar:  20       Max.nchar:  29                Max.nchar: 107  
 NAs      :1037       NAs      : 981                NAs      :1043  
                                                                    
 Single rider line available  Restraint Style Flash Pass available
 Length   :1087              Length   :1087   Length   :1087      
 N.unique :   1              N.unique :   6   N.unique :   1      
 N.blank  :   0              N.blank  :   0   N.blank  :   0      
 Min.nchar:  27              Min.nchar:   7   Min.nchar:  20      
 Max.nchar:  27              Max.nchar:  67   Max.nchar:  20      
 NAs      :1006              NAs      :1065   NAs      :1041      
                                                                  
    Acceleration      Restraints          Name      year_introduced
 Length   :1087   Length   :1087   Length   :1087   Min.   :1884   
 N.unique :  50   N.unique :  12   N.unique :  18   1st Qu.:1989   
 N.blank  :   0   N.blank  :   0   N.blank  :   0   Median :2000   
 Min.nchar:   1   Min.nchar:   7   Min.nchar:   7   Mean   :1995   
 Max.nchar:  65   Max.nchar:  46   Max.nchar:  42   3rd Qu.:2010   
 NAs      :1027   NAs      :1063   NAs      :1052   Max.   :2022   
                                                                   
    latitude        longitude            Type_Main    opening_date_clean
 Min.   :-48.26   Min.   :-123.036   Length   :1087   Length   :1087    
 1st Qu.: 35.03   1st Qu.: -84.552   N.unique :   3   N.unique : 602    
 Median : 40.29   Median : -76.654   N.blank  :   0   N.blank  :   0    
 Mean   : 38.37   Mean   : -41.595   Min.nchar:   4   Min.nchar:   8    
 3rd Qu.: 44.80   3rd Qu.:   2.778   Max.nchar:   5   Max.nchar:  10    
 Max.   : 63.23   Max.   : 153.427                    NAs      : 250    
 NAs    :275      NAs    :275                                           
       speed1           speed2      speed1_value       speed1_unit  
 Length   :1087   Length   :1087   Min.   :  5.00   Length   :1087  
 N.unique : 225   N.unique : 229   1st Qu.: 40.00   N.unique :   2  
 N.blank  :   0   N.blank  :   0   Median : 50.00   N.blank  :   0  
 Min.nchar:   5   Min.nchar:   6   Mean   : 53.85   Min.nchar:   3  
 Max.nchar:  10   Max.nchar:  11   3rd Qu.: 63.00   Max.nchar:   4  
 NAs      : 150   NAs      : 152   Max.   :240.00   NAs      : 150  
                                   NAs    :150                      
   speed_mph       height_value        height_unit     height_ft    
 Min.   :  5.00   Min.   :   4.00   Length   :1087   Min.   : 13.1  
 1st Qu.: 37.30   1st Qu.:  44.00   N.unique :   2   1st Qu.: 51.8  
 Median : 49.70   Median :  79.00   N.blank  :   0   Median : 91.2  
 Mean   : 48.62   Mean   :  89.58   Min.nchar:   1   Mean   :102.0  
 3rd Qu.: 58.00   3rd Qu.: 113.00   Max.nchar:   2   3rd Qu.:131.2  
 Max.   :149.10   Max.   :3937.00   NAs      : 122   Max.   :377.3  
 NAs    :150      NAs    :122                        NAs    :916    
 Inversions_clean  Gforce_clean   
 Min.   : 0.000   Min.   : 0.800  
 1st Qu.: 0.000   1st Qu.: 3.400  
 Median : 0.000   Median : 4.000  
 Mean   : 1.327   Mean   : 3.824  
 3rd Qu.: 2.000   3rd Qu.: 4.500  
 Max.   :14.000   Max.   :12.000  
                  NAs    :725     

Clean Dataset

clean_rollercoaster <- Raw_RollerCoasters |> filter(!is.na(Length), !is.na(speed1), !is.na(coaster_name), !is.na(`Opening date`)) |>  mutate(
    opening_year = as.numeric(gsub(".*?([0-9]{4}).*", "\\1", opening_date_clean, perl = TRUE)),
    age = 2026 - opening_year, Length_m = parse_number(str_extract(Length, "[0-9,.]+(?=\\s*m)"))) |> filter(!is.na(age),!is.na(Length_m)
)

Uses filter & mutate to do so with smaller functions gsub and as.numeric to clean and convert the character array into numerical values. The age is an approximation as it only looks at the years the rides were opened and subtracts that from the current year.

Exploratory Plots

Plot 1 : Ride Age and Length.

ggplot(clean_rollercoaster, aes(x= age, y= Length_m, color = age)) + geom_point() +  geom_smooth(
    method = "lm",
    formula = y ~ x,
    se = TRUE,
    color = "black"
  )+ theme_minimal() + labs(x="Roller Coaster Age", y ="Roller Coaster Length (Meters)", title = "Roller Coaster Length Plotted against Roller Coaster Age", color = "Age")

PLot 2: Roller Coaster’s Speed and Age

ggplot(
  clean_rollercoaster,
  aes(x = age, y = speed_mph, color = age)
) +
  geom_point() +
  geom_smooth(
    method = "lm",
    formula = y ~ x,
    se = TRUE,
    color = "black"
  ) +
  theme_minimal() +
  labs(
    x = "Roller Coaster Age (years)",
    y = "Roller Coaster Speed (mph)",
    title = "Roller Coaster Speed Plotted Against Age",
    color = "Age"
  )

Final Visulization Plot

# Select four locations with many roller coasters
final_plot_data <- clean_rollercoaster |>
  filter(
    Location %in% c(
      "Kings Island",
      "Cedar Point",
      "Six Flags Magic Mountain",
      "Carowinds"
    )
  )

# Select the fastest coasters to label by name
fast_coasters <- final_plot_data |>
  filter(speed_mph >= 90)

# Final visualization
ggplot(
  final_plot_data,
  aes(
    x = age,
    y = speed_mph,
    color = Location
  )
) +
  geom_point(
    size = 3,
    alpha = 0.6
  ) +
  geom_smooth(
    method = "lm",
    formula = y ~ x,
    se = FALSE,
    linetype = "dotdash",
    linewidth = 0.7
  ) +
  geom_text(
    data = fast_coasters,
    aes(label = coaster_name),
    nudge_y = 3,
    size = 3,
    show.legend = FALSE
  ) +
  scale_color_brewer(
    name = "Roller Coaster Location",
    palette = "Set1"
  ) +
  theme_minimal(base_size = 12) +
  labs(
    title = "Roller Coaster Speed and Age Across Four Amusement Parks",
    subtitle = "Dashed lines are the Linear Relationships for each location",
    x = "Roller Coaster Age (years)",
    y = "Maximum Roller Coaster Speed (mph)",
    caption = paste(
      "Source: Rob Mulla, Roller Coaster Dataset;",
      "compiled from Wikipedia roller-coaster pages"
    )
  )

To prepare the roller-coaster dataset for analysis, I first filtered out observations with missing values for length, speed, coaster name, or opening date. Because the opening-date information was stored as character text and included multiple date formats, I used a regular expression with gsub() to extract the four-digit opening year. I converted the extracted year to numeric form and estimated each roller coaster’s age by subtracting the opening year from 2026. The Length column also contained text, units, and multiple formats, so I used str_extract() to extract the number immediately before the meter symbol and parse_number() to convert it into a numeric variable named Length_m. After creating the new variables, I filtered out rows in which age or length remained missing or non-finite. These steps produced numeric variables that could be used correctly in scatterplots and linear-regression models.

The final visualization examines the relationship between roller-coaster age and maximum speed. Age is displayed on the x-axis in years, and speed is displayed on the y-axis in miles per hour. The points are colored according to four selected locations: Kings Island, Cedar Point, Six Flags Magic Mountain, and Carowinds. A different dashed linear-regression line is calculated for each location because location is mapped to color for the entire graph. The graph does not display confidence-interval bands because se = FALSE was used. Roller coasters reaching at least 90 miles per hour are labeled by name to identify especially fast rides. Overall, the graph appears to suggest that newer roller coasters tend to reach higher speeds, although there is considerable variation within and across locations. Some older coasters remain relatively fast, while some newer coasters have only moderate speeds. The overlap among the four locations suggests that location alone does not fully explain differences in roller-coaster speed.

I originally wanted to include every available location and label every roller coaster by name. However, including all locations created too many categories, colors, and legend entries, while labeling every point caused the coaster names to overlap. To keep the final visualization readable, I limited it to four amusement-park locations and labeled only coasters with speeds of at least 90 miles per hour. I also considered including roller-coaster length in the same visualization, but displaying age, speed, location, and selected coaster names already communicated several variables without making the graph overly crowded or difficult to interpret.