12-23 00:00
Notice
Recent Posts
Recent Comments
관리 메뉴

Scientific Computing & Data Science

[Data Science / Baseball] Lahman 데이터를 이용한 야구 데이터 분석 Part 4. 본문

Data Science/ Baseball Data Analysis

[Data Science / Baseball] Lahman 데이터를 이용한 야구 데이터 분석 Part 4.

cinema4dr12 2017. 5. 18. 21:45

Lahman 데이터를 이용한 야구 데이터 분석 Part 4.

QUESTIONS

Q1. 1980년부터 2016년까지 MLB의 요일별 누적 관중수는 어떻게 될까?


지난 포스팅에 이어 이번 포스팅에서는 1980년부터 2016년까지의 메이저리그 요일별 누적 관중수를 계산하여 그래프로 출력해 보도록 하겠다. 데이터는 Retrosheet의 Game Log 데이터로부터 계산되며, 이 데이터를 MongoDB로 입출력하는 방법에 대하여서는 "온라인 야구 데이터를 MongoDB에 저장하기"를 참고하기 바란다.

패키지 및 소스 로딩하기

그래프 출력 및 그래프 저장을 위해 plotlywebshot 패키지를 로딩한다:

1
2
3
4
5
6
7
8
if (! ("plotly" %in% rownames(installed.packages()))) { install.packages("plotly") }
base::require(plotly)
 
if (! ("webshot" %in% rownames(installed.packages()))) {
  install.packages("webshot")
  webshot::install_phantomjs()
}
base::require(webshot)
cs


또한, MongoDB로부터 Retrosheet의 Game Log 데이터 로딩을 위해 R 소스를 로딩한다:

1
base::source('./ImportCollection.R'echo=FALSE)
cs


참고로 ImportCollection.R 파일은 다음과 같다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
##############################################################################
# ImportCollection.R
##############################################################################
# @Author: Geol Choi, ph.D / cinema4dr12@gmail.com
# @Date: Mar.4,2017
# @Description: To import document collection from MongoDB
##############################################################################
source('./Connect.R'echo=FALSE)
 
## Import data by collection name
ImportCollection <- function(colName) {
  con <- Connect(colName);
  df <- con$find(query = '{}');
  rm(con);
  
  return(df);
}
cs


또한, ImportCollection.RConnect.R 소스를 로딩하며, 이 소스는 다음과 같다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
##############################################################################
# Connect.R
##############################################################################
# @Author: Geol Choi, ph.D / cinema4dr12@gmail.com
# @Date: Mar.4,2017
# @Description: To insert Lahman data into MongoDB
##############################################################################
 
## intall packages & load them
if (! ("mongolite" %in% rownames(installed.packages()))) { install.packages("mongolite") }
base::library(mongolite)
 
################################################################################################
## MongoDB connection
Connect <- function(colName) {
  con <- mongolite::mongo(collection = colName,
                          db = "baseball",
                          url = "mongodb://localhost",
                          verbose = TRUE,
                          options = ssl_options())
  
  return(con)
}
 
################################################################################################
## MongoDB insert
InsertDB <- function(colName, basePath) {
  con <- Connect(colName);
  
  ## drop DB if any
  if(con$count() > 0) con$drop();
  
  ## load data
  fileName <- base::sprintf("%s/%s.csv", basePath, colName);
  df <- utils::read.csv(fileName);
  
  ## insert document as data frame
  con$insert(df);
  
  ## disconnect
  base::rm(con);
}
 
################################################################################################
## MongoDB insert
InsertDataFileToDB <- function(colName, fileName) {
  con <- Connect(colName);
  
  ## drop DB if any
  if(con$count() > 0) con$drop();
  
  ## load data
  df <- utils::read.csv(fileName);
  
  ## insert document as data frame
  con$insert(df);
  
  ## disconnect
  base::rm(con);
}
 
################################################################################################
## MongoDB insert
InsertDataFrameToDB <- function(colName, df) {
  con <- Connect(colName);
  
  ## drop DB if any
  if(con$count() > 0) con$drop();
  
  ## insert document as data frame
  con$insert(df);
  
  ## disconnect
  base::rm(con);
}
cs


요일 별 관중수 계산

요일 별 관중수를 계산할 대상년도는 1980년부터 2016년까지로 하였다.

1
years <- 1980:2016
cs


요일 별 관중수를 저장할 Data Frame 변수 att_weekday(Attendance each weekday)를 정의하여 다음과 같이 초기화하였다:

1
2
3
att_weekday <- base::as.data.frame(base::matrix(0, nrow=7, ncol=2))
base::names(att_weekday) <- base::c("Weekday""Attendance")
att_weekday[,1<- base::c("MON""TUE""WED""THU""FRI""SAT""SUN")
cs


이제 1980년부터 2016년까지 MongoDB로부터 각 년도에 대한 Game Log 데이터를 불러와 처리를 한다:

1
2
3
for(year in years) {
  # 해당 년도에 대한 데이터 처리...
}
cs


이제부터의 내용은 상기 for loop에서 처리하는 코드이다.

MongoDB로부터 해당년도에 대한 Game Log 데이터를 불러온다:

1
2
3
tmp <- base::sprintf("GameLog%4d", year)
GL_YEAR <- ImportCollection(tmp)
utils::head(GL_YEAR)
cs


R의 subset() 함수를 이용하여 요일별로 데이터를 분리하는데, Game Log 데이터의 X3는 해당 게임이 치뤄진 요일이 기록되어 있는 Field이다 (참고로 Field에 대한 정보는 Retrosheet Game Log Guide를 참고하면 된다):

1
2
3
4
5
6
7
GL_MON <- base::subset(x=GL_YEAR, X3=="Mon")
GL_TUE <- base::subset(x=GL_YEAR, X3=="Tue")
GL_WED <- base::subset(x=GL_YEAR, X3=="Wed")
GL_THU <- base::subset(x=GL_YEAR, X3=="Thu")
GL_FRI <- base::subset(x=GL_YEAR, X3=="Fri")
GL_SAT <- base::subset(x=GL_YEAR, X3=="Sat")
GL_SUN <- base::subset(x=GL_YEAR, X3=="Sun")
cs


게임의 관중수는 Game Log 데이터의 X18 필드에 해당하므로 이 필드 값들을 합산하면 해당년도의 각 요일별 관중수를 계산할 수 있다:

1
2
3
4
5
6
7
ATT_MON <- base::sum(GL_MON$X18)
ATT_TUE <- base::sum(GL_TUE$X18)
ATT_WED <- base::sum(GL_WED$X18)
ATT_THU <- base::sum(GL_THU$X18)
ATT_FRI <- base::sum(GL_FRI$X18)
ATT_SAT <- base::sum(GL_SAT$X18)
ATT_SUN <- base::sum(GL_SUN$X18)
cs


앞서 정의한 바와 같이, att_weekday 변수의 행은 모두 7개이며 1, 2, 3, 4, 5, 6, 7은 각각 월, 화, 수, 목, 금, 토, 일요일을 의미하며, 첫번째 열은 요일을, 두번째 열은 관중수를 의미한다. 가령,

> utils::head(att_weekday)
  Weekday Attendance
1     MON          0
2     TUE          0
3     WED          0
4     THU          0
5     FRI          0
6     SAT          0

과 같다.

이제 각 해당년도에 대한 요일별 관중수를 att_weekday에 누적하되, 유효한 값이 아닐 경우 무시한다:

1
2
3
4
5
6
7
if(!is.na(ATT_MON)) att_weekday[1,2<- att_weekday[1,2+ ATT_MON
if(!is.na(ATT_TUE)) att_weekday[2,2<- att_weekday[2,2+ ATT_TUE
if(!is.na(ATT_WED)) att_weekday[3,2<- att_weekday[3,2+ ATT_WED
if(!is.na(ATT_THU)) att_weekday[4,2<- att_weekday[4,2+ ATT_THU
if(!is.na(ATT_FRI)) att_weekday[5,2<- att_weekday[5,2+ ATT_FRI
if(!is.na(ATT_SAT)) att_weekday[6,2<- att_weekday[6,2+ ATT_SAT
if(!is.na(ATT_SUN)) att_weekday[7,2<- att_weekday[7,2+ ATT_SUN
cs


그래프로 출력하기

plotly 패키지를 이용하여 att_weekday 저장된 요일별 관중수를 출력하도록 한다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# plotting with Plotly
<- plotly::plot_ly(data = att_weekday,
                     x = ~Weekday,
                     y = ~Attendance,
                     type = 'scatter',
                     mode = 'lines+markers') %>%
  layout(title = "Total attendance per weekday from 1980 to 2016",
         xaxis = base::list(
           title = "Day of Week",
           type = "category",
           categoryorder = "trace",
           showgrid = TRUE,
           showline = TRUE,
           autorange = TRUE,
           showticklabels = TRUE,
           ticks = "outside",
           tickangle = 0
         ),
         yaxis = base::list(title = "Attendance"))
 
# print results
base::print(p)
cs


출력 결과는 다음과 같다:


예상했던 바와 같이 주말에 관중수가 가장 많으며, 월요일과 목요일에 관중수가 가정 적었음을 알 수 있다.

그러나, 단순히 요일별 누적 관중수보다 해당 요일에 치뤄진 경기수를 고려하여 계산하는 것이 더 의미있을 수 있다. 왜냐하면 아무래도 월요일에는 경기수가 가장 적기 때문이다. 따라서, 해당 요일의 경기 당 평균관중수를 계산해보는 것을 시도해 보기 바란다.

전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#######################################################################
# @Author: Geol Choi, ph.D / cinema4dr12@gmail.com
# @Date: May.18,2017
# @Description: MLB Data Analysis
#######################################################################
 
base::rm(list = ls())
base::gc()
 
if (! ("plotly" %in% rownames(installed.packages()))) { install.packages("plotly") }
base::require(plotly)
 
if (! ("webshot" %in% rownames(installed.packages()))) {
  install.packages("webshot")
  webshot::install_phantomjs()
}
base::require(webshot)
 
base::source('./ImportCollection.R'echo=FALSE)
 
#######################################################################
## Question 3: Attendances each weekday
# How many extra people attend ballgames during the weekend?
# What’s the average attendance by day of the week?
#######################################################################
years <- 1980:2016
att_weekday <- base::as.data.frame(base::matrix(0, nrow=7, ncol=2))
base::names(att_weekday) <- base::c("Weekday""Attendance")
att_weekday[,1<- base::c("MON""TUE""WED""THU""FRI""SAT""SUN")
 
for(year in years) {
  tmp <- base::sprintf("GameLog%4d", year)
  GL_YEAR <- ImportCollection(tmp)
  utils::head(GL_YEAR)
  
  GL_MON <- base::subset(x=GL_YEAR, X3=="Mon")
  GL_TUE <- base::subset(x=GL_YEAR, X3=="Tue")
  GL_WED <- base::subset(x=GL_YEAR, X3=="Wed")
  GL_THU <- base::subset(x=GL_YEAR, X3=="Thu")
  GL_FRI <- base::subset(x=GL_YEAR, X3=="Fri")
  GL_SAT <- base::subset(x=GL_YEAR, X3=="Sat")
  GL_SUN <- base::subset(x=GL_YEAR, X3=="Sun")
  
  ATT_MON <- base::sum(GL_MON$X18)
  ATT_TUE <- base::sum(GL_TUE$X18)
  ATT_WED <- base::sum(GL_WED$X18)
  ATT_THU <- base::sum(GL_THU$X18)
  ATT_FRI <- base::sum(GL_FRI$X18)
  ATT_SAT <- base::sum(GL_SAT$X18)
  ATT_SUN <- base::sum(GL_SUN$X18)
 
  if(!is.na(ATT_MON)) att_weekday[1,2<- att_weekday[1,2+ ATT_MON
  if(!is.na(ATT_TUE)) att_weekday[2,2<- att_weekday[2,2+ ATT_TUE
  if(!is.na(ATT_WED)) att_weekday[3,2<- att_weekday[3,2+ ATT_WED
  if(!is.na(ATT_THU)) att_weekday[4,2<- att_weekday[4,2+ ATT_THU
  if(!is.na(ATT_FRI)) att_weekday[5,2<- att_weekday[5,2+ ATT_FRI
  if(!is.na(ATT_SAT)) att_weekday[6,2<- att_weekday[6,2+ ATT_SAT
  if(!is.na(ATT_SUN)) att_weekday[7,2<- att_weekday[7,2+ ATT_SUN
}
 
# plotting with Plotly
<- plotly::plot_ly(data = att_weekday,
                     x = ~Weekday,
                     y = ~Attendance,
                     type = 'scatter',
                     mode = 'lines+markers') %>%
  layout(title = "Total attendance per weekday from 1980 to 2016",
         xaxis = base::list(
           title = "Day of Week",
           type = "category",
           categoryorder = "trace",
           showgrid = TRUE,
           showline = TRUE,
           autorange = TRUE,
           showticklabels = TRUE,
           ticks = "outside",
           tickangle = 0
         ),
         yaxis = base::list(title = "Attendance"))
 
# print results
base::print(p)
 
cs


Comments