패키지 stringr 아주 편리한 문자열 도구

R 사용자들의 가장 큰 불만 중의 하나는...?
아마 상당히 불편한 character 데이터 처리 방법이라고 생각합니다.

문자열을 가지고 흔히 하는 일을 생각해봅시다.

  1. 문자열 합치고 나누기
  2. 문자열의 어떤 부분을 추출하기
  3. 분자열의 일부를 바꾸기
  4. 빈 문자들(white space) 지우기
  5. 공백 문자 채우기
이외에도
  1. 정규식을 적용하여 어떤 패턴이 있나 확인하기
  2. 정규식을 적용하여 어떤 패턴에 해당되는 부분 추출하기
정도는 해줘야 하지요.

R의 {base}는 sub(), gsub(), regex() 등의 함수를 제공합니다만... 너무 느리고 사용이 어렵습니다. 우리의 구세주는 Rice University 통계학과의 Hadley Wickham 교수입니다. 그가 개발한 {stringr}은 빠르고 간편합니다. 이제 구체적인 사례들을 생각하며 {stringr}을 배워봅시다.

경우에 따른 stringr 함수들

문자열 관련

  • str_c(): 합치기
  • str_count(): 세어 보기
  • str_dup(): 반복해서 덧붙이기
  • str_length(): 문자 길이
  • str_pad(): 빈 문자 속에 끼우기
  • str_split(): 나누기
  • str_split_fixed(): 고정해서 나누기
  • str_trim(): 빈문자들 제거
  • str_sub(): 시작과 끝 위치에 해당되는 부분 끊어오기
  • word(): 단어 추출

패턴 추출


  • str_detect(): 패턴 있나 검사
  • str_extract(): 패턴에 해당되는 것 추출
  • str_extract_all(): 패턴에 해당되는 것 모두 추출해서 list로 반환
  • str_locate(): 패턴의 위치 추출
  • str_locate_all(): 패턴의 위치 모두 추출해서 list로 반환
  • str_match(): 해당되는 그룹 패턴 추출
  • str_replace(): 패턴에 해당되는 부분 교체
  • str_replace_all(): 패턴에 해당되는 부분 모두 교체

패턴

  • fixed(): 정규식 패턴 무시하기
  • ignore_case(): 패턴에 대소문자 무시하기
  • perl(): POSIX 정규식 대신 Perl의 정규식 표현 쓰기

연습 과제

백문이 불여일타.
다음 과제들을 많이 연습해서 함수들의 사용법을 익혀 봅시다.



함수: fixed
인자: string 
설명: 정규식을 사용하지 않은 고정된 패턴, 속도 높이는 효과

> pattern <- "a.b"
> strings <- c("abc","a.b")
> str_detect(strings,pattern)
[1] FALSE  TRUE
> str_detect(strings, fixed(pattern))
[1] FALSE  TRUE

함수: ignore.case
인자: string
설명: 패턴을 matching할 때 대소문자 구분을 하지 않도록 함

> str_detect(strings, ignore.case(pattern))
[1] FALSE  TRUE

함수: perl
인자: 펄정규식
설명: 펄 정규식을 사용할 수 있도록 한다.

> pattern <- "(?x)a.b"
> strings <- c("abb","a.b")
> str_detect(strings,perl(pattern))
[1] TRUE TRUE
함수: str_c
인자: 연결할 문자 벡터들
        sep: 연결하는 문자 벡터들 사이를 이어준다.
        collapse: 연결한 다음 한 문자열로 합칠 때 삽입할 문자
설명: 벡터 연산을 하여 문자들을 연결시킨다.

> str_c("Letter: ",letters)
 [1] "Letter: a" "Letter: b" "Letter: c" "Letter: d" "Letter: e" "Letter: f" "Letter: g"
 [8] "Letter: h" "Letter: i" "Letter: j" "Letter: k" "Letter: l" "Letter: m" "Letter: n"
[15] "Letter: o" "Letter: p" "Letter: q" "Letter: r" "Letter: s" "Letter: t" "Letter: u"
[22] "Letter: v" "Letter: w" "Letter: x" "Letter: y" "Letter: z"
> str_c(letters, " is for", "...")
 [1] "a is for..." "b is for..." "c is for..." "d is for..." "e is for..." "f is for..."
 [7] "g is for..." "h is for..." "i is for..." "j is for..." "k is for..." "l is for..."
[13] "m is for..." "n is for..." "o is for..." "p is for..." "q is for..." "r is for..."
[19] "s is for..." "t is for..." "u is for..." "v is for..." "w is for..." "x is for..."
[25] "y is for..." "z is for..."

> str_c("Letter", letters, sep = ": ")
 [1] "Letter: a" "Letter: b" "Letter: c" "Letter: d" "Letter: e" "Letter: f" "Letter: 
...
> str_c(letters, collapse = ", ")
[1] "a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z"



함수: str_count()
인자: string: 문자열
        pattern: 패턴
설명: 패턴이 몇 개 일치하나 숫자를 반환
> fruit <- c("apple", "banana", "pear", "pineapple")
> str_count(fruit, "a")
[1] 1 3 1 1
# 각 문자 벡터에서 일치하는 것의 수를 반환
> str_count(fruit, "p")
[1] 2 0 1 3
> str_count(fruit, "e")
[1] 1 0 1 2
> str_count(fruit, c("a", "b", "p", "p"))
[1] 1 1 1 3
함수: str_detect
인자: string, pattern
설명: 패턴이 존재하면 TRUE, 아니면 FALSE
> fruit <- c("apple", "banana", "pear", "pinapple")

> str_detect(fruit, "a")
[1] TRUE TRUE TRUE TRUE
> str_detect(fruit, "^a")
[1]  TRUE FALSE FALSE FALSE
> str_detect(fruit, "a$")
[1] FALSE  TRUE FALSE FALSE
> str_detect(fruit, "b")
[1] FALSE  TRUE FALSE FALSE
> str_detect(fruit, "[aeiou]")
[1] TRUE TRUE TRUE TRUE

함수: str_dup
인자: string, times
설명: times 벡터에 지정된 것만큼 원소를 반복해서 뒤에 붙인다.

> fruit <- c("apple", "pear", "banana")
> str_dup(fruit, 2)
[1] "appleapple"   "pearpear"     "bananabanana"
> str_dup(fruit, 1:3)
[1] "apple"              "pearpear"           "bananabananabanana"
> str_c("ba", str_dup("na", 0:5))
[1] "ba"           "bana"         "banana"       "bananana"     "banananana"  
[6] "bananananana"

함수: str_extract
인자: string, pattern
설명: 일치하는 최초의 패턴을 뽑아 보여준다.

> str_extract(shopping_list, "\\d")
[1] "4" NA  NA  "2"
> str_extract(shopping_list, "[a-z]+")
[1] "apples" "flour"  "sugar"  "milk"  
> str_extract(shopping_list, "[a-z]{1,4}")
[1] "appl" "flou" "suga" "milk"
> str_extract(shopping_list, "\\b[a-z]{1,4}\\b")
[1] NA     NA     NA     "milk"

함수: str_extract_all
인자: string, pattern
설명: 일치하는 모든 경우를 다 뽑아 list 객체로 반환한다.

> shopping_list <- c("apples x4", "bag of flour", "bag of sugar", "milk x2")
> str_extract_all(shopping_list, "[a-z]+")
[[1]]
[1] "apples" "x"     

[[2]]
[1] "bag"   "of"    "flour"

[[3]]
[1] "bag"   "of"    "sugar"

[[4]]
[1] "milk" "x"   

> str_extract_all(shopping_list, "\\b[a-z]+\\b")
[[1]]
[1] "apples"

[[2]]
[1] "bag"   "of"    "flour"

[[3]]
[1] "bag"   "of"    "sugar"

[[4]]
[1] "milk"

> str_extract_all(shopping_list, "\\d")
[[1]]
[1] "4"

[[2]]
character(0)

[[3]]
character(0)

[[4]]
[1] "2"

함수: str_length
인자: string
설명: 벡터의 문자 길이를 반환한다.

> str_length(letters)
 [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
> str_length(c("i", "like", "programming", NA))
[1]  1  4 11 NA
함수: str_locate
인자: string, pattern
설명: pattern의 시작과 끝 위치를 반환한다.
> fruit <- c("apple", "banana", "pear", "pinapple")
> str_locate(fruit, "a")
     start end
[1,]     1   1
[2,]     2   2
[3,]     3   3
[4,]     4   4
> str_locate(fruit, "an")
     start end
[1,]    NA  NA
[2,]     2   3
[3,]    NA  NA
[4,]    NA  NA
> str_locate(fruit, "e")
     start end
[1,]     5   5
[2,]    NA  NA
[3,]     2   2
[4,]     8   8
> str_locate(fruit, c("a", "b", "p", "p"))
     start end
[1,]     1   1
[2,]     1   1
[3,]     1   1
[4,]     1   1
함수: str_locate_all

매치되는 것들의 전체 리스트를 반환한다.

함수: str_match
인자: string,pattern
설명: ()로 묶인 패턴 그룹을 뽑아 matrix 반환
> strings <- c(" 219 733 8965", "329-293-8753 ", "banana", "595 794 7569",
+              "387 287 6718", "apple", "233.398.9187 ", "482 952 3315",
+              "239 923 8115", "842 566 4692", "Work: 579-499-7527", "$1000",
+              "Home: 543.355.3679")
> phone <- "([2-9][0-9]{2})[- .]([0-9]{3})[- .]([0-9]{4})"
> str_extract(strings, phone)
 [1] "219 733 8965" "329-293-8753" NA             "595 794 7569" "387 287 6718"
 [6] NA             "233.398.9187" "482 952 3315" "239 923 8115" "842 566 4692"
[11] "579-499-7527" NA             "543.355.3679"
> str_match(strings, phone)
      [,1]           [,2]  [,3]  [,4]  
 [1,] "219 733 8965" "219" "733" "8965"
 [2,] "329-293-8753" "329" "293" "8753"
 [3,] NA             NA    NA    NA    
 [4,] "595 794 7569" "595" "794" "7569"
 [5,] "387 287 6718" "387" "287" "6718"
 [6,] NA             NA    NA    NA    
 [7,] "233.398.9187" "233" "398" "9187"
 [8,] "482 952 3315" "482" "952" "3315"
 [9,] "239 923 8115" "239" "923" "8115"
[10,] "842 566 4692" "842" "566" "4692"
[11,] "579-499-7527" "579" "499" "7527"
[12,] NA             NA    NA    NA    
[13,] "543.355.3679" "543" "355" "3679"

str_match_all()도 있다. 
함수: str_pad
인자: string, width, side
        side = c("left","right","both") 빈 칸의 기준 위치를 정함
설명: 빈 칸을 넣어서 적절히 배치한다.

> rbind(
+   str_pad("hadley", 30, "left"),
+   str_pad("hadley", 30, "right"),
+   str_pad("hadley", 30, "both")
+ )
     [,1]                            
[1,] "                        hadley"
[2,] "hadley                        "
[3,] "            hadley            "
함수: str_replace, str_replace_all
인자: string, pattern, replace
설명: 정규식에 해당되는 부분을 replace로 치환한다.

> fruits <- c("one apple", "two pears", "three bananas")
> str_replace(fruits, "[aeiou]", "-")
[1] "-ne apple"     "tw- pears"     "thr-e bananas"
> str_replace_all(fruits, "[aeiou]", "-")
[1] "-n- -ppl-"     "tw- p--rs"     "thr-- b-n-n-s"
> str_replace(fruits, "([aeiou])", "")
[1] "ne apple"     "tw pears"     "thre bananas"
> str_replace(fruits, "([aeiou])", "\\1\\1")
[1] "oone apple"     "twoo pears"     "threee bananas"


함수: str_split
인자: string, pattern, n=Inf
        몇 개의 split 파트를 반환하는가를 n으로 정해준다.
설명: 입력값은 문자열 벡터, 결과값은 list, 각 원소는 
> fruits <- c(
+   "apples and oranges and pears and bananas",
+   "pineapples and mangos and guavas"
+ )
> str_split(fruits, " and ")
[[1]]
[1] "apples"  "oranges" "pears"   "bananas"

[[2]]
[1] "pineapples" "mangos"     "guavas"    

함수: str_split_fixed
설명: str_split과 마찬가지, 결과를 matrix로 뽑고 빈 곳은 ""로 채운다.

> fruits <- c(
+   "apples and oranges and pears and bananas",
+   "pineapples and mangos and guavas"
+ )
> str_split_fixed(fruits, " and ", 3)
     [,1]         [,2]      [,3]               
[1,] "apples"     "oranges" "pears and bananas"
[2,] "pineapples" "mangos"  "guavas"           
> str_split_fixed(fruits, " and ", 4)
     [,1]         [,2]      [,3]     [,4]     
[1,] "apples"     "oranges" "pears"  "bananas"
[2,] "pineapples" "mangos"  "guavas" ""  

함수: str_sub
인자: string, start=1L, end=-1L
설명: 문자열에서 start와 end로 끊어온다.

> hw <- "Hadley Wickham"
> str_sub(hw, 1, 6)
[1] "Hadley"
> str_sub(hw, end = 6)
[1] "Hadley"
> str_sub(hw, 8, 14)
[1] "Wickham"
> str_sub(hw, 8)
[1] "Wickham"
> str_sub(hw, c(1, 8), c(6, 14))
[1] "Hadley"  "Wickham"
> str_sub(hw, -1)
[1] "m"
# 뒤에서부터 따온다.
> str_sub(hw, -7)
[1] "Wickham"
# 뒤에서 7개
> str_sub(hw, end = -7)
[1] "Hadley W"
# 처음부터 뒤에서 7번째 땡겨온 것만큼 뽑는다.

> x <- "BBCDEF"

# 해당되는 부분을 치환할 수 있다.
> str_sub(x, 1, 1) <- "A"; x
[1] "ABCDEF"
> str_sub(x, -1, -1) <- "K"; x
[1] "ABCDEK"
> str_sub(x, -2, -2) <- "GHIJ"; x
[1] "ABCDGHIJK"
> str_sub(x, 2, -2) <- ""; x
[1] "AK"

함수: str_trim
인자: string, side = c("left","right","both")
        기본값은 both
> str_trim(" String with trailing and leading white space\t")
[1] "String with trailing and leading white space"
> str_trim("\n\nString with trailing and leading white space\n\n")
[1] "String with trailing and leading white space"

함수: word
인자: string, start=1L, end=start, sep=fixed(" ")
설명: 단어를 뽑는다.
start와 end는 단어의 순서를 알려준다.
end가 -1이면 뒷 첫 번째.
recycling rule이 적용된다.

> sentences <- c("Jane saw a cat", "Jane sat down")
> word(sentences, 1)
[1] "Jane" "Jane"
> word(sentences, 2)
[1] "saw" "sat"
> word(sentences, -1)
[1] "cat"  "down"
> word(sentences, 2, -1)
[1] "saw a cat" "sat down" 
> # Also vectorised over start and end
> word(sentences[1], 1:3, -1)
[1] "Jane saw a cat" "saw a cat"      "a cat"         
> word(sentences[1], 1, 1:4)
[1] "Jane"           "Jane saw"       "Jane saw a"     "Jane saw a cat"


정규표현식에 관하여 궁금한 점이 많을 것 같습니다.
자세한 사항은 "정규 표현식"이나 regular expression으로 구글링 하세요. ㅋㅋㅋㅋ





댓글

이 블로그의 인기 게시물

Bradley-Terry Model: paired comparison models

xlwings tutorial - 데이터 계산하여 붙여 넣기

R에서 csv 파일 읽는 법