Ch 04. EDA 리포트 작성하기
분석을 마쳤다고 일이 끝난 건 아닙니다. 결과를 다른 사람에게 전달해야 합니다. R에는 분석 코드와 설명 문서를 하나로 합쳐 리포트를 만드는 도구가 있습니다. R Markdown입니다.
이 챕터에서는 앞선 챕터에서 수행한 공공데이터 분석 결과를 R Markdown 리포트로 정리합니다. R Markdown은 기능이 많지만 여기서는 EDA 리포트 작성에 꼭 필요한 핵심만 다룹니다. 본격적인 R Markdown 활용은 2권에서 다룰 예정입니다.
R Markdown이란
R Markdown은 마크다운 문서 안에 R 코드를 포함시킬 수 있는 형식입니다. 문서를 렌더링하면 코드가 실행되고, 그 결과(표, 그래프, 숫자)가 문서에 삽입됩니다. 분석 과정과 결과가 하나의 문서로 통합되기 때문에 재현 가능한 분석에 이상적입니다.
출력 형식은 HTML, PDF, Word 등을 선택할 수 있습니다. EDA 리포트에는 HTML이 가장 적합합니다. 인터랙티브 요소를 넣을 수 있고, 공유도 쉽습니다.
필요한 패키지 설치
install.packages("rmarkdown")
install.packages("knitr")
# PDF 출력이 필요하다면 tinytex도 설치합니다
# install.packages("tinytex")
# tinytex::install_tinytex()
새 R Markdown 파일 만들기
RStudio에서 File > New File > R Markdown을 선택합니다. 제목과 출력 형식을 입력하면 기본 템플릿이 생성됩니다.
파일 확장자는 .Rmd입니다.
YAML 헤더
파일의 맨 위에는 ---로 감싼 YAML 헤더가 있습니다. 문서의 제목, 작성자, 날짜, 출력 형식을 지정합니다.
---title: "서울 공공데이터 EDA 리포트"author: "홍길동"date: "`r Sys.Date()`"output: html_document: toc: true toc_float: true theme: flatly code_folding: show number_sections: true---
주요 옵션을 살펴보겠습니다.
toc: true— 목차를 자동 생성합니다.toc_float: true— 목차를 화면 왼쪽에 고정합니다.theme— 문서 테마입니다. flatly, cerulean, united 등을 선택할 수 있습니다.code_folding: show— 코드 블록을 접거나 펼 수 있는 버튼을 추가합니다.number_sections: true— 섹션 번호를 자동으로 매깁니다.
코드 청크
R 코드는 코드 청크(code chunk) 안에 작성합니다. 세 개의 백틱과 {r}로 시작하고 세 개의 백틱으로 끝납니다.
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
library(tidyverse)
library(ggplot2)
```
청크 이름은 선택 사항이지만, 문서를 관리할 때 편리합니다. 자주 사용하는 청크 옵션은 다음과 같습니다.
| 옵션 | 기본값 | 설명 |
|---|---|---|
echo |
TRUE | 코드를 문서에 출력할지 여부 |
eval |
TRUE | 코드를 실행할지 여부 |
warning |
TRUE | 경고 메시지 출력 여부 |
message |
TRUE | 패키지 로딩 메시지 출력 여부 |
fig.width |
7 | 그래프 너비 (인치) |
fig.height |
5 | 그래프 높이 (인치) |
인라인 R
문장 중간에 R 계산 결과를 넣고 싶을 때는 인라인 R을 사용합니다.
전체 데이터는 `r nrow(df)` 행으로 구성됩니다.
분석 기준 연도는 `r max(df$year)`년입니다.
렌더링하면 backtick 표현식 자리에 실제 값이 삽입됩니다. 보고서에 숫자를 직접 쓰는 대신 인라인 R을 사용하면, 데이터가 바뀌어도 문서 전체가 자동으로 업데이트됩니다.
완성된 리포트 예시
다음은 서울 지하철 데이터 EDA 리포트의 전체 구조입니다. 이 파일을 eda_report.Rmd로 저장합니다.
---
title: "서울 지하철 승하차 EDA 리포트"
author: "홍길동"
date: "`r Sys.Date()`"
output:
html_document:
toc: true
toc_float: true
theme: flatly
code_folding: show
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE,
fig.width = 8, fig.height = 5)
library(tidyverse)
library(ggplot2)
```
## 개요
이 리포트는 서울시 지하철 승하차 데이터를 탐색한 결과입니다.
분석에 사용한 데이터는 공공데이터 포털(data.go.kr)에서 수집했습니다.
## 데이터 불러오기
```{r load-data}
# 예제 데이터를 생성합니다
set.seed(42)
stations <- c("강남", "홍대입구", "잠실", "사당", "신림")
hours <- 0:23
df <- expand_grid(
date = seq(as.Date("2023-01-01"), as.Date("2023-01-07"), by = "day"),
station = stations,
hour = hours
) |>
mutate(
get_on = as.integer(runif(n(), 500, 15000)),
get_off = as.integer(runif(n(), 500, 15000)),
total = get_on + get_off
)
cat("데이터 크기:", nrow(df), "행,", ncol(df), "열\n")
```
전체 데이터는 `r nrow(df)` 행으로 구성됩니다.
분석 대상 역은 `r n_distinct(df$station)`개이며, 기간은
`r min(df$date)`부터 `r max(df$date)`까지입니다.
## 기초 통계
```{r summary}
df |>
group_by(station) |>
summarise(
평균_승차 = round(mean(get_on)),
평균_하차 = round(mean(get_off)),
총_이용객 = sum(total)
) |>
arrange(desc(총_이용객)) |>
knitr::kable(format.args = list(big.mark = ","))
```
## 시각화
### 역별 이용객 순위
```{r plot-station, fig.cap="역별 총 이용객 비교"}
df |>
group_by(station) |>
summarise(total_passengers = sum(total)) |>
mutate(station = fct_reorder(station, total_passengers)) |>
ggplot(aes(x = station, y = total_passengers / 10000, fill = station)) +
geom_col(show.legend = FALSE) +
coord_flip() +
labs(title = "역별 총 이용객", x = "역명", y = "이용객 (만 명)") +
theme_minimal()
```
### 시간대별 패턴
```{r plot-hourly, fig.cap="시간대별 평균 승하차 인원"}
df |>
group_by(hour) |>
summarise(avg_on = mean(get_on), avg_off = mean(get_off)) |>
pivot_longer(c(avg_on, avg_off), names_to = "type", values_to = "passengers") |>
mutate(type = if_else(type == "avg_on", "승차", "하차")) |>
ggplot(aes(x = hour, y = passengers, color = type, group = type)) +
geom_line(linewidth = 1.2) +
geom_point(size = 2) +
scale_x_continuous(breaks = seq(0, 23, 2)) +
labs(title = "시간대별 평균 승하차", x = "시간대", y = "평균 인원", color = "구분") +
theme_minimal()
```
## 결론
- 가장 이용객이 많은 역은 **`r df |> group_by(station) |> summarise(t=sum(total)) |> slice_max(t,n=1) |> pull(station)`** 입니다.
- 출퇴근 시간대(7~9시, 17~19시)에 이용객이 집중되는 패턴이 확인됩니다.
HTML로 출력하기
RStudio에서 Knit 버튼을 클릭하거나, 콘솔에서 다음 명령을 실행합니다.
rmarkdown::render("eda_report.Rmd")
같은 폴더에 eda_report.html 파일이 생성됩니다. 브라우저로 열면 목차, 코드, 그래프가 모두 포함된 리포트를 확인할 수 있습니다.
PDF로 출력하기
YAML 헤더의 output 부분을 변경합니다.
output: pdf_document: latex_engine: xelatex mainfont: "NanumGothic"
한글 PDF 출력에는 XeLaTeX 엔진과 한글 폰트 설정이 필요합니다. PDF보다는 HTML로 시작하는 것을 권장합니다.
리포트 작성 팁
몇 가지 습관을 들이면 리포트의 품질이 높아집니다.
첫째, 청크 이름을 의미 있게 붙입니다. chunk1, chunk2보다 load-data, plot-hourly처럼 내용을 담은 이름이 오류 추적에 유용합니다.
둘째, 전역 설정 청크를 문서 맨 위에 배치합니다. knitr::opts_chunk$set()으로 경고 메시지, 그래프 크기 등을 한 번에 설정하면 각 청크에 반복 설정할 필요가 없습니다.
셋째, 인라인 R을 적극 활용합니다. 보고서에 수치를 직접 입력하면 데이터가 바뀔 때마다 수동으로 수정해야 합니다. 인라인 R을 쓰면 렌더링 시 자동으로 업데이트됩니다.
넷째, 섹션별 결론을 문장으로 적습니다. 그래프만 나열한 리포트는 독자가 의미를 스스로 해석해야 합니다. 각 시각화 뒤에 한두 문장으로 핵심 인사이트를 정리합니다.
이 파트를 마칩니다. data.go.kr에서 데이터를 가져와, 전처리하고, EDA를 수행하고, R Markdown으로 리포트를 만드는 전 과정을 경험했습니다. 다음 파트에서는 금융 데이터를 다루면서 실전 분석의 폭을 넓혀보겠습니다.