Tidying, Visualising and Summarising Data

FMB819: R을 이용한 데이터분석

고려대학교 경영대학 정지웅

Working With Data

  • 2014년 뉴욕타임즈 기사에 따르면, “데이터 과학자들은 데이터를 분석하기 전에 50%에서 80%의 시간을 데이터 수집 및 정리에 소비한다.”라고 함.

  • 본 강의에서는 데이터 정리(Tidying), 시각화(Visualising), 요약(Summarising)의 기본을 배움.

Tidying Data

dplyr 소개

  • dplyrtidyverse 패키지군의 핵심 데이터 조작 패키지.
  • data.table도 대안으로 사용 가능하며, 거대 용량 데이터 처리에 최적화되어 있음.
  • 본 강의에서는 문법이 직관적이고 가독성이 높은 dplyr을 다룸.

dplyr 개요

  • dplyr은 직관적인 영어 동사(verbs) 중심으로 구성되어 있음.
  • data.frame 또는 tibble 형식의 데이터를 주로 다룸.

\[\text{verb}(\underbrace{\text{data.frame}}_{\text{1st argument}}, \underbrace{\text{what to do}}_\text{2nd argument})\]

  • 파이프 연산자 %>% (magrittr 패키지) 또는 R 4.1.0 이상 기본 |> 를 이용하면 코드를 물 흐르듯 작성 가능 (Ctrl+Shift+M):

\[\underbrace{\text{data.frame}}_{\text{1st argument}} \underbrace{\text{ %>% }}_{\text{"pipe" operator}} \text{verb}(\underbrace{\text{what to do}}_\text{2nd argument})\]

주요 dplyr verbs

  1. filter(): 특정 조건을 만족하는 행(Row) 선택
  2. arrange(): 행 정렬 (오름차순/내림차순)
  3. select(): 특정 열(Column) 선택
  4. mutate(): 기존 열을 연산하여 새 변수(열) 생성
  5. summarise(): 데이터 요약 통계 계산
  6. group_by(): 범주형 변수를 기준으로 그룹별 연산 수행

예제 데이터: 2016년 미국 대선 여론조사 (dslabs 패키지)

  • 2016년 미국 대선과 관련된 여론조사 데이터를 포함
if (!require("dslabs")) install.packages("dslabs")
if (!require("tidyverse")) install.packages("tidyverse")
data(polls_us_election_2016, package = "dslabs")
# 데이터를 'tibble' 형식으로 변환
polls_us_election_2016 <- as_tibble(polls_us_election_2016) 
# 데이터의 첫 6개의 행과 첫 6개의 열을 출력
head(polls_us_election_2016[,1:6]) 
## # A tibble: 6 × 6
##   state startdate  enddate    pollster                          grade samplesize
##   <fct> <date>     <date>     <fct>                             <fct>      <int>
## 1 U.S.  2016-11-03 2016-11-06 ABC News/Washington Post          A+          2220
## 2 U.S.  2016-11-01 2016-11-07 Google Consumer Surveys           B          26574
## 3 U.S.  2016-11-02 2016-11-06 Ipsos                             A-          2195
## 4 U.S.  2016-11-04 2016-11-07 YouGov                            B           3677
## 5 U.S.  2016-11-03 2016-11-06 Gravis Marketing                  B-         16639
## 6 U.S.  2016-11-03 2016-11-06 Fox News/Anderson Robbins Resear… A           1295
  • tibble은 data.frame의 개선된 버전으로, 더 깔끔한 출력과 편리한 기능을 제공.

예제 데이터: 구조 확인

  • 이 데이터셋에 어떤 변수가 포함되어 있는지 확인
str(polls_us_election_2016) # 데이터의 구조를 요약하여 출력
## tibble [4,208 × 15] (S3: tbl_df/tbl/data.frame)
##  $ state           : Factor w/ 57 levels "Alabama","Alaska",..: 50 50 50 50 50 50 50 50 37 50 ...
##  $ startdate       : Date[1:4208], format: "2016-11-03" "2016-11-01" ...
##  $ enddate         : Date[1:4208], format: "2016-11-06" "2016-11-07" ...
##  $ pollster        : Factor w/ 196 levels "ABC News/Washington Post",..: 1 63 81 194 65 55 18 113 195 76 ...
##  $ grade           : Factor w/ 10 levels "D","C-","C","C+",..: 10 6 8 6 5 9 8 8 NA 8 ...
##  $ samplesize      : int [1:4208] 2220 26574 2195 3677 16639 1295 1426 1282 8439 1107 ...
##  $ population      : chr [1:4208] "lv" "lv" "lv" "lv" ...
##  $ rawpoll_clinton : num [1:4208] 47 38 42 45 47 ...
##  $ rawpoll_trump   : num [1:4208] 43 35.7 39 41 43 ...
##  $ rawpoll_johnson : num [1:4208] 4 5.46 6 5 3 3 5 6 6 7.1 ...
##  $ rawpoll_mcmullin: num [1:4208] NA NA NA NA NA NA NA NA NA NA ...
##  $ adjpoll_clinton : num [1:4208] 45.2 43.3 42 45.7 46.8 ...
##  $ adjpoll_trump   : num [1:4208] 41.7 41.2 38.8 40.9 42.3 ...
##  $ adjpoll_johnson : num [1:4208] 4.63 5.18 6.84 6.07 3.73 ...
##  $ adjpoll_mcmullin: num [1:4208] NA NA NA NA NA NA NA NA NA NA ...

dplyr: filter()

# 표본 크기(samplesize)가 2000보다 큰 행만 선택, 츨력
polls_us_election_2016 %>% 
  filter(samplesize > 2000)
## # A tibble: 403 × 15
##    state      startdate  enddate    pollster         grade samplesize population
##    <fct>      <date>     <date>     <fct>            <fct>      <int> <chr>     
##  1 U.S.       2016-11-03 2016-11-06 ABC News/Washin… A+          2220 lv        
##  2 U.S.       2016-11-01 2016-11-07 Google Consumer… B          26574 lv        
##  3 U.S.       2016-11-02 2016-11-06 Ipsos            A-          2195 lv        
##  4 U.S.       2016-11-04 2016-11-07 YouGov           B           3677 lv        
##  5 U.S.       2016-11-03 2016-11-06 Gravis Marketing B-         16639 rv        
##  6 New Mexico 2016-11-06 2016-11-06 Zia Poll         <NA>        8439 lv        
##  7 U.S.       2016-11-05 2016-11-07 The Times-Picay… <NA>        2521 lv        
##  8 U.S.       2016-11-01 2016-11-07 USC Dornsife/LA… <NA>        2972 lv        
##  9 Georgia    2016-11-03 2016-11-06 Gravis Marketing B-          2002 rv        
## 10 Virginia   2016-11-01 2016-11-02 Remington        <NA>        3076 lv        
## # ℹ 393 more rows
## # ℹ 8 more variables: rawpoll_clinton <dbl>, rawpoll_trump <dbl>,
## #   rawpoll_johnson <dbl>, rawpoll_mcmullin <dbl>, adjpoll_clinton <dbl>,
## #   adjpoll_trump <dbl>, adjpoll_johnson <dbl>, adjpoll_mcmullin <dbl>

dplyr: filter() 의 연산자들

기본적인 비교 연산자:

  • >: 크다 (greater than) / <: 작다 (smaller than)
  • >=: 크거나 같다 / <=: 작거나 같다
  • !=: 같지 않다 (not equal to)
  • ==: 같다 (equal to) - 주의! = 가 아니라 ==

논리 연산자:

  1. x & y: x 그리고 y (AND: 둘 다 참일 때만 참)
  2. x | y: x 또는 y (OR: 둘 중 하나라도 참이면 참)
  3. !y: y가 아닐 때 (NOT: 논리 반전)

dplyr: filter()%in%

  • %in% 연산자: xy 벡터 안에 포함되어 있는지 확인
  • !(x %in% y): 반대로 포함되지 않은 것만 필터링
# 3이 1부터 3까지의 숫자 중 하나인지 확인 (TRUE)
3 %in% 1:3  
## [1] TRUE
# 벡터화된 연산: 2와 5가 2부터 10 사이의 숫자 중 하나인지 확인
c(2,5) %in% 2:10    
## [1] TRUE TRUE

dplyr: filter() 복합 조건

A 등급을 받은 여론조사 중 표본 크기가 2,000명 이상이고, 트럼프가 최소 45%의 지지를 받은 여론조사는?

# polls_us_election_2016 데이터에서 
# 1. 등급(grade)이 "A"이고
# 2. 표본 크기(samplesize)가 2000명보다 크며
# 3. 트럼프의 여론조사 지지율(rawpoll_trump)이 45% 이상인 행을 필터링.
polls_us_election_2016 %>%
  filter(grade == "A" & samplesize > 2000 & rawpoll_trump > 45) 
## # A tibble: 1 × 15
##   state   startdate  enddate    pollster       grade samplesize population
##   <fct>   <date>     <date>     <fct>          <fct>      <int> <chr>     
## 1 Indiana 2016-04-26 2016-04-28 Marist College A           2149 rv        
## # ℹ 8 more variables: rawpoll_clinton <dbl>, rawpoll_trump <dbl>,
## #   rawpoll_johnson <dbl>, rawpoll_mcmullin <dbl>, adjpoll_clinton <dbl>,
## #   adjpoll_trump <dbl>, adjpoll_johnson <dbl>, adjpoll_mcmullin <dbl>

dplyr: mutate()

새로운 변수(열)를 만들 때 사용. 1. 각 여론조사에서 트럼프와 클린턴의 지지율 합계 2. 트럼프의 원래(raw) 지지율과 조정(adjusted) 지지율 간의 차이

polls_us_election_2016 %>%
  mutate(trump_clinton_tot = rawpoll_trump + rawpoll_clinton, # 두 후보 지지율 합계
         trump_raw_adj_diff = rawpoll_trump - adjpoll_trump) %>% # 원래 지지율과 조정 지지율 차이
  names() # 새롭게 생성된 변수명 확인
##  [1] "state"              "startdate"          "enddate"           
##  [4] "pollster"           "grade"              "samplesize"        
##  [7] "population"         "rawpoll_clinton"    "rawpoll_trump"     
## [10] "rawpoll_johnson"    "rawpoll_mcmullin"   "adjpoll_clinton"   
## [13] "adjpoll_trump"      "adjpoll_johnson"    "adjpoll_mcmullin"  
## [16] "trump_clinton_tot"  "trump_raw_adj_diff"

dplyr: select()

  • 분석에 필요한 특정 열(변수)만 골라낼 때 사용. 데이터의 용량을 줄이고 직관성을 높임.
# select() 함수를 사용하여 특정 열만 선택.
# 1. state: 조사된 주(State)
# 2. startdate: 여론조사가 시작된 날짜
# 3. enddate: 여론조사가 종료된 날짜
# 4. pollster: 여론조사 기관
# 5. rawpoll_clinton: 힐러리 클린턴의 원래(raw) 지지율
# 6. rawpoll_trump: 도널드 트럼프의 원래(raw) 지지율

polls_us_election_2016 %>%
  select(state, startdate, enddate, pollster, rawpoll_clinton, rawpoll_trump) %>% 
  names() # 데이터 프레임의 변수명을 출력하여 올바르게 선택되었는지 확인
## [1] "state"           "startdate"       "enddate"         "pollster"       
## [5] "rawpoll_clinton" "rawpoll_trump"

dplyr: summarise()

  • 원하는 통계량으로 데이터를 요약 (평균, 최대값, 최소값 등)
  • 트럼프의 최대 지지율은 얼마인가?
# summarise() 함수를 사용하여 트럼프의 최대(raw) 지지율을 계산
# max() 함수는 주어진 열에서 가장 큰 값을 반환
polls_us_election_2016 %>%
  summarise(max_trump = max(rawpoll_trump)) 
## # A tibble: 1 × 1
##   max_trump
##       <dbl>
## 1        68

dplyr: group_by()

  • group_by() 단독으로는 데이터에 눈에 띄는 변화가 없지만, summarise()mutate()와 결합하면 강력해짐.
  • 여론조사 등급(grade)별 클린턴의 평균 지지율은 얼마인가?
# group_by()를 사용하여 여론조사 등급(grade)별로 그룹을 나눔.
# summarise()를 사용하여 각 그룹에서 rawpoll_clinton(클린턴의 원래 지지율)의 평균 계산

polls_us_election_2016 %>%
  group_by(grade) %>% # 여론조사 등급별 그룹화
  summarise(mean_vote_clinton = mean(rawpoll_clinton, na.rm = TRUE)) # 각 그룹에서 클린턴의 평균 지지율 계산
## # A tibble: 11 × 2
##    grade mean_vote_clinton
##    <fct>             <dbl>
##  1 D                  46.7
##  2 C-                 43.2
##  3 C                  41.8
##  4 C+                 44.2
##  5 B-                 43.9
##  6 B                  37.3
##  7 B+                 44.1
##  8 A-                 43.0
##  9 A                  45.3
## 10 A+                 45.8
## 11 <NA>               43.2

명령어 연결하기

polls_us_election_2016
## # A tibble: 4,208 × 15
##    state      startdate  enddate    pollster         grade samplesize population
##    <fct>      <date>     <date>     <fct>            <fct>      <int> <chr>     
##  1 U.S.       2016-11-03 2016-11-06 ABC News/Washin… A+          2220 lv        
##  2 U.S.       2016-11-01 2016-11-07 Google Consumer… B          26574 lv        
##  3 U.S.       2016-11-02 2016-11-06 Ipsos            A-          2195 lv        
##  4 U.S.       2016-11-04 2016-11-07 YouGov           B           3677 lv        
##  5 U.S.       2016-11-03 2016-11-06 Gravis Marketing B-         16639 rv        
##  6 U.S.       2016-11-03 2016-11-06 Fox News/Anders… A           1295 lv        
##  7 U.S.       2016-11-02 2016-11-06 CBS News/New Yo… A-          1426 lv        
##  8 U.S.       2016-11-03 2016-11-05 NBC News/Wall S… A-          1282 lv        
##  9 New Mexico 2016-11-06 2016-11-06 Zia Poll         <NA>        8439 lv        
## 10 U.S.       2016-11-04 2016-11-07 IBD/TIPP         A-          1107 lv        
## # ℹ 4,198 more rows
## # ℹ 8 more variables: rawpoll_clinton <dbl>, rawpoll_trump <dbl>,
## #   rawpoll_johnson <dbl>, rawpoll_mcmullin <dbl>, adjpoll_clinton <dbl>,
## #   adjpoll_trump <dbl>, adjpoll_johnson <dbl>, adjpoll_mcmullin <dbl>

명령어 연결하기

polls_us_election_2016 %>%
    mutate(trump_clinton_diff = 
             rawpoll_trump - 
             rawpoll_clinton) 
## # A tibble: 4,208 × 16
##    state      startdate  enddate    pollster         grade samplesize population
##    <fct>      <date>     <date>     <fct>            <fct>      <int> <chr>     
##  1 U.S.       2016-11-03 2016-11-06 ABC News/Washin… A+          2220 lv        
##  2 U.S.       2016-11-01 2016-11-07 Google Consumer… B          26574 lv        
##  3 U.S.       2016-11-02 2016-11-06 Ipsos            A-          2195 lv        
##  4 U.S.       2016-11-04 2016-11-07 YouGov           B           3677 lv        
##  5 U.S.       2016-11-03 2016-11-06 Gravis Marketing B-         16639 rv        
##  6 U.S.       2016-11-03 2016-11-06 Fox News/Anders… A           1295 lv        
##  7 U.S.       2016-11-02 2016-11-06 CBS News/New Yo… A-          1426 lv        
##  8 U.S.       2016-11-03 2016-11-05 NBC News/Wall S… A-          1282 lv        
##  9 New Mexico 2016-11-06 2016-11-06 Zia Poll         <NA>        8439 lv        
## 10 U.S.       2016-11-04 2016-11-07 IBD/TIPP         A-          1107 lv        
## # ℹ 4,198 more rows
## # ℹ 9 more variables: rawpoll_clinton <dbl>, rawpoll_trump <dbl>,
## #   rawpoll_johnson <dbl>, rawpoll_mcmullin <dbl>, adjpoll_clinton <dbl>,
## #   adjpoll_trump <dbl>, adjpoll_johnson <dbl>, adjpoll_mcmullin <dbl>,
## #   trump_clinton_diff <dbl>

명령어 연결하기

polls_us_election_2016 %>%
    mutate(trump_clinton_diff = 
             rawpoll_trump - 
             rawpoll_clinton) %>%
    filter(trump_clinton_diff>5 &
           state == "Iowa" &
           is.na(rawpoll_johnson))
## # A tibble: 3 × 16
##   state startdate  enddate    pollster grade samplesize population
##   <fct> <date>     <date>     <fct>    <fct>      <int> <chr>     
## 1 Iowa  2016-09-09 2016-09-29 Ipsos    A-           343 lv        
## 2 Iowa  2016-09-02 2016-09-22 Ipsos    A-           344 lv        
## 3 Iowa  2016-08-26 2016-09-15 Ipsos    A-           347 lv        
## # ℹ 9 more variables: rawpoll_clinton <dbl>, rawpoll_trump <dbl>,
## #   rawpoll_johnson <dbl>, rawpoll_mcmullin <dbl>, adjpoll_clinton <dbl>,
## #   adjpoll_trump <dbl>, adjpoll_johnson <dbl>, adjpoll_mcmullin <dbl>,
## #   trump_clinton_diff <dbl>

명령어 연결하기

polls_us_election_2016 %>%
    mutate(trump_clinton_diff = 
             rawpoll_trump - 
             rawpoll_clinton) %>%
    filter(trump_clinton_diff>5 &
           state == "Iowa" &
           is.na(rawpoll_johnson)) %>%
    select(pollster) # pollster(여론조사 기관) 열만 선택
## # A tibble: 3 × 1
##   pollster
##   <fct>   
## 1 Ipsos   
## 2 Ipsos   
## 3 Ipsos

명령어 연결하기

polls_us_election_2016 %>%
    mutate(trump_clinton_diff = 
             rawpoll_trump - 
             rawpoll_clinton) %>%
    filter(trump_clinton_diff>5 &
           state == "Iowa" &
           is.na(rawpoll_johnson)) %>%
    pull(pollster) # pollster(여론조사 기관) 열의 값을 추출하여 벡터로 반환
## [1] Ipsos Ipsos Ipsos
## 196 Levels: ABC News/Washington Post ... Zogby Interactive/JZ Analytics

결측값(NA) 처리

  • 값이 누락(missing) 되었을 경우 NA로 표시됨.
x <- NA  # x에 결측값(NA) 할당
  • NA가 포함된 연산을 수행하면, 결과도 NA가 됨.
NA > 5    # NA는 숫자가 아니므로 비교 불가능 -> NA 반환
## [1] NA
NA + 10   # NA와 연산하면 결과도 NA
## [1] NA
  • is.na(x) 함수를 사용하면 값이 NA인지 확인 가능.
is.na(x)  # x가 NA인지 확인 -> TRUE 반환
## [1] TRUE
  • NA 값은 서로 비교할 수 없음.
NA == NA  # NA끼리 비교하면 결과도 NA
## [1] NA
  • 예제
# x는 Mary의 나이, 정확한 나이를 모름.
x <- NA

# y는 John의 나이, 정확한 나이를 모름.
y <- NA

# John과 Mary의 나이가 같은가?
x == y
## [1] NA
  • NA == NATRUE가 아니라 NA를 반환하는데, 이는 두 값이 같은지 여부를 판단할 수 없기 때문임.

Task 1

다음 코드를 실행하여 데이터를 로드합니다.

library(dslabs)
data(polls_us_election_2016)
  1. grade 값이 결측치(NA)인 여론조사는? head를 사용하여 확인.

  2. 다음 조건을 모두 만족하는 여론조사는?

  1. American Strategies, GfK Group, Merrill Poll에서 조사한 경우,
  2. 표본 크기가 1,000명 이상인 경우,
  3. 2016년 10월 20일에 시작된 경우.

힌트: (i) 조건에서는 %in% 연산자가 유용, 벡터는 c()함수로 만들 수 있음. (iii)에서는 날짜 변수의 형식을 확인 “yyyy-mm-dd”

  1. 다음 조건을 모두 만족하는 여론조사는?
  1. Johnson 후보의 여론조사 데이터가 누락되지 않은 경우,
  2. 트럼프와 클린턴의 원본 여론조사 지지율 합이 95%를 초과하는 경우,
  3. 오하이오(OH) 주에서 실시된 경우

힌트: 트럼프와 클린턴의 지지율 합계를 계산하는 새로운 변수를 생성한 후 filter() 를 적용

  1. 표본 크기가 2,000명 이상인 여론조사에서 트럼프의 평균 지지율이 가장 높은 주는?

힌트: filter(), group_by(), summarise(), arrange() 사용. 내림차순 정렬하려면 arrange() 함수 사용.

주가 자료 다루기

if (!require("tidyquant")) install.packages("tidyquant")
library(tidyquant)

# 1. Apple(AAPL)과 Microsoft(MSFT) 주가 데이터 수집 (2020년 이후)
tech_stocks <- tq_get(c("AAPL", "MSFT"), from="2020-01-01", to=Sys.Date())

# 2. 일별 수익률(Daily Return) 계산하기
tech_returns <- tech_stocks %>% 
  group_by(symbol) %>%         # 종목별로 그룹화 (중요!)
  arrange(date) %>%            # 날짜순 정렬
  mutate(
    # 어제 종가 대비 오늘 종가의 수익률 계산. lag() 함수는 '이전 행'의 값을 가져옴
    daily_return = adjusted / lag(adjusted) - 1 
  ) %>%
  drop_na() # lag() 연산으로 인해 발생하는 첫 날의 NA값 제거

head(tech_returns %>% select(symbol, date, adjusted, daily_return))
## # A tibble: 6 × 4
## # Groups:   symbol [2]
##   symbol date       adjusted daily_return
##   <chr>  <date>        <dbl>        <dbl>
## 1 AAPL   2020-01-03     71.7     -0.00972
## 2 MSFT   2020-01-03    150.      -0.0125 
## 3 AAPL   2020-01-06     72.3      0.00797
## 4 MSFT   2020-01-06    151.       0.00258
## 5 AAPL   2020-01-07     71.9     -0.00470
## 6 MSFT   2020-01-07    149.      -0.00912

Visualising Data

기본 R 그래프와 ggplot2

  • ggplot2 (이 패키지는 tidyverse의 일부) 기본 R의 그래프 기능은 보다 유연함

  • 데이터를 시각적 요소(색상, 형태, 크기 등)에 ’매핑(Mapping)’한다는 철학(Grammar of Graphics)을 가짐.

  • 예제를 실행하기 위해 gapminder 데이터셋 사용.

gapminder 개요

  • 먼저 gapminder 데이터를 로드:
library(dslabs) # dslabs 패키지 로드
data(gapminder, package = "dslabs") # gapminder 데이터셋 불러오기
  • 데이터의 처음 3개 행과 마지막 2개 행을 확인.
head(gapminder, n = 3) # 데이터의 처음 3행 출력
##   country year infant_mortality life_expectancy fertility population
## 1 Albania 1960            115.4           62.87      6.19    1636054
## 2 Algeria 1960            148.2           47.50      7.65   11124892
## 3  Angola 1960            208.0           35.98      7.32    5270844
##           gdp continent          region
## 1          NA    Europe Southern Europe
## 2 13828152297    Africa Northern Africa
## 3          NA    Africa   Middle Africa
tail(gapminder, n = 2) # 데이터의 마지막 2행 출력
##        country year infant_mortality life_expectancy fertility population gdp
## 10544   Zambia 2016               NA           57.10        NA         NA  NA
## 10545 Zimbabwe 2016               NA           61.69        NA         NA  NA
##       continent         region
## 10544    Africa Eastern Africa
## 10545    Africa Eastern Africa

Task 2

다음 코드를 실행하여 데이터를 로드.

  1. 대륙(continent)/연도(year)별 평균 인구(변수명: mean_pop)를 계산하고, 결과를 새로운 객체 gapminder_mean에 저장하시오. 힌트: 두 개의 기준으로 그룹화해야 하므로 group_by(continent, year)를 사용하고 summarise를 적용.

gg = Grammar of Graphics

Data

data %>%
  ggplot()

or

ggplot(data)
  • Tidy Data

    • 각 변수는 열(column)을 형성

    • 각 관측치는 행(row)을 형성

  • 시각화를 시작할 때 고려 사항

    • 시각화에서 어떤 정보를 사용할 것인가?

    • 해당 데이터가 하나의 열 또는 행에 포함되어 있는가?

gg = Grammar of Graphics

Data

Aesthetics

+ aes()

데이터를 시각적 요소 또는 매개변수에 매핑

  • year (연도)

  • population (인구)

  • country (국가)

gg = Grammar of Graphics

Data

Aesthetics

+ aes()

데이터를 시각적 요소 또는 매개변수에 매핑

  • year (연도) → x

  • population (인구) → y

  • country (국가) → shape, color, etc.

gg = Grammar of Graphics

Data

Aesthetics

+ aes()
aes(
  x = year,
  y = population,
  color = country
)

gg = Grammar of Graphics

Data

Aesthetics

Geoms

+ geom_*()

Geometric objects:

gg = Grammar of Graphics

Data

Aesthetics

Geoms

+ geom_*()

링크: 많이 사용되는 geoms

유형 함수
포인트 geom_point()
geom_line()
막대 그래프 geom_bar(), geom_col()
히스토그램 geom_histogram()
회귀선 geom_smooth()
박스플롯 geom_boxplot()
텍스트 geom_text()
수직/수평선 geom_{vh}line()
개수 세기 geom_count()
밀도 그래프 geom_density()

gg = Grammar of Graphics

Data

Aesthetics

Geoms

+ geom_*()

RStudio에서 geom_을 입력하면 자동 완성 기능을 통해 사용할 수 있는 모든 옵션 확인 가능

(Y)Our first plot!

gapminder_mean
## # A tibble: 285 × 3
## # Groups:   continent [5]
##    continent  year mean_pop
##    <fct>     <int>    <dbl>
##  1 Africa     1960 5464985.
##  2 Africa     1961 5598112.
##  3 Africa     1962 5736073.
##  4 Africa     1963 5878867.
##  5 Africa     1964 6026474.
##  6 Africa     1965 6178906.
##  7 Africa     1966 6336258.
##  8 Africa     1967 6498656.
##  9 Africa     1968 6666202.
## 10 Africa     1969 6839011.
## # ℹ 275 more rows

(Y)Our first plot!

gapminder_mean %>%
  ggplot() 

(Y)Our first plot!

gapminder_mean %>%
  ggplot() +
  aes(x = year, #<<
      y = mean_pop) #<<

(Y)Our first plot!

gapminder_mean %>%
  ggplot() +
  aes(x = year,
      y = mean_pop) +
  geom_point() #<<

(Y)Our first plot!

gapminder_mean %>%
  ggplot() +
  aes(x = year,
      y = mean_pop,
      color = continent) + #<<
  geom_point()

(Y)Our first plot!

gapminder_mean %>%
  ggplot() +
  aes(x = year,
      y = mean_pop,
      color = continent) +
  geom_point() +
  geom_line() #<<

(Y)Our first plot!

gapminder_mean %>%
  ggplot() +
  aes(x = year,
      y = mean_pop,
      color = continent) +
  # geom_point() + #<<
  geom_line()

(Y)Our first plot!

g = gapminder_mean %>% #<<
  ggplot() +
  aes(x = year,
      y = mean_pop,
      color = continent) +
  # geom_point() + 
  geom_line()
g   #<<
# graphs can be saved as
# objects!

gg = Grammar of Graphics

Data

Aesthetics

Geoms

Facet

+ facet_wrap()      # 여러 개의 작은 그래프로 분할 (자동 배치)
+ facet_grid()      # 행과 열로 그래프를 체계적으로 배열

gg = Grammar of Graphics

Data

Aesthetics

Geoms

Facet

+ facet_wrap() 
+ facet_grid()
g + facet_wrap(~ continent)

gg = Grammar of Graphics

Data

Aesthetics

Geoms

Facet

+ facet_wrap() 
+ facet_grid()
g + facet_grid(~ continent)

gg = Grammar of Graphics

Data

Aesthetics

Geoms

Facet

Labels

+ labs()
g + labs(x = "Year", y = "Average Population", color = "Continent")

gg = Grammar of Graphics

Data

Aesthetics

Geoms

Facet

Labels

Scales

+ scale_*_*()

scale + _ + <aes> + _ + <type> + ()

어떤 매개변수를 조정하고 싶은가? → <aes>

그 매개변수의 유형은 무엇인가? → <type>

  • 이산형(x축) 조정
    scale_x_discrete()
  • 연속형 변수에서 포인트 크기 조정
    scale_size_continuous()
  • y축을 로그 스케일로 변환
    scale_y_log10()
  • 색상 세팅 변환
    scale_fill_discrete()
    scale_color_manual()

gg = Grammar of Graphics

Data

Aesthetics

Geoms

Facet

Labels

Scales

+ scale_*_*()
g + scale_color_viridis_d()

gg = Grammar of Graphics

Data

Aesthetics

Geoms

Facet

Labels

Scales

+ scale_*_*()
g + scale_y_log10()

gg = Grammar of Graphics

Data

Aesthetics

Geoms

Facet

Labels

Scales

+ scale_*_*()
g + scale_x_continuous(breaks = seq(1950, 2020, 10))

ggplot 심화 학습

Task 3

gapminder 데이터를 사용하여 다음의 그래프를 ggplot2로 생성하시오

  1. 2015년 기대수명(Life Expectancy)의 히스토그램을 작성하시오.

    힌트: 히스토그램을 만들 때 aes()에 y 값을 지정해야 할까? geom_* 내에서 다음 옵션을 설정하시오: binwidth = 5, boundary = 45, colour = “white”, fill = “#d90502”. x축은 “기대 수명”, y축은 “빈도”로 레이블 하시오.

    Optional: 생성한 히스토그램을 대륙(Continent)별로 나누어 (facet_grid()) 각 대륙이 새로운 행(row)에 표시되도록 만드시오. 힌트: facet_grid(rows = vars(continent))

  2. 연도/대륙별 평균 기대수명을 구하고 대륙별로 평균 기대수명에 대한 Boxplot 으로 그리시오. geom_* 옵션: colour = “black”, fill = “#d90502”.

    힌트: continent 및 year 두 개의 변수로 그룹화해야함.

  3. 2015년 출산율(Fertility Rate, y축)과 영아 사망률(Infant Mortality, x축) 간의 관계를 나타내는 산점도 (Scatter Plot)를 작성하시오. geom_* 옵션: size = 3, alpha = 0.5, colour = “#d90502”. labs()를 사용하여 레이블을 추가 (x = “영아 사망률”, y = “출산율”)

주가 자료 다루기

# 1. ggplot2 정적 차트 (보고서/논문용)
library(ggthemes)
tech_returns %>% 
  ggplot(aes(x = date, y = adjusted, color = symbol)) +
  geom_line(linewidth = 1) +
  labs(title="Tech Stocks Price Trend", x="Date", y="Adjusted Close") +
  theme_economist() # 이코노미스트 잡지 스타일 테마

주가 자료 다루기

# 2. highcharter 동적 차트 (웹/대시보드용)
if (!require("highcharter")) install.packages("highcharter")
library(highcharter)

# 애플 주가와 거래량을 하나의 인터랙티브 차트로 표현
apple_only <- tech_stocks %>% filter(symbol == "AAPL")

highchart(type = "stock") %>% 
  hc_add_series(apple_only, type = "line", hcaes(x = date, y = adjusted), name = "AAPL Price") %>% 
  hc_add_series(apple_only, type = "column", hcaes(x = date, y = volume), name = "Volume", yAxis = 1) %>% 
  hc_yAxis_multiples(
    list(title = list(text = "Stock Price")),
    list(title = list(text = "Trading Volume"), opposite = TRUE)
  ) %>% 
  hc_title(text = "AAPL Stock Price and Volume")

Summarising

Summarising Data

  • 시각화를 통해 큰 흐름을 파악했다면, 구체적인 수치로 데이터를 요약할 차례.
  • 요약 통계(summary statistics): 대규모의 데이터를 몇 개의 핵심 숫자로 압축.
  • 특히, 중심 경향(central tendency)산포도/변동성(spread)를 중점적으로 다룸.

Central Tendency

  • 평균 (Mean)

mean(x): x의 모든 값의 평균을 계산. \[\bar{x} = \frac{1}{N}\sum_{i=1}^N x_i\]

x <- c(1,2,2,2,2,100)  # 데이터 샘플
mean(x)  # 평균 계산
## [1] 18.16667
mean(x) == sum(x) / length(x)  # 평균 공식 확인
## [1] TRUE
  • 중앙값 (Median)

median(x): x의 값들 중 50%가 해당 값보다 작거나 같고, 50%가 크거나 같은 값을 찾음. \(m\)이 중앙값이라면: \[\Pr(X \leq m) \geq 0.5 \text{ 그리고 } \Pr(X \geq m) \geq 0.5\]

중앙값은 이상치(outliers)에 강건(robust).

median(x, echo=T)  # 중앙값 계산
## [1] 2

Spread

중심(평균)에서 얼마나 퍼져 있는지를 측정

분산(Variance)은 이러한 분포의 척도 중 하나

\[Var(X) = \frac{1}{N} \sum_{i=1}^N(x_i-\bar{x})^2\]

평균이 0인 두 개의 정규 분포를 비교해 보자.

분산 계산:

var(x)

Tabulating Data

  • table(x) 함수는 x 내 각 고유 값의 발생 횟수를 계산하는 데 유용.
table(gapminder$continent)
## 
##   Africa Americas     Asia   Europe  Oceania 
##     2907     2052     2679     2223      684
  • 동일한 작업을 dplyr의 count 함수를 사용하여 수행할 수도 있음.
gapminder %>% count(continent)
##   continent    n
## 1    Africa 2907
## 2  Americas 2052
## 3      Asia 2679
## 4    Europe 2223
## 5   Oceania  684

Tabulating Data

  • Contingency Table 생성
gapminder_new <- gapminder %>%
  filter(year == 2015) %>%
  mutate(fertility_above_2 = (fertility > 2)) # dummy variable for fertility rate above replacement rate
table(gapminder_new$fertility_above_2)
## 
## FALSE  TRUE 
##    73   111
table(gapminder_new$fertility_above_2,gapminder_new$continent)
##        
##         Africa Americas Asia Europe Oceania
##   FALSE      2       13   19     38       1
##   TRUE      49       22   28      1      11
  • prop.table을 사용하여 비율을 계산 가능:
# proportions by row
prop.table(table(gapminder_new$fertility_above_2,gapminder_new$continent), margin = 1)
##        
##              Africa    Americas        Asia      Europe     Oceania
##   FALSE 0.027397260 0.178082192 0.260273973 0.520547945 0.013698630
##   TRUE  0.441441441 0.198198198 0.252252252 0.009009009 0.099099099
# proportions by column
prop.table(table(gapminder_new$fertility_above_2,gapminder_new$continent), margin = 2) 
##        
##             Africa   Americas       Asia     Europe    Oceania
##   FALSE 0.03921569 0.37142857 0.40425532 0.97435897 0.08333333
##   TRUE  0.96078431 0.62857143 0.59574468 0.02564103 0.91666667
  • NA 값을 포함한 table 또는 crosstable을 얻으려면 useNA = "always" 또는 useNA = "ifany" 옵션을 사용

Tabulating Data

  • count 함수를 사용한 데이터 요약
gapminder_new %>%
  count(continent, fertility_above_2)
##    continent fertility_above_2  n
## 1     Africa             FALSE  2
## 2     Africa              TRUE 49
## 3   Americas             FALSE 13
## 4   Americas              TRUE 22
## 5   Americas                NA  1
## 6       Asia             FALSE 19
## 7       Asia              TRUE 28
## 8     Europe             FALSE 38
## 9     Europe              TRUE  1
## 10   Oceania             FALSE  1
## 11   Oceania              TRUE 11
  • count 함수는 NA 값이 포함된 경우에만 이를 표시.

공분산 (Covariance)과 상관계수 (Correlation)

ggplot(gapminder_new, aes(x=infant_mortality, y=fertility)) +
  geom_point(color="#d90502") +
  labs(
    title = "Relationship between fertility and infant mortality in 2015",
    x = "Infant mortality",
    y = "Fertility rate"
  ) +  theme_minimal()

Covariance

  • 공분산은 두 변수의 공동 변동성(joint variability) 을 측정하는 지표

    \[Cov(x,y) = \frac{1}{N} \sum_{i=1}^N(x_i-\bar{x})(y_i-\bar{y})\]

cov(gapminder_new$fertility,gapminder_new$infant_mortality, use = "complete.obs")
## [1] 24.21146

Correlation

  • 상관관계는 두 변수 간의 선형 관계(linear association) 의 강도를 측정하는 지표 \[Cor(x,y) = \frac{Cov(x,y)}{\sqrt{Var(x)}\sqrt{Var(y)}}\]
cor(gapminder_new$fertility,gapminder_new$infant_mortality, use = "complete.obs")
## [1] 0.8286402

Correlation

  • 상관계수는 항상 -1과 1 사이의 값을 가짐

[ Source: mathisfun]

Task 4

  1. 2011년 GDP의 평균을 계산하고 mean이라는 객체에 할당하시오. 결측값은 제외. 힌트: mean 함수의 도움말을 읽고 NA를 제거하는 방법을 확인

  2. 2011년 GDP의 중앙값을 계산하고 median이라는 객체에 할당하시오. 마찬가지로 결측값을 제외. 중앙값이 평균보다 큰가, 작은가?

  3. geom_density를 사용하여 2011년 GDP의 밀도 그래프(density plot)를 생성하시오. 밀도 그래프는 숫자형 변수의 분포를 나타내는 방법임. 또한 다음 코드를 추가하여 평균과 중앙값을 수직선으로 표시하시오.

    geom_vline(xintercept = as.numeric(mean), colour = "red") +
    geom_vline(xintercept = as.numeric(median), colour = "orange")
  4. 2015년 출산율(fertility)과 유아 사망률(infant mortality)의 상관관계를 계산하시오. NA 값을 제외하려면 cor() 함수의 use 인수를 “pairwise.complete.obs”로 설정. 이 상관관계 값이 Task 3에서 생성한 그래프와 일치하는가?

Task (Optional): 주가 데이터 분석

  1. tidyquant를 이용하여 한국의 대표 주식인 삼성전자(“005930.KS”)와 미국의 시장 지수인 S&P500 ETF(“SPY”)의 2021년 1월 1일 이후 주가 데이터를 다운로드하시오. (tq_get에 티커를 벡터 c() 형태로 입력)
  2. group_by()mutate(), lag() 함수를 사용하여 두 종목의 일별 수익률(daily_return)을 각각 계산하고 drop_na()로 결측치를 제거하시오.
  3. ggplot2를 사용하여 두 종목의 일별 수익률 분포를 비교하는 밀도 그래프(geom_density)를 겹쳐서(alpha 값 조정) 그리시오.
  4. summarise()를 활용하여 두 종목 각각의 일평균 수익률(mean)과 변동성(sd)을 계산하시오.
  5. 두 종목의 수익률 간 상관계수(cor)를 계산하시오. (상관관계를 구하려면 두 수익률을 가로(열)로 나란히 배치해야 하는데, 한국과 미국의 휴장일이 달라 데이터의 길이가 어긋나는 현상이 발생하기 때문에 날짜를 기준으로 데이터를 맞추는 과정(pivot_wider 또는 inner_join)이 필요함.)

THE END!