A campus gym has asked for their attendance data to be analyzed. There expectations are actionable insights that would lead to an increase in profits. Before looking at the data a key to a successful result would be to acquire domain knowledge.
library(tidyverse)
library(dplyr)
gym_data <- read_csv("gym.csv")
## Rows: 62184 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (10): number_people, timestamp, day_of_week, is_weekend, is_holiday, te...
## dttm (1): date
##
## ℹ 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(gym_data)
## # A tibble: 6 × 11
## number_people date timestamp day_of_week is_weekend is_holiday
## <dbl> <dttm> <dbl> <dbl> <dbl> <dbl>
## 1 37 2015-08-15 00:00:11 61211 4 0 0
## 2 45 2015-08-15 00:20:14 62414 4 0 0
## 3 40 2015-08-15 00:30:15 63015 4 0 0
## 4 44 2015-08-15 00:40:16 63616 4 0 0
## 5 45 2015-08-15 00:50:17 64217 4 0 0
## 6 46 2015-08-15 01:00:18 64818 4 0 0
## # ℹ 5 more variables: temperature <dbl>, is_start_of_semester <dbl>,
## # is_during_semester <dbl>, month <dbl>, hour <dbl>
view(gym_data)
new_gym_data <- distinct(gym_data)
glimpse(new_gym_data)
## Rows: 62,184
## Columns: 11
## $ number_people <dbl> 37, 45, 40, 44, 45, 46, 43, 53, 54, 43, 39, 38, 4…
## $ date <dttm> 2015-08-15 00:00:11, 2015-08-15 00:20:14, 2015-0…
## $ timestamp <dbl> 61211, 62414, 63015, 63616, 64217, 64818, 66008, …
## $ day_of_week <dbl> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4…
## $ is_weekend <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ is_holiday <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ temperature <dbl> 71.76, 71.76, 71.76, 71.76, 71.76, 72.15, 72.15, …
## $ is_start_of_semester <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ is_during_semester <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ month <dbl> 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8…
## $ hour <dbl> 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 19, 19, 1…
new_gym_data$is_weekend <- as.logical(new_gym_data$is_weekend)
new_gym_data$is_holiday <- as.logical(new_gym_data$is_holiday)
new_gym_data$is_start_of_semester <- as.logical(new_gym_data$is_start_of_semester)
new_gym_data$is_during_semester <- as.logical(new_gym_data$is_during_semester)
glimpse(new_gym_data)
## Rows: 62,184
## Columns: 11
## $ number_people <dbl> 37, 45, 40, 44, 45, 46, 43, 53, 54, 43, 39, 38, 4…
## $ date <dttm> 2015-08-15 00:00:11, 2015-08-15 00:20:14, 2015-0…
## $ timestamp <dbl> 61211, 62414, 63015, 63616, 64217, 64818, 66008, …
## $ day_of_week <dbl> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4…
## $ is_weekend <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
## $ is_holiday <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
## $ temperature <dbl> 71.76, 71.76, 71.76, 71.76, 71.76, 72.15, 72.15, …
## $ is_start_of_semester <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
## $ is_during_semester <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
## $ month <dbl> 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8…
## $ hour <dbl> 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 19, 19, 1…
unique(new_gym_data$day_of_week)
unique(new_gym_data$month)
unique(new_gym_data$hour)
att_month <- (new_gym_data %>% group_by(month) %>% summarise(mean=mean(number_people)))
att_month2<- att_month %>% mutate(month.abb[att_month$month])
colnames(att_month2) = c("month_num", "avg_attendance", "month")
att_month2$month <-factor(att_month2$month,
levels = c("Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"))
head(att_month2)
## # A tibble: 6 × 3
## month_num avg_attendance month
## <dbl> <dbl> <fct>
## 1 1 33.5 Jan
## 2 2 39.2 Feb
## 3 3 33.2 Mar
## 4 4 38.5 Apr
## 5 5 19.8 May
## 6 6 20.3 Jun
att_hour_during <- (new_gym_data %>%
filter(is_during_semester == TRUE) %>%
group_by(hour) %>%
summarise(mean=mean(number_people)))
colnames(att_hour_during) = c("hour","avg_during")
att_hour_break <- (new_gym_data %>%
filter(is_during_semester == FALSE) %>%
group_by(hour) %>%
summarise(mean=mean(number_people)))
colnames(att_hour_break) = c("hour","avg_break")
att_hour <- inner_join(att_hour_during, att_hour_break, by = c("hour"))
head(att_hour)
## # A tibble: 6 × 3
## hour avg_during avg_break
## <dbl> <dbl> <dbl>
## 1 0 18.7 6.96
## 2 1 3.52 0.875
## 3 2 0.235 0.114
## 4 3 0.182 0.103
## 5 4 0.302 0.256
## 6 5 1.13 0.884
att_semester <- (new_gym_data %>% group_by(is_during_semester) %>% summarise(mean=mean(number_people)))
head(att_semester)
## # A tibble: 2 × 2
## is_during_semester mean
## <lgl> <dbl>
## 1 FALSE 18.5
## 2 TRUE 34.5
att_holiday <- (new_gym_data %>% group_by(is_holiday) %>% summarise(mean=mean(number_people)))
head(att_holiday)
## # A tibble: 2 × 2
## is_holiday mean
## <lgl> <dbl>
## 1 FALSE 29.1
## 2 TRUE 7.52
att_week <- (new_gym_data %>% group_by(day_of_week) %>%
summarise(mean=mean(number_people))) %>%
mutate_if(is.numeric, ~round(., 2))
x <- data.frame(wday=rep(0:6, 2))
att_week$day_of_week <- factor(att_week$day_of_week, levels=0:6,
labels=c("Sun", "Mon", "Tues", "Wed",
"Thurs", "Fri", "Sat"))
head(att_week)
## # A tibble: 6 × 2
## day_of_week mean
## <fct> <dbl>
## 1 Sun 32.6
## 2 Mon 32.4
## 3 Tues 32.4
## 4 Wed 30.2
## 5 Thurs 30.0
## 6 Fri 23.0
plot(ggplot(data=att_month2, aes(x=month, y=avg_attendance, group = 1))+
geom_line()+
geom_point()+
labs(title = 'Daily Average Per Month', y = 'attendance'))
As expected attendance drops during the periods between the spring and fall semesters. An option that was conceived is to open the gym to the general public with paid membership fees. Stakeholders however, decided this was not viable for safety reasons.
The percentage of students who commute is 81%. This means a majority of the students who use the facility are still able to travel to the gym during break, but the major drop in attendance reveals a 45% decrease in attendance.
The fall semester (Sep-Dec) saw a progressive decrease all the way into the end of the of the semester which cannot be pinned to just the finals test taking period as attendance is on the decline from the very beginning.
plot(ggplot(att_week, aes(x = day_of_week, y = mean)) +
geom_col() +
geom_text(aes(label = mean), vjust = 1.5, colour = "white")+
labs(title="Gym volume", x="Day of the Week", y="Average"))
As addressed above, knowing when peak times are provides many benefits. This trend shows that students visit the gym the most at the beginning of the week and attendance tapers off towards the end.
matplot(att_hour$hour, cbind(att_hour$avg_during, att_hour$avg_break), type = "l", lty = 1,
col = c("red", "blue"), xlab = "hour",
ylab = "average attendance", main = "Hourly attendance")
During Semester During Break
legend("topleft", legend = c("during semester", "during break"),
cex = 0.75,col = c("red", "blue"),
lty = 1)
The hourly attendance during the break and during the semester trend in similar fashion, with the break trend showing smaller averages as students are away but peaks remain fairly consistent. Hourly attendance for this gym is standard; efforts to look for revenue increase would prove negligible results.