R은 통계 분석과 데이터 시각화에 특화된 오픈소스 프로그래밍 환경으로, 통계·데이터과학 분야에서 사실상의 표준 도구로 자리잡았다. 이번 과제는 R 스튜디오를 활용한 작업 폴더 관리, 내장 데이터셋의 기술통계 산출, 사용자 정의 함수 작성, tibble 자료구조 변환, 조건부 범주 생성, 그리고 ggplot2를 활용한 시각화와 R Markdown 기반 문서화에 이르기까지 R 활용의 전반을 포괄적으로 다룬다. 본 보고서에서는 다섯 문항 각각에 대해 코드와 결과, 그리고 코드가 의도하는 바를 풀어 설명한다.
1번 문항: 작업 폴더 지정과 airquality 데이터 기술통계
1-1) 작업 폴더 생성 및 작업 경로 지정
R에서 작업 디렉터리(Working Directory)는 파일을 읽고 쓸 때 기준이 되는 위치이다. 폴더가 존재하지 않을 수 있으므로 dir.create()에 recursive = TRUE 인자를 함께 주어 상위 경로가 없어도 안전하게 생성하도록 했다. 이후 setwd()로 해당 경로를 작업 경로로 지정하고, getwd()로 정상 반영되었는지 확인한다.
# 1-1) 작업 폴더 생성 및 기본 작업 경로 지정
work_dir <- "c:/R_Study/Analysis"
# 폴더가 없는 경우에만 새로 생성 (showWarnings = FALSE로 중복 생성 경고 억제)
if (!dir.exists(work_dir)) {
dir.create(work_dir, recursive = TRUE, showWarnings = FALSE)
}
setwd(work_dir) # 작업 경로 지정
getwd() # 현재 작업 경로 확인 -> "c:/R_Study/Analysis"
getwd() 실행 결과는 "c:/R_Study/Analysis"로 출력되어 작업 경로가 올바르게 설정되었음을 확인할 수 있다. 절대경로를 사용한 이유는 상대경로보다 실행 위치에 따른 혼란이 적기 때문이며, 윈도우 운영체제에서도 슬래시(/)를 권장한다는 점을 반영했다.
1-2) airquality 데이터 기술통계량 출력
airquality는 1973년 5월부터 9월까지 뉴욕시의 대기질을 측정한 154개 관측치, 6개 변수로 구성된 R 기본 내장 데이터셋이다. 문제는 Month 변수를 단순 수치형이 아닌 범주형으로 변환한 뒤 요약 통계를 산출하라고 명시하고 있다. 수치형 그대로 두면 평균·중앙값 같은 산술 요약이 출력되지만, factor로 변환하면 월별 도수분포(빈도)로 출력되어 자료의 분포 구조를 더 직관적으로 파악할 수 있다.
# 1-2) airquality 데이터 기술통계
data(airquality) # 내장 데이터 로드 (안전장치)
str(airquality) # 변수 구조 확인
# Month 변수를 범주형(factor)으로 변환
airquality$Month <- as.factor(airquality$Month)
# 전체 변수의 요약 통계량 출력
summary(airquality)
summary() 출력 결과에서 Ozone은 평균 약 42.13, 중앙값 31.5로 우측 꼬리가 긴 분포를 보이며 결측치(NA) 37개가 포함되어 있음을 확인할 수 있다. 반면 Month는 범주형으로 변환되었기 때문에 5: 31 6: 30 7: 31 8: 31 9: 30처럼 월별 관측 일수가 도수표로 출력된다. 이는 동일 변수라도 자료형에 따라 요약 결과의 의미가 완전히 달라진다는 점을 잘 보여 준다.
1-3) 요약 통계량을 CSV로 저장
summary() 객체는 table 클래스로 반환되어 그대로 CSV로 저장하기 어렵다. 따라서 as.data.frame()으로 데이터프레임 형태로 변환한 뒤 write.csv() 함수로 저장한다. 한글이 포함될 가능성에 대비해 fileEncoding = "UTF-8"을 명시했고, 행 번호 컬럼을 별도로 저장하지 않도록 row.names = FALSE를 지정했다.
# 1-3) 요약 통계를 데이터프레임으로 변환하여 CSV로 저장
air_summary <- as.data.frame(summary(airquality))
# 작업 경로(현재 c:/R_Study/Analysis)에 air_summary.csv로 저장
write.csv(air_summary,
file = "air_summary.csv",
row.names = FALSE,
fileEncoding = "UTF-8")
# 저장 확인
list.files(pattern = "air_summary") # "air_summary.csv"
저장된 CSV 파일을 열어 보면 Var1(행 번호), Var2(원 변수명), Freq(요약 통계 문자열) 세 컬럼으로 구성되어 있다. 이러한 가공된 요약 표는 보고서 첨부나 후속 분석의 입력 자료로 활용할 수 있다.
2번 문항: 2×2 행렬 곱셈 함수 정의
R에서 행렬 곱은 일반 곱 연산자(*)가 아닌 별도의 행렬곱 연산자 %*%로 수행해야 한다. *를 쓰면 원소별 곱(Hadamard product)이 적용되어 의도와 전혀 다른 결과를 낳기 때문이다. 함수는 두 행렬을 인자로 받아 각각 출력한 뒤, 행렬곱 결과를 보기 좋게 정렬해 화면에 표시하도록 설계했다.
# 2번) 2x2 행렬 곱 함수
matMul2x2 <- function(X, Y) {
# 입력값이 2x2 행렬인지 안전성 검증
if (!is.matrix(X) || !is.matrix(Y) ||
!all(dim(X) == c(2, 2)) || !all(dim(Y) == c(2, 2))) {
stop("X와 Y는 모두 2x2 행렬이어야 합니다.")
}
cat("X = \n"); print(X) # 행렬 X 출력
cat("\nY = \n"); print(Y) # 행렬 Y 출력
result <- X %*% Y # 행렬곱 계산
cat("\nX × Y = \n") # 결과 라벨
print(result) # 결과 행렬 출력
invisible(result) # 함수 외부에서도 결과 활용 가능
}
# 함수 테스트 (예시 입력값)
X <- matrix(c(1, 2, 3, 4), nrow = 2, ncol = 2)
Y <- matrix(c(5, 6, 7, 8), nrow = 2, ncol = 2)
matMul2x2(X, Y)
R의 matrix()는 기본적으로 열 우선(column-major)으로 값을 채운다. 따라서 c(1, 2, 3, 4)로 만든 X는 1행 1열부터 시작해 [1, 3 / 2, 4] 구조를 갖는다. 같은 방식으로 Y는 [5, 7 / 6, 8]이 되며, 행렬곱 결과는 1행 1열이 1·5 + 3·6 = 23, 1행 2열은 1·7 + 3·8 = 31, 2행 1열은 2·5 + 4·6 = 34, 2행 2열은 2·7 + 4·8 = 46이 된다. 즉 X × Y = [23, 31 / 34, 46]이 출력된다. invisible()을 사용한 것은 화면에는 한 번만 출력하되, 변수에 결과를 받아 후속 연산에 활용할 수 있도록 하기 위함이다.
3번 문항: iris 데이터의 tibble 변환과 그룹 비교
3-1) iris를 tibble로 변환
tibble은 tidyverse 생태계에서 사용하는 현대적 데이터프레임이다. 기본 data.frame보다 출력이 깔끔하고, 문자형을 자동으로 factor로 바꾸지 않으며, 부분 일치(partial matching)를 허용하지 않아 안전성이 더 높다는 장점이 있다. tibble 패키지의 as_tibble()이나 dplyr 패키지에서도 같은 함수를 사용할 수 있다.
# 필요한 패키지 로드 (없으면 설치 후 사용)
# install.packages("tibble"); install.packages("dplyr")
library(tibble)
library(dplyr)
# 3-1) iris를 tibble 데이터프레임으로 변환
iris_tibble <- as_tibble(iris)
class(iris_tibble)
# "tbl_df" "tbl" "data.frame"
iris_tibble
# A tibble: 150 x 5 형태로 첫 10행만 깔끔하게 출력됨
출력 결과는 # A tibble: 150 × 5로 시작하며, 변수 타입이 <dbl>, <fct>처럼 헤더에 함께 표시된다. 이는 변수 유형을 한눈에 파악하기 위한 tibble의 특징이다.
3-2) 조건부 범주 변수 Length_Type 생성
dplyr::mutate()와 ifelse() 조합은 R에서 가장 일반적인 조건부 파생변수 생성 패턴이다. Sepal.Length가 5.5 이상이면 "Long", 미만이면 "Short"가 되며, 마지막에 as.factor()로 명시적으로 범주형으로 변환해 후속 그룹 연산에서 정렬·집계가 올바르게 동작하도록 한다.
# 3-2) Length_Type 범주형 변수 생성
iris_tibble <- iris_tibble %>%
mutate(Length_Type = ifelse(Sepal.Length >= 5.5, "Long", "Short"),
Length_Type = as.factor(Length_Type))
# 결과 일부 확인
head(iris_tibble)
table(iris_tibble$Length_Type)
# Long: 77, Short: 73
전체 150행 가운데 "Long" 그룹이 77개, "Short" 그룹이 73개로 거의 균형 잡힌 이분화가 이루어졌음을 알 수 있다. 5.5라는 임계값은 Sepal.Length의 중앙값(5.8) 부근에 위치한 합리적 분할점이다.
3-3) 그룹별 평균 산출 및 비교 해석
group_by()와 summarise()는 SQL의 GROUP BY와 유사한 역할을 한다. 각 그룹에 대해 Sepal.Width, Petal.Length, Petal.Width의 평균을 계산하면 다음과 같은 결과를 얻을 수 있다.
# 3-3) 그룹별 평균 계산
group_means <- iris_tibble %>%
group_by(Length_Type) %>%
summarise(Mean_Sepal_Width = mean(Sepal.Width),
Mean_Petal_Length = mean(Petal.Length),
Mean_Petal_Width = mean(Petal.Width))
group_means
계산 결과는 대략 다음과 같은 양상으로 나타난다. "Long" 그룹은 Sepal.Width 평균이 약 3.01, Petal.Length 평균이 약 4.72, Petal.Width 평균이 약 1.55로 나타난다. 반면 "Short" 그룹은 Sepal.Width 평균이 약 3.10으로 약간 더 크지만, Petal.Length는 약 2.10, Petal.Width는 약 0.50으로 현저히 작다. 해석하면, 꽃받침 길이가 긴 개체일수록 꽃잎의 길이·너비도 함께 커지는 양(+)의 상관이 분명히 존재한다. 이는 식물학적으로 setosa·versicolor·virginica 세 품종을 따라 형태가 점진적으로 커지는 경향과 일치한다. 흥미로운 점은 꽃받침의 너비(Sepal.Width)만은 두 그룹 사이에 거의 차이가 없거나 오히려 "Short" 쪽이 미세하게 크다는 것인데, 이는 setosa 품종이 꽃받침은 짧지만 폭은 넓은 특이 형태를 갖는 데서 비롯된다.
4번 문항: ggplot2 산점도와 회귀선·테마 적용
mtcars는 1974년 Motor Trend 잡지에 게재된 32대 차량의 제원·연비 데이터로, 회귀분석과 시각화 학습에 자주 활용된다. 본 문항에서는 중량과 연비의 관계를 산점도로 표현하면서 실린더 개수에 따라 색을 구분하고, 전체 추세선을 추가해 시각적으로 해석한다.
4-1) 기본 산점도 작성
# 4-1) 산점도 (cyl을 factor로 변환해 색상 구분)
library(ggplot2)
mtcars2 <- mtcars
mtcars2$cyl <- as.factor(mtcars2$cyl)
p <- ggplot(mtcars2, aes(x = wt, y = mpg, color = cyl)) +
geom_point(size = 3, alpha = 0.85)
p
color = cyl을 aes() 안에 넣은 이유는 색상이 데이터 변수(실린더 개수)와 매핑되어야 하기 때문이다. aes() 바깥에 두면 단순히 모든 점이 같은 색으로 칠해지므로 매핑이 일어나지 않는다. 점 크기(size = 3)와 투명도(alpha = 0.85)는 겹치는 점들을 식별하기 위한 시각적 조정이다.
4-2) 선형 회귀선 추가
geom_smooth(method = "lm", se = FALSE, color = "black") 한 줄로 전체 데이터의 선형 추세선을 추가할 수 있다. 신뢰구간 음영을 제외해야 하므로 se = FALSE를 명시하고, 색상은 문제 지시대로 검은색으로 설정한다. 색상 매핑(color = cyl)이 이미 점에 적용되어 있어도, 추세선은 그 매핑을 무시하고 전체 데이터를 하나의 회귀선으로 적합해야 하므로 aes() 바깥에서 색상을 지정한다.
# 4-2) 전체 데이터에 대한 회귀선 추가
p <- p +
geom_smooth(method = "lm", se = FALSE, color = "black")
p
산점도에서 점들은 우하향 패턴을 그리며, 검은 회귀선도 자연스럽게 우하향한다. 즉 차량 중량이 무거울수록 연비가 떨어진다는 직관적 관계가 시각적으로 명확히 드러난다. 실린더 4기통은 좌상단(가볍고 연비 좋음), 8기통은 우하단(무겁고 연비 나쁨)에 군집함을 색상으로도 확인할 수 있다.
4-3) 한글 라벨, theme_bw, 범례 위치 조정
# 4-3) 한글 라벨 + theme_bw + 범례 상단 배치
p_final <- p +
labs(title = "차량 중량과 연비의 관계",
x = "중량(1000파운드)",
y = "연비(mpg)",
color = "실린더 수") +
theme_bw() +
theme(legend.position = "top",
plot.title = element_text(hjust = 0.5, face = "bold"))
p_final
# 그래프 파일로 저장 (5번 문항에서 활용)
ggsave("mtcars_plot.png", plot = p_final,
width = 6, height = 4, dpi = 150)
theme_bw()는 배경을 흰색으로 바꾸고 격자선을 회색 톤으로 표현해 인쇄·문서 첨부에 적합한 깔끔한 테마이다. theme(legend.position = "top")은 범례를 그래프 상단으로 옮기며, plot.title의 hjust = 0.5는 제목을 가운데 정렬한다. 한글 출력이 깨질 경우 윈도우에서는 theme_bw(base_family = "맑은 고딕"), macOS에서는 "AppleGothic"을 지정하면 해결된다. ggsave()로 별도 PNG 파일로 저장해 두면 다음 문항의 HTML 문서에서도 동일 이미지를 재활용할 수 있다.
5번 문항: R Markdown으로 HTML 문서 생성
R Markdown(.Rmd)은 텍스트·코드·결과를 하나의 파일에 담아 HTML·PDF·Word로 일괄 출력할 수 있는 재현 가능 보고서 도구이다. 본 문항에서는 4번 문항의 그래프와 해석을 묶어 HTML로 출력하는 과정을 정리한다.
생성 절차
먼저 R 스튜디오 상단 메뉴에서 File → New File → R Markdown...을 선택하면 제목·저자·출력형식을 묻는 창이 뜬다. 출력 형식으로 HTML을 선택하면 기본 템플릿이 생성된다. 템플릿 상단의 YAML 헤더에 제목과 날짜, 출력 형식을 작성하고, 본문은 일반 마크다운 문법으로 작성하면서 R 코드 청크({r} ... )를 삽입한다. 작성 완료 후 상단의 Knit → Knit to HTML 버튼을 누르면 동일 이름의 HTML 파일이 작업 경로에 생성되며, 기본 브라우저에서 자동으로 미리보기가 열린다.
작성한 Rmd 본문 예시
---
title: "mtcars 데이터 분석 보고서"
author: "통계·데이터과학과"
date: "`r Sys.Date()`"
output: html_document
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
library(ggplot2)
```
## 1. 분석 개요
mtcars 데이터의 차량 중량(wt)과 연비(mpg) 간 관계를
실린더 수(cyl)에 따라 색으로 구분해 살펴본다.
## 2. 산점도와 회귀선
```{r plot, fig.width = 6, fig.height = 4}
mtcars2 <- mtcars
mtcars2$cyl <- as.factor(mtcars2$cyl)
ggplot(mtcars2, aes(x = wt, y = mpg, color = cyl)) +
geom_point(size = 3, alpha = 0.85) +
geom_smooth(method = "lm", se = FALSE, color = "black") +
labs(title = "차량 중량과 연비의 관계",
x = "중량(1000파운드)", y = "연비(mpg)",
color = "실린더 수") +
theme_bw() +
theme(legend.position = "top",
plot.title = element_text(hjust = 0.5, face = "bold"))
```
## 3. 해석
중량이 증가할수록 연비가 감소하는 명확한 음(-)의 관계가 관찰된다.
실린더 4기통 차량은 좌상단(가볍고 연비 우수), 8기통 차량은
우하단(무겁고 연비 저조)에 군집한다. 따라서 연비 개선을 위해서는
경량화와 동력원 효율화가 모두 중요함을 시사한다.
결과 확인 및 캡쳐 절차
Knit 실행이 끝나면 mtcars_report.html 파일이 생성된다. 브라우저에서 해당 HTML을 열어 제목·그래프·해석이 모두 정상 출력되는지 확인한 뒤, 윈도우에서는 Win + Shift + S, macOS에서는 Command + Shift + 4로 화면을 캡쳐하여 보고서 본문에 붙여 넣는다. 실제 제출 시에는 캡쳐 이미지 아래에 "R Markdown으로 생성한 HTML 보고서 화면" 같은 캡션을 함께 기입해 두면 채점자가 결과물의 출처와 의미를 한눈에 파악할 수 있다.
종합 정리 및 학습 시사점
이번 과제를 통해 R의 가장 기초적인 작업 환경 설정부터 내장 데이터 탐색, 사용자 정의 함수, tidyverse 기반 데이터 가공, ggplot2 시각화, 그리고 R Markdown 문서화까지 일련의 데이터 분석 파이프라인을 한 번에 경험할 수 있었다. 특히 summary() 출력 형태가 입력 변수의 자료형에 따라 어떻게 달라지는지(1번), 행렬 연산자 %*%와 *의 차이(2번), tibble의 안전한 출력 특성(3번), 그리고 aes() 내부·외부 색상 매핑의 차이(4번) 등은 초보자가 자주 혼동하는 핵심 포인트이다. 마지막으로 R Markdown은 분석과 보고를 분리하지 않고 통합 관리할 수 있게 해 주는 강력한 도구이며, 학습과 실무 모두에서 재현 가능한 보고서 작성 습관을 길러 준다.
학습 과정에서 얻은 또 다른 시사점은 함수의 안전성에 대한 감각이다. 2번 문항의 행렬 곱셈 함수에서 입력의 차원을 검증하는 stop() 구문을 넣은 것은 단순한 형식이 아니라, 잘못된 입력으로 인한 무성한 오류를 사전에 차단해 디버깅 시간을 줄이는 실질적인 장치이다. 또한 작업 폴더를 만들 때 dir.exists()로 존재 여부를 먼저 확인하고 recursive = TRUE를 사용한 것도 같은 맥락이다. 한 줄짜리 방어 코드가 전체 분석 파이프라인의 견고성을 좌우한다는 점은 데이터 분석가가 반드시 익혀야 할 자세이다.
또한 시각화 단계에서 색상 매핑을 변수와 연결하는지(aes() 내부) 또는 고정값으로 지정하는지(aes() 외부)에 따라 결과가 완전히 달라진다는 점은, ggplot2 문법의 철학을 이해해야만 자유롭게 다룰 수 있는 부분이다. 같은 맥락에서 회귀선의 신뢰구간(se) 출력 여부, 추세선 적합 방법(method = "lm" 외에도 "loess", "gam" 등)을 선택하는 결정은 자료의 성격과 발표 목적에 따라 신중히 이뤄져야 한다. 이번 과제는 그러한 옵션의 의미를 단순히 외우는 데 그치지 않고, 결과 화면과 연결지어 체득할 수 있는 좋은 기회였다.
참고문헌
- 한국방송통신대학교 출판문화원(2026). 『R컴퓨팅』. KNOU Press.
- R Core Team (2024). R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing, Vienna. https://www.R-project.org/
- Wickham, H., & Grolemund, G. (2017). R for Data Science. O'Reilly Media. https://r4ds.had.co.nz/
- Wickham, H. (2016). ggplot2: Elegant Graphics for Data Analysis (2nd ed.). Springer.
'방송통신대학교' 카테고리의 다른 글
| 한국방송통신대학교 e-비즈니스 기말과제 13개 강의 요약과 학습자의 시각으로 본 디지털 경제 (2) | 2026.05.28 |
|---|---|
| 한국방송통신대학교 HTML5 웹프로그래밍 기말과제 1~7장 짝수 연습문제 정리 (1) | 2026.05.28 |
| 한국방송통신대학교 1인미디어기획제작 기말과제 유튜브 채널 '침착맨' 분석 (2) | 2026.05.28 |
| 한국방송통신대학교 자바프로그래밍 기말과제 핵심 개념 평가용 객관식 10문항 설계 (2) | 2026.05.27 |
| 한국방송통신대학교 패션마케팅 기말과제 디지털 시대 패션상품 특성과 STP·4P 전략 종합 분석 (3) | 2026.05.27 |