stringr — Rの文字列をまともな方法で処理する
R標準のbaseパッケージが提供する関数でも文字列処理は可能だが、
stringrのほうが統一的なインターフェイスに合理的な挙動で使いやすい。
factorとcharacterを同じように扱う- 引数オブジェクトの各要素の名前や位置を保持する
- 長さゼロのオブジェクトを引数として与えた場合には長さゼロの結果を返す
- 引数オブジェクトに
NAが含まれる場合はその部分の結果をNAとする
- 対象文字列が一貫して第一引数で、パターンが二番目
- 何をやる関数なのか名前から分かりやすい
(標準が覚えにくすぎ:grep,grepl,regexpr,gregexpr,regexec) - ICU4C (via stringi) を使って動くため高速
- ICU正規表現 の仕様が明確
今や stringr は stringi のラッパーだし、
どちらもほぼ同じインターフェイスなので、
もし前者に不足があれば後者を直接使えばよいが、
普通に使う分にはそんな場面には出くわさない。
むしろ、機能がある程度絞られているほうが取っ付き易いし、
str_* のほうが stri_* よりも1文字短いので、
基本的には stringr を使っとけばよい。
tidyverse に含まれているので、
install.packages("tidyverse") で一括インストール、
library(tidyverse) で一括ロード。
Functions
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),str_locate_all(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()はマッチした部分を消すためのショートカット。 str_split(string, pattern, n = Inf, simplify = FALSE)- 文字列を分割してlistを返す
base::strsplit(x, split)の改良版。stringとpatternの要素数が噛み合わないときにちゃんと警告が出る。 最大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*()系の関数を使う。
上記関数の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対策?
Combining
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_flatten(string, collapse = "")- 文字列vectorを1つの文字列に結合する。
base::paste0(string, collapse = "")と同等だがNAを扱える。str_flatten(c("Dragon", NA, "Force"))[1] NAstr_flatten(c("Dragon", NA, "Force"), na.rm = TRUE)[1] "DragonForce"paste(c("Dragon", NA, "Force"), collapse = "")[1] "DragonNAForce" 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のような簡易フォーマッタは無くてちょっと不便。
Basic Operation
str_length(string)- 文字列の長さを数える。より正確には、コードポイントの数を数える。
base::nchar(x)と相同。x = c("NA", NA, "な", "\u2668", "\u2668\uFE0F") print(x)[1] "NA" NA "な" "♨" "♨️"str_length(x)[1] 2 NA 1 1 2 str_width()- 半角英数を1として幅を計算する。
str_width(x)[1] 2 NA 2 2 2 str_sub(string, start = 1, end = -1)- 文字列を部分的に参照・変更する。
base::substr()と相同だが、負数で末尾からの位置を指定できる。 ただしRのインデックスは1始まりで終端も含むの。str_sub<-が定義されているので置換にも使える。str_sub("supercalifragilisticexpialidocious", 10, -15)[1] "fragilistic" str_equal(x, y, locale = "en", ignore_case = FALSE, ...)- Unicode正規化を考慮して文字列が等しいかどうかを判定する。
"No\u0338gne" == "Nøgne"[1] FALSEstr_equal("No\u0338gne", "Nøgne")[1] TRUE str_unique(x, locale = "en", ignore_case = FALSE, ...)- Unicode正規化を考慮して重複を除去する。
str_unique(c("No\u0338gne", "Nøgne"))[1] "No̸gne" str_sort(x, decreasing = FALSE, na_last = TRUE, locale = "en", numeric = FALSE, ...)- 文字列をソートする。
str_order()はソートに使えるインデックスを返す。 e.g.,x[str_order(x)]str_rank()は各要素の順位を返す。 e.g.,starwars |> dplyr::arrange(str_rank(name))
Formatting
str_to_upper(),str_to_lower(),str_to_title(),str_to_sentence()- 大文字・小文字の変換
str_to_camel(),str_to_snake(),str_to_kebab()- プログラミングの命名規則のような変換。
x = "quick brown fox" str_to_camel(x)[1] "quickBrownFox"str_to_camel(x, first_upper = TRUE)[1] "QuickBrownFox"str_to_snake(x)[1] "quick_brown_fox"str_to_kebab(x)[1] "quick-brown-fox" str_dup(string, times)- 指定した回数だけ文字列を繰り返して結合。
base::strrep()と同等。str_dup("pizza", 10)[1] "pizzapizzapizzapizzapizzapizzapizzapizzapizzapizza" 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 のように大文字にすると反転してそれ以外にマッチ。
| 演算子 | 意味 |
|---|---|
a? |
0回か1回のa |
a* |
0回以上繰り返したa |
a+ |
1回以上繰り返したa |
a{n,m} |
n回以上m回以下のa |
a(?=c) |
cに先立つa |
(?<=b)a |
bに続くa |
生文字列
数字にマッチする正規表現を書こうとして 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)