R 프로그래밍 - tibble 에서 하나의 문자열을 여러개의 컬럼으로 나누기

tibble 을 사용해서 데이터를 구성하면, 수많은 tidyverse 의 패키지들을 이용해서 데이터를 정리하거나 원하는 형태로 가공하기 편하다. 그 중에서 하나의 문자열을 특정 구분자나 규칙(정규식)을 이용하여 여러개의 컬럼으로 나누는 방법을 설명하려 한다.

구분자가 있는 데이터인 경우

구분자가 있는 문자열의 샘플 데이터를 만들어 본다. 아래 t1 데이터는 _ 로 데이터가 구분되어 있다. tibble 함수를 사용해서, x 에는 알파벳, y 에는 숫자를 랜덤하게 셋팅해서 unite 함수로 문자열을 하나로 합쳤다.

library(tidyverse)

set.seed(129409238)
t1 = tibble(x = sample(LETTERS, 5),
            y = sample(0:9, 5)) %>% 
  unite("data", x:y, sep = "_")

# A tibble: 5 × 1
  data 
  <chr>
1 N_3  
2 F_8  
3 L_9  
4 O_6  
5 H_4 

구분자가 명확히 있는 경우, separate 함수를 사용하면 편하게 나눌 수 있다.

t1 %>% 
  separate(data, into=c('letter', 'number'), 
           sep = '_', remove = F)

# A tibble: 5 × 3
  data  letter number
  <chr> <chr>  <chr> 
1 N_3   N      3     
2 F_8   F      8     
3 L_9   L      9     
4 O_6   O      6     
5 H_4   H      4   

구분자가 없는 데이터의 경우

구분자가 없는 데이터를 샘플로 만들어 본다. 알파벳 한자리에 숫자 한자리를 조립한 형태이다.

set.seed(27840)
t2 = tibble(x = sample(LETTERS, 5),
            y = sample(0:9, 5)) %>% 
  unite("data", x:y, sep = "")

# A tibble: 5 × 1
  data 
  <chr>
1 F6   
2 A7   
3 U1   
4 P8   
5 Y9   

자리수 규칙이 명확하다면 문자열 위치로 각각 잘라서 컬럼을 추가하는 방법이 제일 간단하다. str_sub 함수를 사용한다.

t2 %>% 
  mutate(letter = str_sub(data, 1, 1)) %>% 
  mutate(number = str_sub(data, 2, 2))

# A tibble: 5 × 3
  data  letter number
  <chr> <chr>  <chr> 
1 F6    F      6     
2 A7    A      7     
3 U1    U      1     
4 P8    P      8     
5 Y9    Y      9     

문자열 위치만으로는 판단할 수 없다면, 정규식을 이용해서 처리해야 한다. 정규식관련문서는 구글링하면 많이 나오는데, R 패키지중에 stringr 에서 제공하는 cheatsheets 를 참고하면 좋다.

extract 함수의 특징은 into 로 컬럼을 나눌 갯수가 정해지면, regex 에서는 그 갯수만큼 () 안에 정규식을 각각 만들어 넣어야 한다. 그래서 아래와 같이 정규식을 각각 넣으면 하나의 문자열씩 분리되는 것을 볼 수 있다.

t2 %>% 
  extract(data, into = c('letter', 'number'), 
          regex = '(.)(.)', remove = F)

t2 %>% 
  extract(data, into = c('letter', 'number'), 
          regex = '(\\w)(\\w)', remove = F)

# 둘 다 같은 결과
# A tibble: 5 × 3
  data  letter number
  <chr> <chr>  <chr> 
1 F6    F      6     
2 A7    A      7     
3 U1    U      1     
4 P8    P      8     
5 Y9    Y      9     

[:digit:], [:alpha:] 같은 정규식스타일도 가능하다.

t2 %>% 
  extract(data, into = c('letter', 'number'), 
          regex = '([[:alpha:]])([[:digit:]])', remove = F)

# A tibble: 5 × 3
  data  letter number
  <chr> <chr>  <chr> 
1 F6    F      6     
2 A7    A      7     
3 U1    U      1     
4 P8    P      8     
5 Y9    Y      9     

조건이 불일치하게 되면, 아래와 같이 NA 만 출력되니 유의한다.

t2 %>% 
  extract(data, into = c('letter', 'number'), 
          regex = '([[:digit:]])([[:alpha:]])', remove = F)

# A tibble: 5 × 3
  data  letter number
  <chr> <chr>  <chr> 
1 F6    NA     NA    
2 A7    NA     NA    
3 U1    NA     NA    
4 P8    NA     NA    
5 Y9    NA     NA  
r 

더 보면 좋을 글들