Heavy Watal

stringr — Rの文字列をまともな方法で処理する

R標準のbaseパッケージが提供する関数でも文字列処理は可能だが、 stringrのほうが統一的なインターフェイスに合理的な挙動で使いやすい。

今や stringrstringi のラッパーだし、 どちらもほぼ同じインターフェイスなので、 もし前者に不足があれば後者を直接使えばよいが、 普通に使う分にはそんな場面には出くわさない。 むしろ、機能がある程度絞られているほうが取っ付き易いし、 str_* のほうが stri_* よりも1文字短いので、 基本的には stringr を使っとけばよい。

tidyverse に含まれているので、 install.packages("tidyverse") で一括インストール、 library(tidyverse) で一括ロード。

Functions

Basic Operation

str_length(string)
文字列の長さを数える。 base::nchar(x) と相同。
str_length(c("NA", NA))
[1]  2 NA
str_sub(string, start = 1, end = -1)
文字列を部分的に参照・変更する。 base::substr() と相同だが、負数で末尾からの位置を指定できる。 ただしRのインデックスは1始まりで終端も含むの。 str_sub<- が定義されているので置換にも使える。
str_sub("supercalifragilisticexpialidocious", 10, -15)
[1] "fragilistic"
str_flatten(string, collapse = "")
文字列vectorを1つの文字列に結合する。 base::paste0(string, collapse = "") と同等だが NA を扱える。
str_flatten(c("Dragon", NA, "Force"))
[1] NA
str_flatten(c("Dragon", NA, "Force"), na.rm = TRUE)
[1] "DragonForce"
paste0(c("Dragon", NA, "Force"), collapse = "")
[1] "DragonNAForce"
str_c(..., sep = "", collapse = NULL)
複数の引数で与えた文字列を結合する。 デフォルトの sep がスペースじゃないので base::paste0() に近い。
str_c(c("Dragon", "Hammer"), c(NA, ""), c("Force", "Fall"))
[1] NA           "HammerFall"
paste0(c("Dragon", "Hammer"), c(NA, ""), c("Force", "Fall"))
[1] "DragonNAForce" "HammerFall"   
str_split(string, pattern, n = Inf, simplify = FALSE)
文字列を分割してlistを返す base::strsplit(x, split) の改良版。 stringpattern の要素数が噛み合わないときにちゃんと警告が出る。 最大 n 個に分割するということを指定できる。 simplify = TRUE とするとmatrixで返す。
str_split(c("DragonForce", "HammerFall"), "(?<=[a-z])(?=[A-Z])")
[[1]]
[1] "Dragon" "Force" 
    
[[2]]
[1] "Hammer" "Fall"  
str_split(c("DragonForce", "HammerFall"), "(?<=[a-z])(?=[A-Z])", simplify = TRUE)
     [,1]     [,2]   
[1,] "Dragon" "Force"
[2,] "Hammer" "Fall" 
str_split_1("DragonForce", "(?<=[a-z])(?=[A-Z])")
[1] "Dragon" "Force" 
str_split_i(c("DragonForce", "HammerFall"), "(?<=[a-z])(?=[A-Z])", 1)
[1] "Dragon" "Hammer"
str_split_1(string, pattern) は1つの文字列を受け取ってvectorを返す簡略版。
str_split_i(string, pattern, i) は分割してできたi番目の要素だけをvectorで返す亜種。
str_split_fixed(string, pattern, n)n 必須、 simplify = TRUE 固定でmatrixを返すショートカット。
data.frame内の文字列を分割したい場合は tidyr::separate*() 系の関数を使う。
str_dup(string, times)
指定した回数だけ文字列を繰り返して結合。 base::strrep() と同等。
str_dup("pizza", 10)
[1] "pizzapizzapizzapizzapizzapizzapizzapizzapizzapizza"

Pattern Matching

str_count(string, pattern)
マッチする箇所の数を返す。
str_detect(string, pattern, negate = FALSE)
マッチするかどうか logical を返す。 negate = TRUE で結果を反転。 base::grepl(pattern, x) と相同。
正規表現を覚えてなくても始まりと終わりだけ手軽にマッチできる str_starts(), str_ends() もある。
str_extract(string, pattern), str_extract_all(string, pattern)
マッチした部分文字列を取り出す。しなかった要素には NA
数値+単位のような文字列から数値部分だけを抜き出すには readr::parse_number() が便利。
str_subset(string, pattern, negate = FALSE)
x[str_detect(x, pattern)] のショートカット。 マッチする要素だけ元の形で返すので str_extract() より base::grep(pattern, x, value = TRUE) に近い。
str_which(string, pattern, negate = FALSE)
マッチする要素のインデックスを整数で返す which(str_detect(x, pattern)) のショートカット。 base::grep(pattern, x) と相同。
str_locate(string, pattern)
マッチする最初の箇所の start, end 位置を行列で返す。
str_match(string, pattern), str_match_all(string, pattern)
マッチした部分文字列を取り出し、後方参照を含む行列を返す。 str_extract(string, pattern) と同じ結果全体 \0 が1列目で、 カッコでマッチさせた \1 以降の結果が2列目以降に入る。
str_replace(string, pattern, replacement)
マッチしなかった部分をそのままに、マッチした部分を置換する。 base::sub(pattern, replacement, x) と相同。 base::gsub() のように全てのマッチを置換するには str_replace_all()str_remove() はマッチした部分を消すためのショートカット。

上記関数のpattern引数は普通に文字列を渡すと正規表現として解釈してくれるが、 下記の関数を通して渡すことでその挙動を変更することができる。

stringr::regex(pattern, ignore_case = FALSE, multiline = FALSE, comments = FALSE, dotall = FALSE, ...)
デフォルトのICU正規表現。 複数行ファイルに対するマッチではこの関数を通して挙動をいじることになる。
stringr::fixed(pattern)
正規表現ではなくそのままの文字としてマッチさせる
stringr::boundary(type = "character", skip_word_none = NA, ...)
境界に対するマッチ。 typeの選択肢は character, line_break, sentence, word.
stringr::coll(pattern, ignore_case = FALSE, locale = NULL, ...)
よくわからないけど非ascii対策?

Formatting

str_to_upper(), str_to_lower(), str_to_title(), str_to_sentence()
大文字・小文字の変換
str_glue(..., .sep = "", .envir = parent.frame())
渡された文字列の中の {R表現} を評価して埋め込む。 sprintf() よりも使い方が簡単。ライバルは paste0() とか。 str_interp() はこれに取って代わられた。
str_glue("fruit[1] is {fruit[1]}.")
fruit[1] is apple.
data.frameの流れるpipe上では str_glue_data() が便利:
mtcars |> str_glue_data("mean(disp) is {mean(disp)}.")
mean(disp) is 230.721875.
本家の library(glue) にはほかのオプションもある。 それでもPythonのf-stringのような簡易フォーマッタは無くてちょっと不便。
str_pad(string, width, side = c("left", "right", "both"), pad = " ")
文字列の幅を width に伸ばして side 側を pad で埋める。
str_pad(c("9", "10"), 3L, pad = "0")
[1] "009" "010"
str_trim(string, side = "both")
端の空白文字を除去する。 Python でいうところの string.strip()
str_squish() は両端trimしたうえに内部の連続する空白文字を1つに縮める亜種。
str_trim("   trim   me   ")
[1] "trim   me"
str_squish("   trim   me   ")
[1] "trim me"
str_trunc(string, width, side = c("right", "left", "center"), ellipsis = "...")
一定の長さを超えたら捨てて ... にする。
str_wrap(string, width = 80, indent = 0, exdent = 0)
指定した幅で折り返す。 indent は先頭行の左余白。 exdent はそれ以外の行の左余白。

文字列と数値の型変換はstringrの管轄外なので、標準の as.character()as.double() などを使うか、 readr::parse_*()系の関数 を使う。

Rの文字列と正規表現

ダブルクォーテーションで挟んで作る。 文字列の中に " を含む場合はシングルクォーテーションで挟む。

s = "This is a string."
s = 'This is a string with "double quotes".'

エスケープシーケンス

バックスラッシュを使って改行 \n やタブ \t などの制御文字を表現できる。 バックスラッシュ自体を表すためには \\ のように重ねる必要がある。

string = "x\ty\n0\t1\n"
print(string)
[1] "x\ty\n0\t1\n"
cat(string)
x	y
0	1
readr::read_tsv(I(string))
  x y
1 0 1

See ?Quotes

正規表現

ICU正規表現からよく使うやつを抜粋。

メタ文字 意味
\d 数字
\s 空白
\w 英数字
. 何でも
^ 行頭
$ 行末

\D, \S, \W のように大文字にすると反転してそれ以外にマッチ。

演算子 意味
? 0回か1回
* 0回以上繰り返し
+ 1回以上繰り返し
{n,m} n回以上m回以下
XXX(?=YYY) YYYに先立つXXX
(?<=YYY)XXX YYYに続くXXX

生文字列

数字にマッチする正規表現を書こうとして pattern = "\d" とすると怒られる。 先述のようにバックスラッシュそのものを表すには二重にしておく必要があるため。

"\d"
# Error: '\d' is an unrecognized escape in character string starting (<input>:1:3)

"\\d"
# Good.

エスケープシーケンスを無効にした生文字列(raw string)を用いることでバックスラッシュを重ねずに済む。 PythonやC++などでは前からあったけどRでもようやく4.0.0 から使えるようになった。

pattern = "\\d"
pattern = r"(\d)"
pattern = R"(\d)"
pattern = r"---(\d)---"
pattern = r"---[\d]---"
pattern = r"---{\d}---"
stringr::str_count("1q2w3e4r", pattern)