iBetter Books
수정

Ch 05. 데이터 변환 (mutate, case_when)

원본 데이터에는 없지만 분석에 꼭 필요한 변수가 있습니다. 연비를 킬로미터로 환산한 값, 나이를 구간으로 나눈 범주, 두 열을 조합해서 만든 점수. 이런 파생 변수를 만드는 것이 mutate()의 역할입니다.

mutate()로 새 열 만들기

mpg 데이터에서 도심 연비(cty)와 고속도로 연비(hwy)의 평균을 구해 새 열로 추가합니다.

library(tidyverse)

mpg %>%
  mutate(avg_mpg = (cty + hwy) / 2) %>%
  select(manufacturer, model, cty, hwy, avg_mpg)
# A tibble: 234 × 5
   manufacturer model      cty   hwy avg_mpg
   <chr>        <chr>    <int> <int>   <dbl>
 1 audi         a4          18    29    23.5
 2 audi         a4          21    29    25  
 3 audi         a4          20    31    25.5
...

mutate() 안에서 만든 열을 같은 mutate() 안에서 바로 참조할 수도 있습니다.

mpg %>%
  mutate(
    avg_mpg  = (cty + hwy) / 2,
    avg_kmpl = avg_mpg * 0.4251    # 미국 mpg → 한국 km/L 환산
  ) %>%
  select(manufacturer, model, avg_mpg, avg_kmpl)
# A tibble: 234 × 4
   manufacturer model      avg_mpg avg_kmpl
   <chr>        <chr>        <dbl>    <dbl>
 1 audi         a4            23.5     9.99
 2 audi         a4            25      10.6 
...

기존 열 변환

mutate()는 새 열을 추가할 뿐 아니라 기존 열을 덮어쓸 수도 있습니다.

# 배기량을 소수점 없이 반올림
mpg %>%
  mutate(displ = round(displ, 0)) %>%
  select(manufacturer, model, displ)
# 제조사 이름을 대문자로 변환
mpg %>%
  mutate(manufacturer = toupper(manufacturer)) %>%
  select(manufacturer, model)

transmute()로 선택하며 변환

transmute()mutate()와 달리 지정한 열만 남깁니다. mutate()select()를 쓰는 것과 같습니다.

mpg %>%
  transmute(
    manufacturer,
    model,
    avg_kmpl = (cty + hwy) / 2 * 0.4251
  )
# A tibble: 234 × 3
   manufacturer model      avg_kmpl
   <chr>        <chr>         <dbl>
 1 audi         a4             9.99
 2 audi         a4            10.6 
...

case_when()으로 조건 분기

case_when()은 여러 조건에 따라 다른 값을 할당합니다. if-else 체인을 깔끔하게 표현합니다.

mpghwy 연비를 기준으로 등급을 매깁니다.

mpg %>%
  mutate(
    grade = case_when(
      hwy >= 35 ~ "A",
      hwy >= 28 ~ "B",
      hwy >= 22 ~ "C",
      TRUE      ~ "D"
    )
  ) %>%
  select(manufacturer, model, hwy, grade)
# A tibble: 234 × 4
   manufacturer model      hwy grade
   <chr>        <chr>    <int> <chr>
 1 audi         a4          29 B    
 2 audi         a4          29 B    
 3 audi         a4          31 B    
 4 audi         a4          30 B    
 5 audi         a4          26 C    
...

case_when()의 구조는 조건 ~ 값 쌍을 순서대로 평가합니다. 위에서부터 처음으로 TRUE가 되는 조건의 값이 사용됩니다. 마지막 TRUE ~ "D"는 모든 조건에 해당하지 않을 때의 기본값입니다.

문자 조건도 자유롭게 쓸 수 있습니다.

mpg %>%
  mutate(
    origin = case_when(
      manufacturer %in% c("audi", "volkswagen")         ~ "독일",
      manufacturer %in% c("toyota", "honda", "nissan")  ~ "일본",
      manufacturer %in% c("chevrolet", "ford", "dodge") ~ "미국",
      TRUE ~ "기타"
    )
  ) %>%
  select(manufacturer, model, origin)
# A tibble: 234 × 3
   manufacturer model      origin
   <chr>        <chr>      <chr> 
 1 audi         a4         독일  
 2 audi         a4         독일  
...

across()로 여러 열에 동시 적용

같은 변환을 여러 열에 적용할 때 across()를 사용합니다.

# cty와 hwy를 동시에 km/L로 변환
mpg %>%
  mutate(across(c(cty, hwy), ~ .x * 0.4251)) %>%
  select(manufacturer, model, cty, hwy)
# A tibble: 234 × 4
   manufacturer model      cty   hwy
   <chr>        <chr>    <dbl> <dbl>
 1 audi         a4        7.65  12.3
 2 audi         a4        8.93  12.3
...

across()의 두 번째 인수는 함수입니다. .x는 현재 처리 중인 열을 가리킵니다. ~(tilde)로 시작하는 람다 함수 형식입니다.

열 이름 패턴으로 선택할 수도 있습니다.

# 숫자형 열 모두에 반올림 적용
mpg %>%
  mutate(across(where(is.double), round, 1))

# starts_with로 선택
mpg %>%
  mutate(across(starts_with("c"), ~ .x * 2))

실전 예제: 여러 변환 한 번에

starwars 데이터셋으로 종합 실습을 해봅니다. 스타워즈 캐릭터 데이터입니다.

starwars %>%
  filter(!is.na(height), !is.na(mass)) %>%
  mutate(
    bmi       = mass / (height / 100)^2,
    bmi_grade = case_when(
      bmi < 18.5 ~ "저체중",
      bmi < 25   ~ "정상",
      bmi < 30   ~ "과체중",
      TRUE       ~ "비만"
    ),
    height_m  = height / 100
  ) %>%
  select(name, height_m, mass, bmi, bmi_grade) %>%
  arrange(desc(bmi))
# A tibble: 59 × 5
   name                  height_m  mass   bmi bmi_grade
   <chr>                    <dbl> <dbl> <dbl> <chr>    
 1 Jabba Desilijic Tiure     1.75  1358  443. 비만     
 2 Dud Bolt                  0.94    45  50.9 비만     
 3 Yoda                      0.66    17  39.0 비만     
...

키, 체중, BMI, BMI 등급이 한 파이프 안에서 순서대로 처리됩니다. 코드가 분석의 흐름을 그대로 표현합니다.

열을 만드는 방법을 익혔으니, 이제 그룹별로 요약하는 방법을 배웁니다. 데이터 분석에서 가장 자주 쓰는 패턴입니다.