Skip to content

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 verbPurposeSQL analogyQuick 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_trimtolower로 텍스트를 표준화해, 중복 범주가 생기지 않도록 합니다.

빠른 스타터 템플릿

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"))를 적용했는지.
  • 요약 이후 그룹이 해제되어, 다음 단계에서 예상치 못한 동작이 나오지 않는지.
  • 출력 결과가 플롯이나 모델에 바로 사용할 수 있는 깨끗하고 읽기 쉬운 컬럼만 포함하는지.

관련 가이드