컬럼의 텍스트 나누기

엑셀의 “텍스트 나누기” 기능을 R에서 구현하는 방법을 설명하고자 한다.
여기서 가장 중요한 함수는 [ 함수이다.
일반적으로 리스트 데이터 유형을 다룰 때 주로 사용하는 기호인데, [기호는 실제로 함수이다. 일반적으로 리스트 원소를 추출할 때 mylist[1]과 같이 작성하는데, 실제로 ’내부’에서 일어나는 일은 측면 대괄호 안에 있는 번호 또는 이름이 지정된 항목 (이 경우 하나만)이 추출되어 ’mylist’의 [함수에 전달된다. 여기서는 첫 번째 함수 인수이므로 다음과 같다고 할 수 있다.
[(list, 1) 즉, 이것은 list[[1]]과 같음.

2020.09.17 수정

[ 를 대신하는 magtrittr::extract() 추가

0. 데이터

library(dplyr)   # %>% 연산자를 사용하기 위해 필요함.
library(stringr) # str_split 등 string을 처리하기 위해 필요함.

v1 <- c("ASUSTeK Computer Inc.","ASUSTeK Computer Inc.","Altos Computing Inc.","China Academy of Technology","Cisco Systems","Dell Inc" )
v2 <- c(72,72,40,96,224,96)
data<-data.frame(v1,v2)
data %>% str()
'data.frame':   6 obs. of  2 variables:
 $ v1: chr  "ASUSTeK Computer Inc." "ASUSTeK Computer Inc." "Altos Computing Inc." "China Academy of Technology" ...
 $ v2: num  72 72 40 96 224 96
data
                           v1  v2
1       ASUSTeK Computer Inc.  72
2       ASUSTeK Computer Inc.  72
3        Altos Computing Inc.  40
4 China Academy of Technology  96
5               Cisco Systems 224
6                    Dell Inc  96

1. 문자열을 공백을 기준으로 나누어서 첫번째 문자열만 삽입

v1 컬럼의 문자열을 공백(white space)을 기준으로 나누어서 첫번째 문자열만 ’Company’라는 컬럼에 넣어보자.

1.1 문자열을 공백을 기준으로 분리

k<-data$v1 %>% str_split("\\s+")
k
[[1]]
[1] "ASUSTeK"  "Computer" "Inc."    

[[2]]
[1] "ASUSTeK"  "Computer" "Inc."    

[[3]]
[1] "Altos"     "Computing" "Inc."     

[[4]]
[1] "China"      "Academy"    "of"         "Technology"

[[5]]
[1] "Cisco"   "Systems"

[[6]]
[1] "Dell" "Inc" 

출력을 확인하면 리스트 유형임을 알 수 있다.
이 리스트의 원소를 추출하는 방법은 여러가지가 있다.
대표적으로 다음과 같다.

k[[1]]
[1] "ASUSTeK"  "Computer" "Inc."    
k[[1]][[1]]
[1] "ASUSTeK"
k[[4]][[4]]
[1] "Technology"

그런데, %>% 연산자를 사용할 때는 이런 방식으로 추출이 불가능하고, 특히 각 리스트의 1th 원소들만 한번에 추출하는 것은 불가능하다.
예를 들어 다음과 같이 수행하면 에러가 발생한다.

data$v1 %>% str_split("\\s+") %>% .[[1]] # 에러 발생하지 않음
[1] "ASUSTeK"  "Computer" "Inc."    
data$v1 %>% str_split("\\s+") %>% .[[1]][[1]] # 에러 발생
Error in .[[.[[1]], 1]]: 첨자들의 개수가 올바르지 않습니다
data$v1 %>% str_split("\\s+") %>% .[[1]] %>% .[[1]] # 에러 발생하지 발생
[1] "ASUSTeK"
data$v1 %>% str_split("\\s+") %>% .[[4]] %>% .[[4]] # 에러 발생하지 않음
[1] "Technology"
data$v1 %>% str_split("\\s+") %>% .[[]] %>% .[[1]]  # NULL 값 출력
NULL

각 리스트의 1th 원소들만 추출하는 방법이 필요하다.
여기에 사용된 것이 [ 기호의 함수이다.
아래를 보자.

data %>% 
  mutate(Company=data$v1 %>%       # "Company" 컬럼을 아래의 내용으로 추가
           str_split("\\s+") %>%   # 공백을 기준으로 분리
           sapply(`[`,1),          # 리스트의 첫번째 항목에 대해 sapply 적용
         .before=v1)               # "Company" 컬럼을 v1 컬럼 이전에 삽입
  Company                          v1  v2
1 ASUSTeK       ASUSTeK Computer Inc.  72
2 ASUSTeK       ASUSTeK Computer Inc.  72
3   Altos        Altos Computing Inc.  40
4   China China Academy of Technology  96
5   Cisco               Cisco Systems 224
6    Dell                    Dell Inc  96
  1. str_split(x, “pattern”): 여기서 “pattern”에 적용된 “\s+”는 (+:여러 개의)공백(\s)을 기준으로 분리하라는 의미다.
  2. str_split()는 list를 반환하는데, 이 리스트를 처리하기 위해 아래와 같은 [ 기호의 함수를 사용한다.
  3. sapply([,1): 이 의미는 리스트의 첫번째 항목에 대해 sapply를 적용하라는 뜻이다. 리스트의 두번째 항목을 적용하고 싶을 때는 2를 사용하면 된다.

2. 문자열을 공백을 기준으로 나누어서 모든 문자열을 각각의 컬럼에 삽입

이번에는 v1 컬럼의 각 문자열을 공백을 기준으로 분리해서 분리된 문자열을 모두 각각의 컬럼에 삽입해 보자.
먼저 리스트의 구조가 어떻게 되는지 위에서 다시한번 살펴보자.

data$v1 %>% str_split("\\s+") %>% sapply(length)   # 각 리스트의 원소 개수 파악
[1] 3 3 3 4 2 2
data$v1 %>% str_split("\\s+") %>% sapply(length) %>% max() # 최대 원소 개수 파악
[1] 4

최대 원소 개수가 4개다. 따라서 4개의 컬럼이 추가될 것이다.

data %>% 
  mutate(com1=data$v1 %>%      
           str_split("\\s+") %>%  
           sapply(`[`,1), 
         com2=data$v1 %>%      
           str_split("\\s+") %>%   
           sapply(`[`,2),
         com3=data$v1 %>%       
           str_split("\\s+") %>%   
           sapply(`[`,3), 
         com4=data$v1 %>%       
           str_split("\\s+") %>%   
           sapply(`[`,4), 
         .before=v1)               # "Company" 컬럼을 v1 컬럼 이전에 삽입
     com1      com2 com3       com4                          v1  v2
1 ASUSTeK  Computer Inc.       <NA>       ASUSTeK Computer Inc.  72
2 ASUSTeK  Computer Inc.       <NA>       ASUSTeK Computer Inc.  72
3   Altos Computing Inc.       <NA>        Altos Computing Inc.  40
4   China   Academy   of Technology China Academy of Technology  96
5   Cisco   Systems <NA>       <NA>               Cisco Systems 224
6    Dell       Inc <NA>       <NA>                    Dell Inc  96

위와 같이 v1 컬럼의 문자열이 공백을 기준으로 분리되어 com1, com2, com3, com4에 삽입되었고, 문자열이 없는 셀은 NA로 처리된 것을 볼 수 있다.

추가

library(magrittr)
data %>% 
  mutate(com1=data$v1 %>%      
           str_split("\\s+") %>%  
           sapply(extract,1), 
         com2=data$v1 %>%      
           str_split("\\s+") %>%   
           sapply(extract,2),
         com3=data$v1 %>%       
           str_split("\\s+") %>%   
           sapply(extract,3), 
         com4=data$v1 %>%       
           str_split("\\s+") %>%   
           sapply(extract,4), 
         .before=v1)               
     com1      com2 com3       com4                          v1  v2
1 ASUSTeK  Computer Inc.       <NA>       ASUSTeK Computer Inc.  72
2 ASUSTeK  Computer Inc.       <NA>       ASUSTeK Computer Inc.  72
3   Altos Computing Inc.       <NA>        Altos Computing Inc.  40
4   China   Academy   of Technology China Academy of Technology  96
5   Cisco   Systems <NA>       <NA>               Cisco Systems 224
6    Dell       Inc <NA>       <NA>                    Dell Inc  96