stringr 1.0.0を使ってみる

stringrのバージョンがついに1.0.0になりました。アップデート内容を読んでみます。

stringiとの統合

CRANberriesによると、これは実に、2012年12月以来のアップデートらしいです。

2年半。長かった。その間に、勃興してきたものといえば?

そう、stringiですよね。

stringi: THE string processing package for R

ICU libraryを使ってちょっぱやなstringiは、数々のスピード狂たちに愛されてきました。しかし、羽鳥教でありながらスピードのためにstringrを使わないことで背徳感に苛まれてきた信者たちも少なくないでしょう。

安心してください、もう大丈夫です。

stringrは今や、ただのstringiのラッパーになりました!

stringr is now powered by stringi instead of base R regular expressions. This improves unicode and support, and makes most operations considerably faster. If you find stringr inadequate for your string processing needs, I highly recommend looking at stringi in more detail.

ちなみに、「そんなラッパーとかいらなくない?」とか思った人はちょっとこれをみてください:RPubs - stringr and stringi

stringiのほうが、圧倒的に関数が多いです。この辺の使い分けとか、オプションのつけ方で悩みそうなら、stringrを使っとくのが無難でしょう。(個人的には、Unicode正規化をしたいときにstri_trans_nfc()を使う以外はstringrで事足りています)

vignette

stringr gains a vignette, currently a straight forward update of the article that appeared in the R Journal.

vignetteが付きました。とりあえず使いかたはこれ読んどけば大丈夫です。

stringr/stringr.Rmd at master · hadley/stringr · GitHub

str_c()の長さゼロのベクターNAの扱い

str_c() now returns a zero length vector if any of its inputs are zero length vectors. This is consistent with all other functions, and standard R recycling rules. Similarly, using str_c("x", NA) now yields NA. If you want "xNA", use str_replace_na() on the inputs.

長さがゼロのベクターとかNAの扱いを、Rの標準的なマナーに合わせましたよ、という話。長さゼロのベクターは長さゼロのベクターとして返し、引数にひとつでもNAが入ってると結果はNAになります。 たしかにこんな感じの挙動になる関数多いですよね。

具体的にいうと、これまでは、

str_c(character(0))
#> [1] ""
str_c("x", NA)
#> [1] "xNA"

だったのが、

str_c(character(0))
#> character(0)
str_c("x", NA)
#> NA

になったということみたいです。NAをむりやり文字列にするには、str_replace_na()という関数が用意されています。replacementで置き換える文字を指定できます。

str_replace_na(c(NA, "ん", NA, "ん", "だ"), replacement = "な")
#> [1] "な" "ん" "な" "ん" "だ"

ちなみに、NAの扱いについては、baseはイケてなくて、たとえばpaste()はこんなことになります...。

paste0("x", NA)
#> "xNA"

str_replacement_all()

str_replace_all() gains a convenient syntax for applying multiple pairs of pattern and replacement to the same vector

str_replace_all()は、str_replace()のパワーアップ版みたいな関数です。名前の通り、マッチした文字列をすべて置換します。

> str_replace("abc", "[ab]", "☆")
[1] "☆bc"
> str_replace_all("abc", "[ab]", "☆")
[1] "☆☆c"

あと、str_replace_all()は、複数の置換パターンを指定することができます。(dplyrの*_join()に慣れてる人は親しみやすい書き方でしょう)

str_replace("abc", c("a" = "★", "b" = "☆"))
#> Error in stri_replace_all_regex(x, c("\\$", "\\\\(\\d)"), c("\\\\$", "\\$$1"),  : 
#>   argument "replacement" is missing, with no default
str_replace_all("abc", c("a" = "★", "b" = "☆"))
#> [1] "★☆c"

str_match()NAの扱い

str_match() now returns NA if an optional group doesn't match (previously it returned ""). This is more consistent with str_extract() and other match failures.

これはstr_c()のアップデート内容と似たような感じです。マッチするものがないとNAを返すようになりました。

ちなみに、str_match()str_extract()ってなにが違うのかと思ったら、前者はサブマッチ(っていうんでしょうか?)も含めてmatrixで返す関数でした。

str_subset()

New str_subset() keeps values that match a pattern. It's a convenient wrapper for x[str_detect(x)] (#21, @jiho).

baseでいうgrep(value = TRUE)に相当するものみたいです。ドキュメントに載ってるのはこんな感じです。

fruit <- c("apple", "banana", "pear", "pinapple")
str_subset(fruit, "a")
#> [1] "apple"    "banana"   "pear"     "pinapple"

NAは暗黙的に結果から取り除かれます。

str_subset(c("a", NA, "b"), ".")
#> [1] "a" "b"

str_order()str_sort()

New str_order() and str_sort() allow you to sort and order strings in a specified locale.

baseでいうsort()order()にあたるものですが、ロケールを指定できます。でもこれは、同じ文字でも並び方が変わることがあるアルファベット圏とかの話っぽくて、日本語圏にはあんまり関係なさそうです。Locale - ICU User Guideによると、ロケールの表記はISO-639に従うそうです。

str_conv()

New str_conv() to convert strings from specified encoding to UTF-8.

エンコードを指定するとUTF-8に直してくれる関数です。これでもう、Why are you using SJIS?案件も怖くないですね!(たぶん)

ドキュメントに載ってる例はこんな感じ。

x <- rawToChar(as.raw(177))
x
#> [1] "\xb1"
str_conv(x, "ISO-8859-2")
#> [1] "ą"
str_conv(x, "ISO-8859-1")
#> [1] "±"

boundary()

New modifier boundary() allows you to count, locate and split by character, word, line and sentence boundaries.

これは、文字列を分割するときに便利な関数です。あまり理解できてないんですが、空白とか改行とかにいい感じにマッチしてくれる正規表現的なもの?らしいです。(modifierってこういうときはなんて訳すんでしょう...?)

これまで、こんな感じに数の揃っていない空白に悩まされていたのが、

words <- c("These are   some words.")
str_split(words, " ")[[1]]
#> [1] "These"  "are"    ""       ""       "some"   "words."

boundary()を使うときれいに分割することができます。

str_split(words, boundary("word"))[[1]]
#> [1] "These" "are"   "some"  "words"

これは文と文の区切りですが、他にも色々指定できるみたいです。

formals(boundary)$type
#> c("character", "line_break", "sentence", "word")

日本語の分割のされ方はよく分からない感じ。

str_split("吾輩は 猫である", boundary("word"))[[1]]
[1] "吾輩" "は"   "猫"   "で"   "ある"

ドキュメントが愛に満ち溢れている

The documentation got a lot of love, and very similar functions (e.g. first and all variants) are now documented together. This should hopefully make it easier to locate the function you need.

らしいです。

deprecatedになった関数

ignore.case(x) has been deprecated in favour of fixed|regexp|coll(x, ignore.case = TRUE), perl(x) has been deprecated in favour of regexp(x).

str_join() is deprecated, please use str_c() instead.

str_join()、名前がstr_c()より憶えやすいので使ってました。気を付けます。

感想

割とかゆいところに手が届く感じがすてき。基本stringrで、困ったときのstringi、という感じがよさそうです。