R dplyr 데이터 정제 파이프라인: Raw Data에서 깨끗한 테이블까지
Problem
분석가는 데이터를 정제하기 위해 반복적인 R 반복문을 작성하느라 시간을 잃습니다. 그 결과 스크립트는 취약해지고, 컬럼은 일관성이 떨어지며, 새 파일이 도착할 때마다 반복 작업이 느려집니다.
Agitate
수작업 단계가 차곡차곡 쌓입니다. 잘못된 컬럼을 선택하거나, 변환을 잘못된 순서로 적용하거나, factor와 character 처리를 빠뜨리는 식입니다. 각 실수는 전체 재실행을 유발하고, 잘못된 지표가 downstream 대시보드로 전달될 위험을 만듭니다.
Solution
SQL 로직을 그대로 반영하면서 각 변환 과정을 읽기 쉽게 유지하는 일관된 dplyr 파이프라인을 도입하세요. 아래의 동사들은 컬럼 선택, 행 필터링, 새로운 피처 생성, 요약, 정렬, 안전한 조인을 모두 다루어, 모델링이나 시각화 전에 데이터가 tidy 상태가 되도록 도와줍니다.
Core pipeline in action
library(dplyr)
clean_sales <- raw_sales %>%
mutate(
region = as.character(region), # avoid accidental factor levels
revenue = price * quantity
) %>%
filter(!is.na(revenue), revenue > 0) %>%
select(order_id, region, revenue, channel, order_date) %>%
arrange(desc(revenue)) %>%
group_by(region, channel) %>%
summarise(
orders = n(),
revenue = sum(revenue),
avg_order = mean(revenue),
.groups = "drop"
)dplyr 동사와 SQL 대응 관계
| dplyr verb | Purpose | SQL analogy | Quick note |
|---|---|---|---|
select() | 컬럼 유지 또는 이름 변경 | SELECT col1, col2 | 빠르게 재정렬할 때 everything()을 사용하세요. |
filter() | 행 부분집합 | WHERE condition | 여러 값을 대상으로 할 때 %in%과 함께 사용하세요. |
mutate() | 컬럼 추가/변환 | SELECT col1, col2*2 AS col2x | 타입이 안정적인 조건 로직에는 if_else()를 선호하세요. |
summarise() + group_by() | 그룹별 지표 계산 | GROUP BY ... | 이후 단계를 위해 .groups = "drop"으로 그룹을 해제하세요. |
arrange() | 행 정렬 | ORDER BY | 내림차순에는 desc()를 사용하세요. |
left_join() | lookup으로 데이터 보강 | LEFT JOIN | 조인 키의 타입(character vs factor)이 동일한지 확인하세요. |
bind_rows() / bind_cols() | 테이블 세로 결합 또는 가로 확장 | UNION ALL / 컬럼 연결 | 세로 결합 시 스키마가 일치하는지 확인하세요. |
Joins vs. binds 한눈에 보기
# Join: add columns from a lookup table
sales_with_regions <- sales %>%
left_join(region_lookup, by = "region_id")
# Bind: stack identical schemas
all_sales <- bind_rows(sales_2023, sales_2024)factor 및 인코딩 함정 피하기
- 조인 전에 식별자 컬럼을
character로 변환하여 level 불일치를 방지합니다. - CSV를 읽을 때 암묵적으로 factor가 생겼다면
mutate(across(where(is.factor), as.character))를 사용합니다. - 그룹핑 전에
stringr::str_trim과tolower로 텍스트를 표준화해, 중복 범주가 생기지 않도록 합니다.
빠른 스타터 템플릿
prep_data <- function(df) {
df %>%
mutate(across(where(is.factor), as.character)) %>%
filter(!is.na(key_id)) %>%
distinct() %>%
select(key_id, everything())
}저장 전 체크리스트
- 컬럼이 명시적으로 선택되고 원하는 순서로 정리되었는지.
- 주요 조인이 타입을 맞추어 사용하며, 필요 시 명확한 접미사(
suffix = c(".src", ".lkp"))를 적용했는지. - 요약 이후 그룹이 해제되어, 다음 단계에서 예상치 못한 동작이 나오지 않는지.
- 출력 결과가 플롯이나 모델에 바로 사용할 수 있는 깨끗하고 읽기 쉬운 컬럼만 포함하는지.
관련 가이드
- 처음부터 tidy 테이블 만들기: Creating Dataframe in R
- 요약을 위한 데이터 그룹핑: Grouping in R with group_by()
- 시각화 준비: R ggplot2 Quickstart