Step One: Setting Up Library and Reading in Data

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)

The NYC Bike Share Analysis from Kaggle (https://www.kaggle.com/code/samratp/bike-share-analysis/output?select=NYC-2016-Summary.csv) is going to be used for this tutorial. Initially, the data can be read in as a dataframe and we can find the variable types of each column.

bike <- read.csv("NYC-2016-Summary.csv", header = TRUE, sep = ",")

str(bike)
## 'data.frame':    276798 obs. of  5 variables:
##  $ duration   : num  13.98 11.43 5.25 12.32 20.88 ...
##  $ month      : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ hour       : int  0 0 0 0 0 0 0 1 1 1 ...
##  $ day_of_week: chr  "Friday" "Friday" "Friday" "Friday" ...
##  $ user_type  : chr  "Customer" "Subscriber" "Subscriber" "Subscriber" ...

While this is well and good for reading in a dataframe, the month, hour, day_of_week, and user_type columns are all categorical data. This can make data manipulation and visualization difficult as there is no order to these columns. By reading in the csv and specifying these columns as factors, it will make data usage and visualization more manageable.

bikes <- read.csv("NYC-2016-Summary.csv", header = TRUE, sep = ",") %>%
  mutate(across(c(month, hour, day_of_week, user_type), as.factor))

str(bikes)
## 'data.frame':    276798 obs. of  5 variables:
##  $ duration   : num  13.98 11.43 5.25 12.32 20.88 ...
##  $ month      : Factor w/ 12 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ hour       : Factor w/ 24 levels "0","1","2","3",..: 1 1 1 1 1 1 1 2 2 2 ...
##  $ day_of_week: Factor w/ 7 levels "Friday","Monday",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ user_type  : Factor w/ 3 levels "","Customer",..: 2 3 3 3 2 3 3 3 3 2 ...

Step Two: Conversions

Unfortunately, the months are listed as their list position in numerical order (1 = Jan, 2 = Feb, etc) and we want then as their three letter abbreviation so the categories are more recognizable. Luckily, the previous section had us read in the month column as a factor, making this step a breeze as there is a function that exists in base R for such a thing.

bikes$month <- month.abb[bikes$month]

str(bikes$month)
##  chr [1:276798] "Jan" "Jan" "Jan" "Jan" "Jan" "Jan" "Jan" "Jan" "Jan" "Jan" ...

Performing this conversion does turn the column back to a character variable, so the months will need to be converted back to factors. When the conversion is made, we want to specify the order of the levels of the months in chronological order. This has to be accomplished by creating a list of the months in chronological order and applying it to the months column when we convert it to a factor.

month <- c("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")

str(month)
##  chr [1:12] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" ...

Now the conversion of the months column back to a factor can be made.

bikes$month <- factor(bikes$month, levels = month)

str(bikes$month)
##  Factor w/ 12 levels "Jan","Feb","Mar",..: 1 1 1 1 1 1 1 1 1 1 ...

Specifying the order of our levels, in this particular case of months, is important for several reasons. When the levels get printed out (which will be seen later) we can see all of the levels to make sure there are none that are repeated or missing, in this case we do not have levels pertaining to the same month or missing months. This is important for data visualization as if we were to create plots using the months as categories, our data would be presented in alphabetical order rather than chronological order, which is essential for data interpretation. In this case, it would be harder to see the correlation between how long people were renting bikes and the time of year.

Additionally, the hour column should be the time of day, and since this column has been read in as a factor it will make this easier. The strptime function allows for the conversion of the hour as a numerical value to the associated time of day. This function does result in the hour being converted to the time of day but also gives the date specified, and if not specified then it would report the current date, and since this dataset is from 2016, that is not very helpful. This is where format() is needed. By running the strptime function in a format function, you can specify how you want the output after the strptime function. In this case, by specifying we only want the time, we can lose the date format the strptime function would normally give.

bikes$hour <- format(strptime(bikes$hour, format = '%H'), '%H:%M:%S')

str(bikes$hour)
##  chr [1:276798] "00:00:00" "00:00:00" "00:00:00" "00:00:00" "00:00:00" ...

The conversion back to factors also has to be made, but since the levels will already be in numerical order we do not need to specify the order of the levels.

bikes$hour <- factor(bikes$hour)

str(bikes$hour)
##  Factor w/ 24 levels "00:00:00","01:00:00",..: 1 1 1 1 1 1 1 2 2 2 ...
levels(bikes$month)
##  [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
nlevels(bikes$month)
## [1] 12
levels(bikes$hour)
##  [1] "00:00:00" "01:00:00" "02:00:00" "03:00:00" "04:00:00" "05:00:00"
##  [7] "06:00:00" "07:00:00" "08:00:00" "09:00:00" "10:00:00" "11:00:00"
## [13] "12:00:00" "13:00:00" "14:00:00" "15:00:00" "16:00:00" "17:00:00"
## [19] "18:00:00" "19:00:00" "20:00:00" "21:00:00" "22:00:00" "23:00:00"
nlevels(bikes$hour)
## [1] 24
str(bikes)
## 'data.frame':    276798 obs. of  5 variables:
##  $ duration   : num  13.98 11.43 5.25 12.32 20.88 ...
##  $ month      : Factor w/ 12 levels "Jan","Feb","Mar",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ hour       : Factor w/ 24 levels "00:00:00","01:00:00",..: 1 1 1 1 1 1 1 2 2 2 ...
##  $ day_of_week: Factor w/ 7 levels "Friday","Monday",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ user_type  : Factor w/ 3 levels "","Customer",..: 2 3 3 3 2 3 3 3 3 2 ...

Now the printout shows us the levels of both the month and hour columns as well as the different levels there are.