My data set is about the 2023 marathon in boston this dataset includes 2 catergorical varibales age and gender and one qantative varible which is finsih net times so in my project i will be showing Which runner, the top male or top female, was more remarkable in comparison to their respective fields?The dataset contain information about participants in the 2023 Boston Marathon. This includes details such as age, gender, nationality, race times, and potentially additional demographic or performance-related variables. The data was sourced from the SCORE Network, a platform dedicated to sports data and analytics. thie source of this dataset is from https://data.scorenetwork.org/running/boston_marathon_2023.html.

Load necessary libraries

library(readr)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)

Load the dataset

boston_marathon_2023 <- read_csv("https://data.scorenetwork.org/data/boston_marathon_2023.csv")
## Rows: 26598 Columns: 15
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (8): age_group, name, team, bib_number, half_time, finish_net, finish_gu...
## dbl (7): place_overall, place_gender, place_division, half_time_sec, finish_...
## 
## ℹ 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.

Display the structure of the dataset

str(boston_marathon_2023)
## spc_tbl_ [26,598 × 15] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ age_group         : chr [1:26598] "18-39" "18-39" "18-39" "18-39" ...
##  $ place_overall     : num [1:26598] 1 2 3 4 5 6 7 8 9 10 ...
##  $ place_gender      : num [1:26598] 1 2 3 4 5 6 7 8 9 10 ...
##  $ place_division    : num [1:26598] 1 2 3 4 5 6 7 8 9 10 ...
##  $ name              : chr [1:26598] "Chebet, Evans" "Geay, Gabriel" "Kipruto, Benson" "Korir, Albert" ...
##  $ team              : chr [1:26598] "Team–" "Team–" "Team–" "Team–" ...
##  $ bib_number        : chr [1:26598] "1" "3" "5" "19" ...
##  $ half_time         : chr [1:26598] "1H 2M 20S" "1H 2M 20S" "1H 2M 19S" "1H 2M 20S" ...
##  $ finish_net        : chr [1:26598] "2H 5M 54S" "2H 6M 4S" "2H 6M 6S" "2H 8M 1S" ...
##  $ finish_gun        : chr [1:26598] "2H 5M 54S" "2H 6M 4S" "2H 6M 6S" "2H 8M 1S" ...
##  $ gender            : chr [1:26598] "M" "M" "M" "M" ...
##  $ half_time_sec     : num [1:26598] 3740 3740 3739 3740 3740 ...
##  $ finish_net_sec    : num [1:26598] 7554 7564 7566 7681 7715 ...
##  $ finish_gun_sec    : num [1:26598] 7554 7564 7566 7681 7715 ...
##  $ finish_net_minutes: num [1:26598] 126 126 126 128 129 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   age_group = col_character(),
##   ..   place_overall = col_double(),
##   ..   place_gender = col_double(),
##   ..   place_division = col_double(),
##   ..   name = col_character(),
##   ..   team = col_character(),
##   ..   bib_number = col_character(),
##   ..   half_time = col_character(),
##   ..   finish_net = col_character(),
##   ..   finish_gun = col_character(),
##   ..   gender = col_character(),
##   ..   half_time_sec = col_double(),
##   ..   finish_net_sec = col_double(),
##   ..   finish_gun_sec = col_double(),
##   ..   finish_net_minutes = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>

View the first few rows of the dataset

head(boston_marathon_2023)
## # A tibble: 6 × 15
##   age_group place_overall place_gender place_division name      team  bib_number
##   <chr>             <dbl>        <dbl>          <dbl> <chr>     <chr> <chr>     
## 1 18-39                 1            1              1 Chebet, … Team– 1         
## 2 18-39                 2            2              2 Geay, Ga… Team– 3         
## 3 18-39                 3            3              3 Kipruto,… Team– 5         
## 4 18-39                 4            4              4 Korir, A… Team– 19        
## 5 18-39                 5            5              5 Talbi, Z… Team– 31        
## 6 18-39                 6            6              6 Kipchoge… Team– 2         
## # ℹ 8 more variables: half_time <chr>, finish_net <chr>, finish_gun <chr>,
## #   gender <chr>, half_time_sec <dbl>, finish_net_sec <dbl>,
## #   finish_gun_sec <dbl>, finish_net_minutes <dbl>

Check for missing values

missing_values <- colSums(is.na(boston_marathon_2023))
print(missing_values)
##          age_group      place_overall       place_gender     place_division 
##                  0                  0                  0                  0 
##               name               team         bib_number          half_time 
##                  0                  0                  0                 72 
##         finish_net         finish_gun             gender      half_time_sec 
##                  0                  0                  0                 72 
##     finish_net_sec     finish_gun_sec finish_net_minutes 
##                  0                  0                  0

Check data types

data_types <- sapply(boston_marathon_2023, class)
print(data_types)
##          age_group      place_overall       place_gender     place_division 
##        "character"          "numeric"          "numeric"          "numeric" 
##               name               team         bib_number          half_time 
##        "character"        "character"        "character"        "character" 
##         finish_net         finish_gun             gender      half_time_sec 
##        "character"        "character"        "character"          "numeric" 
##     finish_net_sec     finish_gun_sec finish_net_minutes 
##          "numeric"          "numeric"          "numeric"

Summary statistics for numeric variables

summary(boston_marathon_2023)
##   age_group         place_overall    place_gender   place_division
##  Length:26598       Min.   :    1   Min.   :    1   Min.   :   1  
##  Class :character   1st Qu.: 6656   1st Qu.: 3325   1st Qu.: 469  
##  Mode  :character   Median :13318   Median : 6650   Median :1085  
##                     Mean   :13315   Mean   : 6783   Mean   :1485  
##                     3rd Qu.:19972   3rd Qu.: 9975   3rd Qu.:2013  
##                     Max.   :26623   Max.   :15176   Max.   :5285  
##                                                                   
##      name               team            bib_number         half_time        
##  Length:26598       Length:26598       Length:26598       Length:26598      
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##   finish_net         finish_gun           gender          half_time_sec  
##  Length:26598       Length:26598       Length:26598       Min.   : 3739  
##  Class :character   Class :character   Class :character   1st Qu.: 5548  
##  Mode  :character   Mode  :character   Mode  :character   Median : 6213  
##                                                           Mean   : 6413  
##                                                           3rd Qu.: 7016  
##                                                           Max.   :12435  
##                                                           NA's   :72     
##  finish_net_sec  finish_gun_sec  finish_net_minutes
##  Min.   : 7554   Min.   : 7554   Min.   :125.9     
##  1st Qu.:11464   1st Qu.:11584   1st Qu.:191.1     
##  Median :12836   Median :12998   Median :213.9     
##  Mean   :13351   Mean   :13527   Mean   :222.5     
##  3rd Qu.:14667   3rd Qu.:14914   3rd Qu.:244.4     
##  Max.   :26431   Max.   :26484   Max.   :440.5     
## 

Check unique values in the ‘age_group’ variable

unique_values <- unique(boston_marathon_2023$age_group)
print(unique_values)
##  [1] "18-39" "40-44" "45-49" "50-54" "55-59" "60-64" "65-69" "70-74" "75-79"
## [10] "80+"

Check data types of all variables

data_types <- sapply(boston_marathon_2023, class)
print(data_types)
##          age_group      place_overall       place_gender     place_division 
##        "character"          "numeric"          "numeric"          "numeric" 
##               name               team         bib_number          half_time 
##        "character"        "character"        "character"        "character" 
##         finish_net         finish_gun             gender      half_time_sec 
##        "character"        "character"        "character"          "numeric" 
##     finish_net_sec     finish_gun_sec finish_net_minutes 
##          "numeric"          "numeric"          "numeric"

Create a bar plot for the distribution of participant age groups

barplot(table(boston_marathon_2023$age_group), 
        main = "Distribution of Participant Age Groups",
        xlab = "Age Group", ylab = "Frequency", col = "skyblue")

Check the data type of the ‘age_group’ variable

class(boston_marathon_2023$age_group)
## [1] "character"

Check summary statistics of the ‘age_group’ variable

summary(boston_marathon_2023$age_group)
##    Length     Class      Mode 
##     26598 character character

Remove rows with missing or NaN values in the ‘age_group’ variable

cleaned_data <- boston_marathon_2023[!is.na(boston_marathon_2023$age_group) & !is.nan(boston_marathon_2023$age_group), ]

Check summary statistics of the cleaned ‘age_group’ variable

summary(cleaned_data$age_group)
##    Length     Class      Mode 
##     26598 character character

Check the distribution of participant age groups using a barplot

barplot(table(boston_marathon_2023$age_group), 
        main = "Distribution of Participant Age Groups",
        xlab = "Age Group", ylab = "Frequency", col = "skyblue")

Check the distribution of participant age groups using a his with a specified number of breaks

Create a bar plot for the distribution of participant age groups

barplot(table(boston_marathon_2023$age_group), 
        main = "Distribution of Participant Age Groups",
        xlab = "Age Group", ylab = "Frequency", col = "skyblue")

Check summary statistics of the ‘finish_net_sec’ variable

summary(boston_marathon_2023$finish_net_sec)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    7554   11464   12836   13351   14667   26431

Create a histogram for the distribution of net finishing times

hist(boston_marathon_2023$finish_net_sec, main = "Distribution of Net Finishing Times",
     xlab = "Net Finishing Time (seconds)", ylab = "Frequency", col = "skyblue")

# Create a scatter plot of net finishing time vs. gender

boston_marathon_2023$gender <- ifelse(boston_marathon_2023$gender == "W", "F", boston_marathon_2023$gender)

Create a violon plot of net finishing time vs. gender (M/W)

ggplot(boston_marathon_2023, aes(x = gender, y = finish_net_sec, fill = gender)) +
  geom_violin(trim = FALSE) +
  labs(title = "Net Finishing Time by Gender",
       x = "Gender",
       y = "Net Finishing Time (seconds)") +
  scale_fill_manual(values = c("blue", "pink")) +
  theme_minimal()

Check for missing values in ‘gender’

missing_gender <- sum(is.na(boston_marathon_2023$gender))

Check for missing values in ‘finish_net_sec’

missing_finish_net_sec <- sum(is.na(boston_marathon_2023$finish_net_sec))
print(paste("Missing values in 'gender':", missing_gender))
## [1] "Missing values in 'gender': 0"
print(paste("Missing values in 'finish_net_sec':", missing_finish_net_sec))
## [1] "Missing values in 'finish_net_sec': 0"

Ensure ‘finish_net_sec’ is numeric

boston_marathon_2023$finish_net_sec <- as.numeric(boston_marathon_2023$finish_net_sec)

Create side-by-side box plots for age distribution by gender

ggplot(boston_marathon_2023, aes(x = gender, y = age_group, fill = gender)) +
  geom_boxplot() +
  labs(title = "Age Distribution by Gender", x = "Gender", y = "Age") +
  scale_fill_manual(values = c("skyblue", "pink")) +
  theme_minimal()

Check unique values and missing values in ‘gender’

print(unique(boston_marathon_2023$gender))
## [1] "M" "F"
print(sum(is.na(boston_marathon_2023$gender)))
## [1] 0

Check for missing values in ‘age’

print(sum(is.na(boston_marathon_2023$age_group)))
## [1] 0

Categorize ages into groups (18-39)

str(boston_marathon_2023)
## spc_tbl_ [26,598 × 15] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ age_group         : chr [1:26598] "18-39" "18-39" "18-39" "18-39" ...
##  $ place_overall     : num [1:26598] 1 2 3 4 5 6 7 8 9 10 ...
##  $ place_gender      : num [1:26598] 1 2 3 4 5 6 7 8 9 10 ...
##  $ place_division    : num [1:26598] 1 2 3 4 5 6 7 8 9 10 ...
##  $ name              : chr [1:26598] "Chebet, Evans" "Geay, Gabriel" "Kipruto, Benson" "Korir, Albert" ...
##  $ team              : chr [1:26598] "Team–" "Team–" "Team–" "Team–" ...
##  $ bib_number        : chr [1:26598] "1" "3" "5" "19" ...
##  $ half_time         : chr [1:26598] "1H 2M 20S" "1H 2M 20S" "1H 2M 19S" "1H 2M 20S" ...
##  $ finish_net        : chr [1:26598] "2H 5M 54S" "2H 6M 4S" "2H 6M 6S" "2H 8M 1S" ...
##  $ finish_gun        : chr [1:26598] "2H 5M 54S" "2H 6M 4S" "2H 6M 6S" "2H 8M 1S" ...
##  $ gender            : chr [1:26598] "M" "M" "M" "M" ...
##  $ half_time_sec     : num [1:26598] 3740 3740 3739 3740 3740 ...
##  $ finish_net_sec    : num [1:26598] 7554 7564 7566 7681 7715 ...
##  $ finish_gun_sec    : num [1:26598] 7554 7564 7566 7681 7715 ...
##  $ finish_net_minutes: num [1:26598] 126 126 126 128 129 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   age_group = col_character(),
##   ..   place_overall = col_double(),
##   ..   place_gender = col_double(),
##   ..   place_division = col_double(),
##   ..   name = col_character(),
##   ..   team = col_character(),
##   ..   bib_number = col_character(),
##   ..   half_time = col_character(),
##   ..   finish_net = col_character(),
##   ..   finish_gun = col_character(),
##   ..   gender = col_character(),
##   ..   half_time_sec = col_double(),
##   ..   finish_net_sec = col_double(),
##   ..   finish_gun_sec = col_double(),
##   ..   finish_net_minutes = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>

Check for missing values in the age variable

sum(is.na(boston_marathon_2023$age_group))
## [1] 0

Step 1: Verify the structure of the age_group variable

str(boston_marathon_2023$age_group)
##  chr [1:26598] "18-39" "18-39" "18-39" "18-39" "18-39" "18-39" "18-39" ...

Step 2: Convert to Numeric

If you need to convert age_group to numeric, create a new variable

boston_marathon_2023$numeric_age_group <- as.numeric(as.character(boston_marathon_2023$age_group))
## Warning: NAs introduced by coercion

Step 3: Verify the conversion

str(boston_marathon_2023$numeric_age_group)
##  num [1:26598] NA NA NA NA NA NA NA NA NA NA ...

Create a box plot of finish times by age group

ggplot(boston_marathon_2023, aes(x = gender, y = finish_net_sec)) +
  geom_boxplot(aes(fill = gender), fill = c("skyblue", "pink")) +
  labs(title = "Finish Time Distribution by Gender", x = "Gender", y = "Finish Time (seconds)") +
  scale_fill_identity() +  # Use identity scale for fill colors
  theme_minimal()

Subset the data for male and female participants

male_finish_times <- boston_marathon_2023$finish_net_sec[boston_marathon_2023$gender == "M"]
female_finish_times <- boston_marathon_2023$finish_net_sec[boston_marathon_2023$gender == "W"]

Check the number of observations in male_finish_times and female_finish_times

length(male_finish_times)
## [1] 15175
length(female_finish_times)
## [1] 0

Check for missing values

sum(is.na(male_finish_times))
## [1] 0
sum(is.na(female_finish_times))
## [1] 0

Confirm data type

class(male_finish_times)
## [1] "numeric"
class(female_finish_times)
## [1] "numeric"

Inspect variable values

summary(male_finish_times)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    7554   10774   12056   12746   14024   26019
summary(female_finish_times)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 

Review t-test assumptions

Check the structure of the female_finish_times variable

str(female_finish_times)
##  num(0)

Inspect the data in the female_finish_times variable

summary(female_finish_times)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 

Perform two-sample t-test

Create an interactive scatter plot

plot_ly(data = boston_marathon_2023, x = ~age_group, y = ~finish_net_sec, color = ~gender,
        type = 'scatter', mode = 'markers', marker = list(size = 8)) %>%
  layout(title = "Finish Time vs. Age (Interactive)",
         xaxis = list(title = "Age"),
         yaxis = list(title = "Finish Time (seconds)"),
         hoverinfo = "text",
         text = ~paste("Gender: ", gender))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
## Warning: 'layout' objects don't have these attributes: 'hoverinfo', 'text'
## Valid attributes include:
## '_deprecated', 'activeshape', 'annotations', 'autosize', 'autotypenumbers', 'calendar', 'clickmode', 'coloraxis', 'colorscale', 'colorway', 'computed', 'datarevision', 'dragmode', 'editrevision', 'editType', 'font', 'geo', 'grid', 'height', 'hidesources', 'hoverdistance', 'hoverlabel', 'hovermode', 'images', 'legend', 'mapbox', 'margin', 'meta', 'metasrc', 'modebar', 'newshape', 'paper_bgcolor', 'plot_bgcolor', 'polar', 'scene', 'selectdirection', 'selectionrevision', 'separators', 'shapes', 'showlegend', 'sliders', 'smith', 'spikedistance', 'template', 'ternary', 'title', 'transition', 'uirevision', 'uniformtext', 'updatemenus', 'width', 'xaxis', 'yaxis', 'barmode', 'bargap', 'mapType'

The dataset was sourced from the provided link: Boston Marathon 2023 Dataset., I performed data cleaning to ensure its quality and integrity for analysis. This involved addressing missing values, checking for outliers, and formatting variables as needed. For example, I removed any incomplete rows with missing data points and ensured consistency in the formatting of categorical variables.

I chose this topic and dataset because marathon running is not only a popular sport but its interesting to me . Analyzing the data from an event like the Boston Marathon allows for insights into the demographics of participants, their performance, and potentially uncovering any disparities or interesting trends.

The Boston Marathon is one of the oldest marathons in the world, attracting elite athletes as well as amateur runners from around the globe. It was first held in 1897 and is held annually on Patriots’ Day, the third Monday of April, in Boston, Massachusetts, USA.

In recent years, the Boston Marathon has become a symbol of resilience and unity, particularly after the tragic bombings near the finish line in 2013. The event continues to draw thousands of participants and spectators each year, showcasing the spirit of community and determination.

One interesting pattern that emerged from the visualization is the general trend of older participants having longer finish times compared to younger participants. Additionally, there was a noticeable difference in finish times between male and female participants, with male participants tending to have shorter finish times on average.

While the visualization effectively captured the main relationship of interest, there could have been additional variables included for a more comprehensive analysis. For example, incorporating data on training mileage, weather conditions, or previous marathon experience could provide further insights into performance determinants.

Overall, the visualization explores the relationship between age, gender, and finish times in the Boston Marathon dataset, offering valuable insights into the demographics and performance of participants.