데이터 불러오기와 정리
분석은 데이터를 불러오는 것부터 시작합니다. CSV 파일, 엑셀 파일, 내장 데이터셋을 R로 가져오는 방법을 익히고, tidyverse로 분석하기 좋은 형태로 정리합니다.
작업 디렉토리 설정
R은 파일을 읽고 쓸 때 작업 디렉토리를 기준으로 경로를 해석합니다. 현재 작업 디렉토리를 먼저 확인합니다.
getwd() # 현재 작업 디렉토리 확인
setwd("경로") # 작업 디렉토리 변경
# RStudio를 사용한다면 프로젝트(.Rproj)를 만들면
# 자동으로 프로젝트 폴더가 작업 디렉토리가 됩니다.
# 프로젝트를 쓰는 것을 권장합니다.
CSV 파일 불러오기
가장 많이 쓰는 형식입니다. readr 패키지의 read_csv()는 base R의 read.csv()보다 빠르고, 결과가 tibble로 반환됩니다.
library(readr)
# CSV 불러오기
df <- read_csv("data/students.csv")
# 인코딩 지정 — 한글이 깨질 때
df <- read_csv("data/students.csv", locale = locale(encoding = "EUC-KR"))
# 특정 열 자료형 지정
df <- read_csv("data/students.csv",
col_types = cols(
id = col_character(),
score = col_double(),
date = col_date(format = "%Y-%m-%d")
))
# 확인
glimpse(df) # 열 이름, 자료형, 미리보기
head(df) # 처음 6행
read_csv()와 read.csv()의 차이를 정리하면 이렇습니다.
| 비교 항목 | read.csv() (base R) | read_csv() (readr) |
|---|---|---|
| 반환 형식 | data.frame | tibble |
| 속도 | 보통 | 빠름 |
| 문자열 처리 | factor 변환 | 문자 유지 |
| 열 이름 | 공백을 . 으로 변환 | 그대로 유지 |
엑셀 파일 불러오기
library(readxl)
# 기본 — 첫 번째 시트를 읽습니다
df <- read_excel("data/sales.xlsx")
# 시트 지정
df <- read_excel("data/sales.xlsx", sheet = "2024년")
df <- read_excel("data/sales.xlsx", sheet = 2) # 두 번째 시트
# 범위 지정
df <- read_excel("data/sales.xlsx", range = "A1:D50")
# 헤더가 없는 경우
df <- read_excel("data/sales.xlsx", col_names = FALSE)
# 시트 목록 확인
excel_sheets("data/sales.xlsx")
R 내장 데이터셋
R에는 연습용 데이터셋이 기본으로 포함되어 있습니다. 파일 없이 바로 쓸 수 있어서 이 책에서도 자주 활용합니다.
# 내장 데이터셋 목록
data()
# 자주 쓰는 데이터셋
data(iris) # 붓꽃 데이터 (150행, 5열)
data(mtcars) # 자동차 데이터 (32행, 11열)
data(airquality) # 대기질 데이터 (153행, 6열)
head(iris)
str(mtcars)
데이터 탐색
데이터를 불러왔다면 분석 전에 반드시 데이터 구조를 파악합니다.
library(tidyverse)
df <- iris
# 기본 탐색
dim(df) # 행과 열 수: 150 5
nrow(df) # 행 수
ncol(df) # 열 수
names(df) # 열 이름
str(df) # 구조 요약
glimpse(df) # tidyverse 스타일 구조 요약
summary(df) # 각 열의 기술통계량
# 결측값 확인
sum(is.na(df)) # 전체 결측값 수
colSums(is.na(df)) # 열별 결측값 수
df %>% summarise(across(everything(), ~sum(is.na(.))))
filter — 행 조건 필터링
library(dplyr)
df <- mtcars
# 단일 조건
df %>% filter(cyl == 6) # 실린더가 6인 차
df %>% filter(mpg > 20) # 연비가 20 초과
# 복합 조건 — & (AND), | (OR)
df %>% filter(cyl == 4 & mpg > 25)
df %>% filter(cyl == 4, mpg > 25) # 쉼표도 AND로 동작합니다
df %>% filter(cyl == 4 | cyl == 6)
df %>% filter(cyl %in% c(4, 6)) # %in%으로 여러 값 비교
# 결측값 필터
df %>% filter(!is.na(hp)) # hp가 결측이 아닌 행
df %>% filter(is.na(hp)) # hp가 결측인 행
mutate — 열 추가와 변환
df <- mtcars
# 새 열 추가
df <- df %>%
mutate(
kpl = mpg * 0.4251, # 마일/갤런 → 킬로미터/리터 변환
weight_kg = wt * 453.6, # 파운드 → kg
high_power = hp > 150 # 고출력 여부 (논리값)
)
# 기존 열 변환
df <- df %>%
mutate(cyl = as.factor(cyl)) # 숫자를 범주형으로 변환
# case_when — 여러 조건 분기 (ifelse의 확장판)
df <- df %>%
mutate(
mpg_grade = case_when(
mpg >= 25 ~ "상",
mpg >= 18 ~ "중",
TRUE ~ "하" # 나머지 모두
)
)
select — 열 선택
df <- iris
# 열 이름으로 선택
df %>% select(Sepal.Length, Species)
# 열 제외
df %>% select(-Species)
# 범위 선택
df %>% select(Sepal.Length:Petal.Length)
# 헬퍼 함수
df %>% select(starts_with("Sepal")) # "Sepal"로 시작하는 열
df %>% select(ends_with("Width")) # "Width"로 끝나는 열
df %>% select(contains("Length")) # "Length"를 포함하는 열
df %>% select(where(is.numeric)) # 숫자형 열만
arrange — 정렬
df <- mtcars
df %>% arrange(mpg) # 오름차순
df %>% arrange(desc(mpg)) # 내림차순
df %>% arrange(cyl, desc(mpg)) # cyl 오름차순, 같은 cyl 안에서 mpg 내림차순
group_by + summarise — 그룹별 집계
분석에서 가장 자주 쓰는 조합입니다.
df <- iris
df %>%
group_by(Species) %>%
summarise(
n = n(),
mean_length = mean(Sepal.Length),
sd_length = sd(Sepal.Length),
min_length = min(Sepal.Length),
max_length = max(Sepal.Length)
)
결과를 보면 종별로 꽃받침 길이가 다르다는 것을 바로 알 수 있습니다.
결측값 처리
현실 데이터에는 결측값이 거의 항상 있습니다.
# 예제 데이터 생성
df <- data.frame(
name = c("A", "B", "C", "D", "E"),
score = c(85, NA, 78, 95, NA),
age = c(20, 22, NA, 20, 23)
)
# 결측값 제거 — 결측값이 있는 행 전체 삭제
df_clean <- df %>% drop_na() # 모든 열 기준
df_clean <- df %>% drop_na(score) # score 열만 기준
# 결측값 대체
df_filled <- df %>%
mutate(
score = replace_na(score, mean(score, na.rm = TRUE)), # 평균으로 대체
age = replace_na(age, 21) # 특정값으로 대체
)
데이터 형 변환
df <- data.frame(
id = c("001", "002", "003"),
score = c("85", "92", "78"),
date = c("2024-01-15", "2024-01-16", "2024-01-17")
)
df <- df %>%
mutate(
score = as.numeric(score), # 문자 → 숫자
date = as.Date(date), # 문자 → 날짜
id = as.factor(id) # 문자 → 범주형
)
str(df)
전체 흐름 예시
데이터를 불러오고 정리하는 전형적인 흐름을 한눈에 봅니다.
library(tidyverse)
# 1. 데이터 불러오기
raw <- read_csv("data/exam_results.csv")
# 2. 탐색
glimpse(raw)
summary(raw)
colSums(is.na(raw))
# 3. 정리
clean <- raw %>%
filter(!is.na(score)) %>% # 점수 없는 행 제거
mutate(
score = as.numeric(score),
grade = case_when(
score >= 90 ~ "A",
score >= 80 ~ "B",
score >= 70 ~ "C",
TRUE ~ "D"
)
) %>%
select(id, name, score, grade) %>% # 필요한 열만 선택
arrange(desc(score)) # 점수 높은 순 정렬
# 4. 결과 저장
write_csv(clean, "data/exam_results_clean.csv")
데이터 정리가 끝났다면 이제 숫자로 데이터를 요약하는 기술통계 단계로 넘어갑니다.