--- title: "Tidying, Visualising and Summarising Data" subtitle: "FMB819: R을 이용한 데이터분석" author: "고려대학교 경영대학 정지웅" format: revealjs: theme: simple transition: fade transition-speed: fast scrollable: true chalkboard: true slide-number: true revealjs-plugins: - revealjs-text-resizer # Thanks to https://github.com/gadenbuie/revealjs-text-resizer#readme --- ```{r setup, include=FALSE,warning=FALSE,message=FALSE} options(htmltools.dir.version = FALSE) knitr::opts_chunk$set( message = FALSE, warning = FALSE, dev = "svg", cache = TRUE, fig.align = "center", comment = "##" #fig.width = 11, #fig.height = 5 ) library(countdown) # countdown style countdown( color_border = "#d90502", color_text = "black", color_running_background = "#d90502", color_running_text = "white", color_finished_background = "white", color_finished_text = "#d90502", color_finished_border = "#d90502" ) ``` ## Working With Data - [2014년 뉴욕타임즈 기사](https://www.nytimes.com/2014/08/18/technology/for-big-data-scientists-hurdle-to-insights-is-janitor-work.html)에 따르면, "데이터 과학자들은 데이터를 분석하기 전에 **50%에서 80%의 시간**을 데이터 수집 및 정리에 소비한다."라고 함. - 본 강의에서는 **데이터 정리(Tidying), 시각화(Visualising), 요약(Summarising)**의 기본을 배움. # Tidying Data ------------------------------------------------------------------------ ## `dplyr` 소개 - [`dplyr`](https://dplyr.tidyverse.org)은 [`tidyverse`](https://www.tidyverse.org) 패키지군의 핵심 데이터 조작 패키지. - [`data.table`](https://github.com/Rdatatable/data.table/wiki)도 대안으로 사용 가능하며, 거대 용량 데이터 처리에 최적화되어 있음. - 본 강의에서는 문법이 직관적이고 가독성이 높은 `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년 미국 대선과 관련된 여론조사 데이터를 포함 ```{r dslabs, echo = TRUE} 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]) ``` - tibble은 data.frame의 개선된 버전으로, 더 깔끔한 출력과 편리한 기능을 제공. ------------------------------------------------------------------------ ## 예제 데이터: 구조 확인 - 이 데이터셋에 어떤 변수가 포함되어 있는지 확인 ```{r, echo=TRUE} str(polls_us_election_2016) # 데이터의 구조를 요약하여 출력 ``` ------------------------------------------------------------------------ ## `dplyr`: `filter()` ```{r, echo=TRUE} # 표본 크기(samplesize)가 2000보다 큰 행만 선택, 츨력 polls_us_election_2016 %>% filter(samplesize > 2000) ``` ------------------------------------------------------------------------ ## `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%` 연산자: `x`가 `y` 벡터 안에 포함되어 있는지 확인 - `!(x %in% y)`: 반대로 포함되지 않은 것만 필터링 ```{r, echo=TRUE} # 3이 1부터 3까지의 숫자 중 하나인지 확인 (TRUE) 3 %in% 1:3 # 벡터화된 연산: 2와 5가 2부터 10 사이의 숫자 중 하나인지 확인 c(2,5) %in% 2:10 ``` ------------------------------------------------------------------------ ## `dplyr`: `filter()` 복합 조건 A 등급을 받은 여론조사 중 표본 크기가 2,000명 이상이고, 트럼프가 최소 45%의 지지를 받은 여론조사는? ```{r, echo=TRUE} # 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) ``` ------------------------------------------------------------------------ ## `dplyr`: `mutate()` 새로운 변수(열)를 만들 때 사용. 1. 각 여론조사에서 트럼프와 클린턴의 지지율 합계 2. 트럼프의 원래(raw) 지지율과 조정(adjusted) 지지율 간의 차이 ```{r, echo=TRUE} #| code-line-numbers: "|5|6|7" polls_us_election_2016 %>% mutate(trump_clinton_tot = rawpoll_trump + rawpoll_clinton, # 두 후보 지지율 합계 trump_raw_adj_diff = rawpoll_trump - adjpoll_trump) %>% # 원래 지지율과 조정 지지율 차이 names() # 새롭게 생성된 변수명 확인 ``` ------------------------------------------------------------------------ ## `dplyr`: `select()` - 분석에 필요한 특정 열(변수)만 골라낼 때 사용. 데이터의 용량을 줄이고 직관성을 높임. ```{r, echo = TRUE} # 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() # 데이터 프레임의 변수명을 출력하여 올바르게 선택되었는지 확인 ``` ------------------------------------------------------------------------ ## `dplyr`: `summarise()` - 원하는 통계량으로 데이터를 요약 (평균, 최대값, 최소값 등) - 트럼프의 최대 지지율은 얼마인가? ```{r, echo = TRUE} # summarise() 함수를 사용하여 트럼프의 최대(raw) 지지율을 계산 # max() 함수는 주어진 열에서 가장 큰 값을 반환 polls_us_election_2016 %>% summarise(max_trump = max(rawpoll_trump)) ``` ------------------------------------------------------------------------ ## `dplyr`: `group_by()` - `group_by()` 단독으로는 데이터에 눈에 띄는 변화가 없지만, `summarise()`나 `mutate()`와 결합하면 강력해짐. - 여론조사 등급(grade)별 클린턴의 평균 지지율은 얼마인가? ```{r, echo = TRUE} # group_by()를 사용하여 여론조사 등급(grade)별로 그룹을 나눔. # summarise()를 사용하여 각 그룹에서 rawpoll_clinton(클린턴의 원래 지지율)의 평균 계산 polls_us_election_2016 %>% group_by(grade) %>% # 여론조사 등급별 그룹화 summarise(mean_vote_clinton = mean(rawpoll_clinton, na.rm = TRUE)) # 각 그룹에서 클린턴의 평균 지지율 계산 ``` ------------------------------------------------------------------------ ## 명령어 연결하기 ::::: columns ::: {.column width="40%"} ```{r, echo=TRUE, eval = FALSE} polls_us_election_2016 ``` ::: ::: {.column width="60%"} ```{r, echo = FALSE} polls_us_election_2016 ``` ::: ::::: ------------------------------------------------------------------------ ## 명령어 연결하기 ::::: columns ::: {.column width="40%"} ```{r, echo=TRUE, eval = FALSE} polls_us_election_2016 %>% mutate(trump_clinton_diff = rawpoll_trump - rawpoll_clinton) ``` ::: ::: {.column width="60%"} ```{r, echo = FALSE} polls_us_election_2016 %>% mutate(trump_clinton_diff = rawpoll_trump - rawpoll_clinton) ``` ::: ::::: ------------------------------------------------------------------------ ## 명령어 연결하기 ::::: columns ::: {.column width="40%"} ```{r, echo=TRUE, eval = FALSE} polls_us_election_2016 %>% mutate(trump_clinton_diff = rawpoll_trump - rawpoll_clinton) %>% filter(trump_clinton_diff>5 & state == "Iowa" & is.na(rawpoll_johnson)) ``` ::: ::: {.column width="60%"} ```{r, echo = FALSE} polls_us_election_2016 %>% mutate(trump_clinton_diff = rawpoll_trump - rawpoll_clinton) %>% filter(trump_clinton_diff>5 & state == "Iowa" & is.na(rawpoll_johnson)) ``` ::: ::::: ## 명령어 연결하기 ::::: columns ::: {.column width="40%"} ```{r, echo=TRUE, eval = FALSE} 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(여론조사 기관) 열만 선택 ``` ::: ::: {.column width="60%"} ```{r, echo = FALSE} 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) ``` ::: ::::: ------------------------------------------------------------------------ ## 명령어 연결하기 ::::: columns ::: {.column width="40%"} ```{r, echo=TRUE, eval = FALSE} 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(여론조사 기관) 열의 값을 추출하여 벡터로 반환 ``` ::: ::: {.column width="60%"} ```{r, echo = FALSE} 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) ``` ::: ::::: ------------------------------------------------------------------------ ## 결측값(`NA`) 처리 ::::: columns ::: {.column width="50%"} - 값이 *누락(missing)* 되었을 경우 `NA`로 표시됨. ```{r, echo=TRUE} x <- NA # x에 결측값(NA) 할당 ``` - `NA`가 포함된 연산을 수행하면, 결과도 `NA`가 됨. ```{r, echo=TRUE} NA > 5 # NA는 숫자가 아니므로 비교 불가능 -> NA 반환 NA + 10 # NA와 연산하면 결과도 NA ``` - `is.na(x)` 함수를 사용하면 값이 `NA`인지 확인 가능. ```{r, echo=TRUE} is.na(x) # x가 NA인지 확인 -> TRUE 반환 ``` ::: ::: {.column width="50%"} - NA 값은 서로 비교할 수 없음. ```{r, echo=TRUE} NA == NA # NA끼리 비교하면 결과도 NA ``` - 예제 ```{r, echo=TRUE} # x는 Mary의 나이, 정확한 나이를 모름. x <- NA # y는 John의 나이, 정확한 나이를 모름. y <- NA # John과 Mary의 나이가 같은가? x == y ``` - `NA == NA`는 `TRUE`가 아니라 `NA`를 반환하는데, 이는 두 값이 같은지 여부를 판단할 수 없기 때문임. ::: ::::: ------------------------------------------------------------------------ ## Task 1 {background-color="#ffebf0"} `r countdown(minutes = 10, top = "20px", right = "10px", font_size = "0.8em", color_border = "blue")` 다음 코드를 실행하여 데이터를 로드합니다. ```{r, echo = T, eval = F} library(dslabs) data(polls_us_election_2016) ``` 1. grade 값이 결측치(NA)인 여론조사는? `head`를 사용하여 확인. 2. 다음 조건을 모두 만족하는 여론조사는? (i) American Strategies, GfK Group, Merrill Poll에서 조사한 경우, (ii) 표본 크기가 1,000명 이상인 경우, (iii) 2016년 10월 20일에 시작된 경우. **힌트**: (i) 조건에서는 %in% 연산자가 유용, 벡터는 `c()`함수로 만들 수 있음. (iii)에서는 날짜 변수의 형식을 확인 "yyyy-mm-dd" 3. 다음 조건을 모두 만족하는 여론조사는? (i) Johnson 후보의 여론조사 데이터가 누락되지 않은 경우, (ii) 트럼프와 클린턴의 원본 여론조사 지지율 합이 95%를 초과하는 경우, (iii) 오하이오(OH) 주에서 실시된 경우 **힌트**: 트럼프와 클린턴의 지지율 합계를 계산하는 새로운 변수를 생성한 후 `filter()` 를 적용 4. 표본 크기가 2,000명 이상인 여론조사에서 트럼프의 평균 지지율이 가장 높은 주는? **힌트**: `filter(), group_by(), summarise(), arrange()` 사용. 내림차순 정렬하려면 `arrange()` 함수 사용. ------------------------------------------------------------------------ ## 주가 자료 다루기 {background-color="#E2E2E2"} ```{r, echo=T, eval=T} 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)) ``` # Visualising Data ## 기본 `R` 그래프와 `ggplot2` - `ggplot2` (이 패키지는 `tidyverse`의 일부) 기본 `R`의 그래프 기능은 보다 유연함 - 데이터를 시각적 요소(색상, 형태, 크기 등)에 '매핑(Mapping)'한다는 철학(Grammar of Graphics)을 가짐. - 예제를 실행하기 위해 `gapminder` 데이터셋 사용. ------------------------------------------------------------------------ ## `gapminder` 개요 - 먼저 `gapminder` 데이터를 로드: ```{r, echo = TRUE} library(dslabs) # dslabs 패키지 로드 data(gapminder, package = "dslabs") # gapminder 데이터셋 불러오기 ``` - 데이터의 처음 3개 행과 마지막 2개 행을 확인. ```{r, echo = TRUE} head(gapminder, n = 3) # 데이터의 처음 3행 출력 tail(gapminder, n = 2) # 데이터의 마지막 2행 출력 ``` ------------------------------------------------------------------------ ## Task 2 {background-color="#ffebf0"} `r countdown(minutes = 5, top = "20px", right = "10px", font_size = "0.8em", color_border = "blue")` 다음 코드를 실행하여 데이터를 로드. ```{r, eval = F} library(dslabs) data(gapminder, package = "dslabs") ``` 1. 대륙(continent)/연도(year)별 **평균 인구(변수명: mean_pop)**를 계산하고, 결과를 새로운 객체 `gapminder_mean`에 저장하시오. **힌트**: 두 개의 기준으로 그룹화해야 하므로 `group_by(continent, year)`를 사용하고 `summarise`를 적용. ```{r, echo=F} gapminder_mean <- gapminder %>% group_by(continent, year) %>% summarise(mean_pop = mean(population)) ``` ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data ``` r data %>% ggplot() ``` or ``` r ggplot(data) ``` ::: ::: {.column width="60%"} - Tidy Data - 각 변수는 ***열(column)***을 형성 - 각 관측치는 ***행(row)***을 형성 - 시각화를 시작할 때 고려 사항 - 시각화에서 어떤 정보를 사용할 것인가? - 해당 데이터가 하나의 열 또는 행에 포함되어 있는가? ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics ``` r + aes() ``` ::: ::: {.column width="60%"} 데이터를 시각적 요소 또는 매개변수에 매핑 - year (연도) - population (인구) - country (국가) ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics ``` r + aes() ``` ::: ::: {.column width="60%"} 데이터를 시각적 요소 또는 매개변수에 매핑 - year (연도) → **x** - population (인구) → **y** - country (국가) → *shape*, *color*, etc. ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics ``` r + aes() ``` ::: ::: {.column width="60%"} ``` r aes( x = year, y = population, color = country ) ``` ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms ``` r + geom_*() ``` ::: ::: {.column width="60%"} Geometric objects: ```{r geom_demo, echo=FALSE, fig.width=6, fig.height=3.5, out.width="650px"} minimal_theme <- theme_bw() + theme( axis.text = element_blank(), axis.ticks = element_blank(), panel.grid = element_blank(), panel.border = element_blank(), axis.title = element_blank(), plot.title = element_text(hjust = 0.5), text = element_text(family = "Fira Mono"), plot.background = element_rect(fill = "white", color = NA), panel.background = element_rect(fill = "white", color = NA) ) set.seed(4242) df_geom <- data_frame(y = rnorm(10), x = 1:10) g_geom <- list() g_geom$point <- ggplot(df_geom, aes(x, y)) + geom_point() + ggtitle("geom_point()") g_geom$line <- ggplot(df_geom, aes(x, y)) + geom_line() + ggtitle("geom_line()") g_geom$bar <- ggplot(df_geom, aes(x, y)) + geom_col() + ggtitle("geom_col()") g_geom$boxplot <- ggplot(df_geom, aes(y = y)) + geom_boxplot() + ggtitle("geom_boxplot()") g_geom$histogram <- ggplot(df_geom, aes(y)) + geom_histogram(binwidth = 1) + ggtitle("geom_histogram()") g_geom$density <- ggplot(df_geom, aes(y)) + geom_density(fill = "grey40", alpha = 0.25) + ggtitle("geom_density()") + xlim(-4, 4) g_geom <- map(g_geom, ~ . + minimal_theme) cowplot::plot_grid(plotlist = g_geom) ``` ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms ``` r + geom_*() ``` ::: ::: {.column width="60%"} [링크: 많이 사용되는 geoms](https://eric.netlify.com/2017/08/10/most-popular-ggplot2-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 ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms ``` r + geom_*() ``` ::: ::: {.column width="60%"} `RStudio`에서 `geom_`을 입력하면 자동 완성 기능을 통해 사용할 수 있는 모든 옵션 확인 가능 ::: ::::: ------------------------------------------------------------------------ ## (Y)Our first plot! ::::: columns ::: {.column width="40%"} ```{r first-plot0a, echo=TRUE, eval=FALSE} gapminder_mean ``` ::: ::: {.column width="60%"} ```{r first-plot0a-out, ref.label='first-plot0a', echo=FALSE} ``` ::: ::::: ------------------------------------------------------------------------ ## (Y)Our first plot! ::::: columns ::: {.column width="40%"} ```{r first-plot1a, echo=TRUE, eval=FALSE} gapminder_mean %>% ggplot() ``` ::: ::: {.column width="60%"} ```{r first-plot1a-out, ref.label='first-plot1a', echo=FALSE, fig.height = 3.5, out.width="100%"} ``` ::: ::::: ------------------------------------------------------------------------ ## (Y)Our first plot! ::::: columns ::: {.column width="40%"} ```{r first-plot1b, echo=TRUE, eval=FALSE} gapminder_mean %>% ggplot() + aes(x = year, #<< y = mean_pop) #<< ``` ::: ::: {.column width="60%"} ```{r first-plot1b-out, ref.label='first-plot1b', echo=FALSE, fig.height = 3.5, out.width="100%"} ``` ::: ::::: ------------------------------------------------------------------------ ## (Y)Our first plot! ::::: columns ::: {.column width="40%"} ```{r first-plot1c, echo=TRUE, eval=FALSE} gapminder_mean %>% ggplot() + aes(x = year, y = mean_pop) + geom_point() #<< ``` ::: ::: {.column width="60%"} ```{r first-plot1c-out, ref.label='first-plot1c', echo=FALSE, fig.height = 3.5, out.width="100%"} ``` ::: ::::: ------------------------------------------------------------------------ ## (Y)Our first plot! ::::: columns ::: {.column width="40%"} ```{r first-plot1, echo=TRUE, eval=FALSE} gapminder_mean %>% ggplot() + aes(x = year, y = mean_pop, color = continent) + #<< geom_point() ``` ::: ::: {.column width="60%"} ```{r first-plot1-out, ref.label='first-plot1', echo=FALSE, fig.height = 3.5, out.width="100%"} ``` ::: ::::: ------------------------------------------------------------------------ ## (Y)Our first plot! ::::: columns ::: {.column width="40%"} ```{r first-plot2-fake, echo=TRUE, eval=FALSE} gapminder_mean %>% ggplot() + aes(x = year, y = mean_pop, color = continent) + geom_point() + geom_line() #<< ``` ::: ::: {.column width="60%"} ```{r first-plot2-fake-out, ref.label='first-plot2-fake', fig.height = 3.5, echo=FALSE, out.width="100%"} ``` ::: ::::: ------------------------------------------------------------------------ ## (Y)Our first plot! ::::: columns ::: {.column width="40%"} ```{r first-plot2-line, echo=TRUE, eval=FALSE} gapminder_mean %>% ggplot() + aes(x = year, y = mean_pop, color = continent) + # geom_point() + #<< geom_line() ``` ::: ::: {.column width="60%"} ```{r first-plot2-line-out, ref.label='first-plot2-line', fig.height = 3.5, echo=FALSE, out.width="100%"} ``` ::: ::::: ------------------------------------------------------------------------ ## (Y)Our first plot! ::::: columns ::: {.column width="40%"} ```{r save-plot, echo=TRUE, eval=FALSE} g = gapminder_mean %>% #<< ggplot() + aes(x = year, y = mean_pop, color = continent) + # geom_point() + geom_line() g #<< # graphs can be saved as # objects! ``` ::: ::: {.column width="60%"} ```{r save-plot-out, ref.label='save-plot', fig.height = 3.5, echo=FALSE, out.width="100%"} ``` ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms Facet ``` r + facet_wrap() # 여러 개의 작은 그래프로 분할 (자동 배치) + facet_grid() # 행과 열로 그래프를 체계적으로 배열 ``` ::: ::: {.column width="60%"} ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms Facet ``` r + facet_wrap() + facet_grid() ``` ::: ::: {.column width="60%"} ```{r geom_facet_setup, include=FALSE} g <- ggplot(gapminder_mean) + aes(x = year, y = mean_pop, color = continent) + geom_point() + geom_line() ``` ```{r geom_facet, echo=TRUE, out.width="90%", fig.height = 3.5, fig.width=6} g + facet_wrap(~ continent) ``` ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms Facet ``` r + facet_wrap() + facet_grid() ``` ::: ::: {.column width="60%"} ```{r geom_grid, echo=TRUE, out.width="90%", fig.height = 3.5, fig.width=6} g + facet_grid(~ continent) ``` ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms Facet Labels ``` r + labs() ``` ::: ::: {.column width="60%"} ```{r labs-ex, echo=TRUE, out.width="90%", fig.height = 3.5, fig.width=6} g + labs(x = "Year", y = "Average Population", color = "Continent") ``` ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms Facet Labels Scales ``` r + scale_*_*() ``` ::: ::: {.column width="60%"} `scale` + `_` + `` + `_` + `` + `()` 어떤 매개변수를 조정하고 싶은가? → `` 그 매개변수의 유형은 무엇인가? → `` - 이산형(x축) 조정
`scale_x_discrete()` - 연속형 변수에서 포인트 크기 조정
`scale_size_continuous()` - y축을 로그 스케일로 변환
`scale_y_log10()` - 색상 세팅 변환
`scale_fill_discrete()`
`scale_color_manual()` ::: ::::: ------------------------------------------------------------------------ ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms Facet Labels Scales ``` r + scale_*_*() ``` ::: ::: {.column width="60%"} ```{r scale_ex1, out.width="90%", fig.height = 3.5, fig.width=6, echo=TRUE} g + scale_color_viridis_d() ``` ::: ::::: ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms Facet Labels Scales ``` r + scale_*_*() ``` ::: ::: {.column width="60%"} ```{r scale_ex2, out.width="90%", fig.height = 3.5, fig.width=6, echo=TRUE} g + scale_y_log10() ``` ::: ::::: ## gg = Grammar of Graphics ::::: columns ::: {.column width="40%"} Data Aesthetics Geoms Facet Labels Scales ``` r + scale_*_*() ``` ::: ::: {.column width="60%"} ```{r scale_ex4, out.width="90%", fig.height = 3.5, fig.width=6, echo=TRUE} g + scale_x_continuous(breaks = seq(1950, 2020, 10)) ``` ::: ::::: ------------------------------------------------------------------------ ## `ggplot` 심화 학습 - `ggplot2`는 사용자가 정의할 수 있는 무수한 옵션을 제공. - ggplot 웹사이트 에 있는 치트시트를 참고: [링크](https://ggplot2.tidyverse.org) - [Garrick Aden-Buie](https://www.garrickadenbuie.com/)의 [Gentle Guide to the Grammar of Graphics with `ggplot2`](https://pkg.garrickadenbuie.com/gentle-ggplot2/#1) ------------------------------------------------------------------------ ## Task 3 {background-color="#ffebf0"} `r countdown(minutes = 10, top = "20px", right = "10px", font_size = "0.8em", color_border = "blue")` `gapminder` 데이터를 사용하여 다음의 그래프를 `ggplot2`로 생성하시오 ```{r} library(dslabs) data(gapminder, package = "dslabs") ``` 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 = "출산율") --- ## 주가 자료 다루기 {background-color="#E2E2E2"} ```{r, echo=T, eval=T} # 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() # 이코노미스트 잡지 스타일 테마 ``` ## 주가 자료 다루기 {background-color="#E2E2E2"} ```{r, echo=T, eval=T} # 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 ::::: columns ::: {.column width="45%"} - 평균 (Mean) `mean(x)`: `x`의 모든 값의 평균을 계산. $$\bar{x} = \frac{1}{N}\sum_{i=1}^N x_i$$ ```{r, echo=T} x <- c(1,2,2,2,2,100) # 데이터 샘플 mean(x) # 평균 계산 mean(x) == sum(x) / length(x) # 평균 공식 확인 ``` ::: ::: {.column width="45%"} - 중앙값 (Median) `median(x)`: `x`의 값들 중 **50%가 해당 값보다 작거나 같고, 50%가 크거나 같은 값**을 찾음. $m$이 중앙값이라면: $$\Pr(X \leq m) \geq 0.5 \text{ 그리고 } \Pr(X \geq m) \geq 0.5$$ 중앙값은 **이상치(outliers)**에 강건(robust). ```{r, echo=T} median(x, echo=T) # 중앙값 계산 ``` ::: ::::: ------------------------------------------------------------------------ ## Spread ::::: columns ::: {.column width="45%"} 중심(평균)에서 얼마나 퍼져 있는지를 측정 분산(Variance)은 이러한 분포의 척도 중 하나 $$Var(X) = \frac{1}{N} \sum_{i=1}^N(x_i-\bar{x})^2$$ 평균이 0인 두 개의 `정규 분포`를 비교해 보자. ::: ::: {.column width="55%"} ```{r, echo = FALSE, fig.height=4, message = FALSE, warning = FALSE} library(ggplot2) ggplot(data = data.frame(x = c(-5, 5)), aes(x)) + stat_function(fun = dnorm, n = 101, args = list(mean = 0, sd = 1), aes(color = "1"), size = 2) + stat_function(fun = dnorm, n = 101, args = list(mean = 0, sd = 2), aes(color = "4"), size = 2) + ylab(NULL) + scale_y_continuous(breaks = NULL) + scale_color_manual("분산:", values = c("#d90502", "#DE9854")) + theme_bw() + theme(legend.position = c(0.02,0.98), legend.justification = c(0,1), text = element_text(size=20)) ``` 분산 계산: ```{r, eval = FALSE, echo=TRUE} var(x) ``` ::: ::::: ------------------------------------------------------------------------ ## Tabulating Data - `table(x)` 함수는 `x` 내 각 고유 값의 발생 횟수를 계산하는 데 유용. ```{r, echo=TRUE} table(gapminder$continent) ``` - 동일한 작업을 dplyr의 count 함수를 사용하여 수행할 수도 있음. ```{r, echo=TRUE} gapminder %>% count(continent) ``` ------------------------------------------------------------------------ ## Tabulating Data - Contingency Table 생성 ```{r, echo=TRUE} gapminder_new <- gapminder %>% filter(year == 2015) %>% mutate(fertility_above_2 = (fertility > 2)) # dummy variable for fertility rate above replacement rate ``` ::::: columns ::: {.column width="45%"} ```{r, echo=TRUE} table(gapminder_new$fertility_above_2) ``` ::: ::: {.column width="55%"} ```{r, echo=TRUE} table(gapminder_new$fertility_above_2,gapminder_new$continent) ``` ::: ::::: - `prop.table`을 사용하여 비율을 계산 가능: ```{r, echo=TRUE} # proportions by row prop.table(table(gapminder_new$fertility_above_2,gapminder_new$continent), margin = 1) # proportions by column prop.table(table(gapminder_new$fertility_above_2,gapminder_new$continent), margin = 2) ``` - `NA` 값을 포함한 `table` 또는 `crosstable`을 얻으려면 `useNA = "always"` 또는 `useNA = "ifany"` 옵션을 사용 ------------------------------------------------------------------------ ## Tabulating Data - count 함수를 사용한 데이터 요약 ```{r, echo=T} gapminder_new %>% count(continent, fertility_above_2) ``` - `count` 함수는 `NA` 값이 포함된 경우에만 이를 표시. ------------------------------------------------------------------------ ## 공분산 (Covariance)과 상관계수 (Correlation) ```{r x-y-corr,echo=TRUE,eval=TRUE,fig.align='center',fig.height = 4,fig.width=8} 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})$$ ```{r, echo=T} cov(gapminder_new$fertility,gapminder_new$infant_mortality, use = "complete.obs") ``` ------------------------------------------------------------------------ ## Correlation - 상관관계는 두 변수 간의 선형 관계(linear association) 의 강도를 측정하는 지표 $$Cor(x,y) = \frac{Cov(x,y)}{\sqrt{Var(x)}\sqrt{Var(y)}}$$ ```{r, echo=T} cor(gapminder_new$fertility,gapminder_new$infant_mortality, use = "complete.obs") ``` ------------------------------------------------------------------------ ## Correlation - 상관계수는 항상 -1과 1 사이의 값을 가짐 ```{r, echo = F, out.width = "100%"} knitr::include_graphics("../img/photos/correlation-examples.svg") ``` \[ *Source: [mathisfun](https://www.mathsisfun.com/data/correlation.html)*\] ------------------------------------------------------------------------ ## Task 4 {background-color="#ffebf0"} `r countdown(minutes = 10, top = "20px", right = "10px", font_size = "0.8em", color_border = "blue")` 1. 2011년 GDP의 평균을 계산하고 mean이라는 객체에 할당하시오. 결측값은 제외. **힌트**: `mean` 함수의 도움말을 읽고 `NA`를 제거하는 방법을 확인 2. 2011년 GDP의 중앙값을 계산하고 median이라는 객체에 할당하시오. 마찬가지로 결측값을 제외. 중앙값이 평균보다 큰가, 작은가? 3. `geom_density`를 사용하여 2011년 GDP의 밀도 그래프(density plot)를 생성하시오. 밀도 그래프는 숫자형 변수의 분포를 나타내는 방법임. 또한 다음 코드를 추가하여 평균과 중앙값을 수직선으로 표시하시오. ``` r 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): 주가 데이터 분석 {background-color="#ebf8ff"} 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!