stringr package를 이용한 문자열 조작

2014-07-04
R stringr package

최근 R for everyone이라는 책을 읽었다. 그 중 stringr 이라는 패키지를 이용한 문자열 처리 방법이 나와 있어 이번 기회에 정리해 보려 한다.

R 표준 base 패키지에 포함된 함수군와 비슷한 기능을 하는 것으로 보이지만 더 합리적인 출력형식을 가지므로 사용하기 편리하다.

이 패키지의 몇몇 특징을 살펴보면

  • factor와 character를 같은 방식으로 처리

  • 일관성 있는 함수 이름과 인수

  • 다른 함수의 입력값으로 사용하기 편리한 출력값.
    • 길이 0인 입력값에 대해 길이 0인 결과를 돌려줌
    • 입력값 NA가 포함되어 있을 때는 그 부분의 결과를 NA로 돌려줌
  • 사용빈도가 떨어지는 문자열 조작 처리를 과감하게 제거하여 간략화시킴

Installation

ggplot2를 설치하면 자동으로 설치된다. 혹은

> install.packages("stringr")

함수

Basic string operation

  • str_length(string)
    • 문자열의 길이를 계산. base::nchar(x)와 같은 기능을 하는 함수. 단, NA 에 대해서는 2가 아닌 NA를 돌려준다.
    > library(stringr)
    > str_length(c("i", "like", "programming", NA))
    #> [1]  1  4 11 NA
    > nchar(c("i", "like", "programming", NA))
    #> [1]  1  4 11 NA
  • str_sub(string, start=1, end=-1)
    • 문자열을 부분적으로 참조, 변경. base::substr()와 같은 기능을 하는 함수. 음수를 사용하여 문자열의 끝으로 부터의 위치를 지정할 수 있다.
    > hw <- "Hadley Wickham"
    > str_sub(hw, 1, 6)
    #> [1] "Hadley"
    > substr(hw, 1, 6)
    #> [1] "Hadley"
    > str_sub(hw, end = 6)
    #> [1] "Hadley"
    > str_sub(hw, -7)
    #> [1] "Wickham"
  • str_c(..., sep='', collapse=NULL), str_join(..., sep='', collapse=NULL)
    • 문자열을 통합. 디폴트의 sep가 스페이스 공백이 아니므로 base::paste0()와 비슷하다.
    > str_c(letters[-26], " comes before ", letters[-1])
    #>  [1] "a comes before b" "b comes before c" "c comes before d"
    #>  [4] "d comes before e" "e comes before f" "f comes before g"
    #>  [7] "g comes before h" "h comes before i" "i comes before j"
    #> [10] "j comes before k" "k comes before l" "l comes before m"
    #> [13] "m comes before n" "n comes before o" "o comes before p"
    #> [16] "p comes before q" "q comes before r" "r comes before s"
    #> [19] "s comes before t" "t comes before u" "u comes before v"
    #> [22] "v comes before w" "w comes before x" "x comes before y"
    #> [25] "y comes before z"
    > paste0(letters[-26], " comes before ", letters[-1])
    #>  [1] "a comes before b" "b comes before c" "c comes before d"
    #>  [4] "d comes before e" "e comes before f" "f comes before g"
    #>  [7] "g comes before h" "h comes before i" "i comes before j"
    #> [10] "j comes before k" "k comes before l" "l comes before m"
    #> [13] "m comes before n" "n comes before o" "o comes before p"
    #> [16] "p comes before q" "q comes before r" "r comes before s"
    #> [19] "s comes before t" "t comes before u" "u comes before v"
    #> [22] "v comes before w" "w comes before x" "x comes before y"
    #> [25] "y comes before z"
    > 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_split(string, pattern, n=Inf)
    • 문자열을 분리. base::strsplit(x, split) 와 대응하는 함수. 최대 n 개의 분할을 지정할 수 있다. 결과값은 list. 문자열을 정해진 n 개로 나누는 str_split_fixed() 도 있다. 결과값은 matrix.
    > 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"
    > strsplit(fruits, "and")
    #> [[1]]
    #> [1] "apples "   " oranges " " pears "   " bananas" 
    #> 
    #> [[2]]
    #> [1] "pineapples " " mangos "    " guavas"
    > str_split(fruits, " and ", n = 3)
    #> [[1]]
    #> [1] "apples"            "oranges"           "pears and bananas"
    #> 
    #> [[2]]
    #> [1] "pineapples" "mangos"     "guavas"
    > str_split(fruits, " and ", n = 2)
    #> [[1]]
    #> [1] "apples"                        "oranges and pears and bananas"
    #> 
    #> [[2]]
    #> [1] "pineapples"        "mangos and guavas"
    > str_split_fixed(fruits, " and ", 4)
    #>      [,1]         [,2]      [,3]     [,4]     
    #> [1,] "apples"     "oranges" "pears"  "bananas"
    #> [2,] "pineapples" "mangos"  "guavas" ""

Pattern Matching

  • str_detect(string, pattern)
    • 매치하는 곳이 있는지 없는지를 logical 값으로 돌려준다. base::grepl(pattern, x)과 대응.
    > 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_count(string, pattern)
    • 매치하는 곳의 수를 돌려준다.
    > str_count(fruit, c("a", "b", "p", "p"))
    #> [1] 1 1 1 3
  • str_locate(string, pattern)
    • 처음으로 매치되는 곳의 start, end 위치를 행렬로 돌려준다.
    > str_locate(fruit, "e")
    #>      start end
    #> [1,]     5   5
    #> [2,]    NA  NA
    #> [3,]     2   2
    #> [4,]     8   8
  • str_extract(string, pattern), str_extract_all(string, pattern)
    • 매치된 부분 문자열을 추출. 매치되지 않은 요소는 NA. base::grep(pattern, x, value=TRUE)는 매치된 요소만 원래의 형태로 돌려준다.
    > shopping_list <- c("apples x4", "flour", "sugar", "milk x2")
    > str_extract(shopping_list, "\\d")
    #> [1] "4" NA  NA  "2"
    > grep("\\d", shopping_list, value = TRUE)
    #> [1] "apples x4" "milk x2"
    > str_extract(shopping_list, "[a-z]+")
    #> [1] "apples" "flour"  "sugar"  "milk"
    > grep("[a-z]+", shopping_list, value = TRUE)
    #> [1] "apples x4" "flour"     "sugar"     "milk x2"
  • str_match(string, pattern), str_match_all(string, pattern)
    • 매치된 부분 문자열을 추출하고 참조를 행렬로 돌려준다。 str_extract(string, pattern)의 결과를 1열에 각 괄호에 매치된 이후의 결과가 2열 이후에 들어간다. 예제를 보는 것이 이해에 도움이 됨.
    > 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"
    #>  [5] "387 287 6718" NA             "233.398.9187" "482 952 3315"
    #>  [9] "239 923 8115" "842 566 4692" "579-499-7527" NA            
    #> [13] "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_replace(string, pattern, replacement)
    • 매치되지 않은 부분은 그대로 매치된 부분만 치환. base::sub(pattern, replacement, x) 와 대응. base::gsub()과 같이 모든 매치부분을 치환하기 위해서는 str_replace_all()을 사용.
    > 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"

    위에서 나열한 함수에서 _pattern_은 디폴트로 POSIX 정규표현식으로 취급된다. 이하의 함수를 통해 인수값을 변경할 수 있다.

  • stringr::fixed(string)
    • 입력값 그대로의 문자로 매칭 시킴
  • stringr::ignore.case(string)
    • 대문자 소문자를 무시하여 매치 시킴
  • stringr::perl(string)
    • Perl 정규표현식으로 취급

Formatting

  • str_trim(string, side="both")
    • 공백문자를 제거.
  • str_pad(string, width, side="left", pad=" ")
    • 폭을 width 만큼 늘려서 side를 기준으로 공백을 pad에 지정된 문자로 채워넣음.
  • str_wrap(string, width=80, indent=0, exdent=0)
    • 지정한 폭으로 줄바꿈. indent는 선두행의 왼쪽 여백, exdent는 그 이외 행의 왼쪽여백.

결국은 정규표현식을 잘써야 한다는 이야기!!

참고자료 : stringr: mordern, consistent string processing

comments powered by Disqus