はい、承知いたしました。R言語における正規表現の初心者向け解説記事を作成します。約5000語のボリュームで、詳細な説明を含めて執筆します。
【初心者必見】R言語で正規表現を学ぶための第一歩:文字列操作の最強ツールを使いこなそう!
はじめに
データ分析の世界に足を踏み入れたばかりのあなたへ。R言語を学び始め、数値データの扱いは少しずつ慣れてきたけれど、アンケートの自由回答、SNSの投稿、Webサイトから取得したテキストなど、「文字列データ」の扱いに頭を悩ませていませんか?
「”東京都”から”都”を取り除きたい」
「大量のメールアドレスの中から、特定のドメインのものだけを抽出したい」
「”1990/01/20″や”90-1-20″といったバラバラな日付の形式を統一したい」
このような課題に直面したとき、for
ループやif
文を駆使して複雑なコードを書くこともできますが、もっとスマートで、強力で、そして何より「楽しい」解決策があります。それが正規表現(Regular Expression)です。
正規表現は、一見すると意味不明な記号の羅列に見えるかもしれません。しかし、その記号一つ一つが持つ意味を理解すれば、まるで魔法のように複雑な文字列操作をたった1行のコードで実現できてしまいます。それは、データクレンジングの時間を劇的に短縮し、テキストマイニングの精度を高め、あなたのデータ分析の可能性を大きく広げる「最強の武器」となるでしょう。
この記事は、R言語を学び始めたばかりの初心者や、正規表現という言葉は聞いたことがあるけれど、どこから手をつけていいかわからない、というあなたのために書かれました。この記事を最後まで読めば、あなたは以下のことができるようになります。
- 正規表現の基本的な「文法」を理解し、簡単なパターンを読み書きできるようになる。
- Rの標準機能(Base R)である
grep
,sub
,gsub
などの使い方をマスターできる。 - よりモダンで直感的な
stringr
パッケージを使いこなし、tidyverseのエコシステムの中で自在に文字列を操れるようになる。
正規表現は、一度身につければR言語だけでなく、Python、JavaScript、SQLなど、多くのプログラミング言語やツールで応用が効く「一生モノのスキル」です。さあ、私たちと一緒に、この奥深くも魅力的な正規表現の世界へ、第一歩を踏み出しましょう!
第1章: 正規表現の世界へようこそ
1.1. 正規表現とは? – 文字列の「パターン」を記述する言語
正規表現(Regular Expression、しばしばRegexと略されます)とは、一言で言えば「文字列のパターンを表現するための、特別なルール(記号の集まり)」です。
通常の文字列検索は「完全一致」が基本です。例えば、「apple」という文字列を探す場合、コンピュータは「a」「p」「p」「l」「e」という文字がこの順番で並んでいる箇所を探します。これは非常にシンプルですが、柔軟性に欠けます。
では、次のような検索をしたい場合はどうでしょうか?
- 「
a
で始まり、e
で終わる、5文字の単語」を探したい(例: “apple”, “adobe”) - 「3桁の数字 – 4桁の数字」という形式の郵便番号を探したい(例: “123-4567″)
- 有効なメールアドレスの形式に合致する文字列をすべて見つけたい
このような「曖昧さ」や「構造」を含む検索を可能にするのが正規表現です。正規表現では、.
(ドット)や*
(アスタリスク)といったメタ文字(meta-character)と呼ばれる特別な意味を持つ記号を使い、抽象的なパターンを記述します。
例えば、「a
で始まり、e
で終わる、5文字の単語」は、正規表現を使うと a..e
と表現できます。この .
が「任意の一文字」を表すメタ文字です。このように、メタ文字を組み合わせることで、私たちはコンピュータに対して非常に複雑で柔軟な「文字列の探し方」を指示することができるのです。
1.2. なぜ正規表現は「最強」なのか?
プログラミングの世界には多くのツールやテクニックがありますが、なぜ正規表現は「最強」とまで言われるのでしょうか。その理由は、以下の3つの点に集約されます。
-
圧倒的な柔軟性と表現力:
前述の通り、正規表現は非常に複雑なパターンを驚くほど簡潔に表現できます。もし正規表現を使わなければ、何十行にもわたる条件分岐やループ処理が必要になるような文字列操作も、正規表現ならたった1行のパターンで記述できることが珍しくありません。これにより、コードは短く、そして意図が明確になります。 -
驚異的な汎用性:
正規表現はR言語の専売特許ではありません。Python、Java、JavaScript、PHP、Perl、Rubyといった主要なプログラミング言語はもちろん、Linuxのgrep
コマンド、テキストエディタ(VSCode, Sublime Textなど)、データベース(MySQL, PostgreSQLなど)に至るまで、テキストを扱うあらゆる場面で利用されています。つまり、一度Rで正規表現をマスターすれば、その知識は他の多くの場面で直接役立ち、あなたの技術的価値を大きく高めてくれるのです。 -
生産性の劇的な向上:
データ分析の実務、特に前処理の段階(データクレンジング)では、汚いテキストデータを整形する作業に多くの時間が費やされます。正規表現を使いこなせれば、この時間を劇的に短縮できます。手作業や複雑なコードで数時間かかっていた作業が、数分で完了することも夢ではありません。これにより、あなたはより本質的な分析や考察に多くの時間を割けるようになります。
1.3. R言語における正規表現の位置づけ
R言語は、その誕生当初から統計解析とデータ可視化に強みを持っていましたが、強力な文字列操作機能も標準で備わっています。これらはBase Rと呼ばれる、パッケージをインストールしなくても最初から使える機能群に含まれています。
一方で、よりモダンで一貫性のある操作を目指すtidyverseというエコシステムの中では、stringr
というパッケージが文字列操作の主役を担っています。stringr
は内部的にBase Rの正規表現エンジンを使いつつ、よりユーザーフレンドリーな関数名と引数の順序を提供しており、特にdplyr
などの他のtidyverseパッケージとの連携がスムーズです。
この記事では、まず基本となるBase Rの関数を学び、その上でstringr
パッケージの利便性と使い方を解説していきます。どちらも理解することで、あなたはどんな状況でも最適なツールを選択できるようになるでしょう。
第2章: 正規表現の基本文法 – メタ文字を覚えよう
ここが正規表現学習の核心部分です。一見すると暗号のようですが、一つ一つの記号の意味は非常にシンプルです。焦らず、具体例と一緒にじっくりと理解していきましょう。
【重要】R言語で正規表現を書く際の注意点
本題に入る前に、非常に重要なR特有のルールについて説明します。正規表現では、特別な意味を持つ文字を打ち消すために\
(バックスラッシュ、円マーク)を使います(これをエスケープと呼びます)。しかし、Rの文字列リテラル("
や'
で囲まれた部分)の中でも、\
はエスケープ文字として特別な役割を持っています。
そのため、正規表現パターンの中で\
という文字そのものを使いたい場合、Rの文字列の中では\\
と2つ重ねて書く必要があります。
- 正規表現のパターン:
\d
(数字1文字を表す) - Rのコードで書く場合:
"\\d"
この「\
は\\
と書く」というルールは、Rで正規表現を扱う上で絶対に忘れてはならないお約束です。最初は戸惑うかもしれませんが、すぐに慣れるはずです。
では、いよいよメタ文字の世界へ飛び込みましょう!
2.1. 文字の一致 – 特定の文字や文字種を指定する
メタ文字 | 意味 | Rでの例 | マッチする例 |
---|---|---|---|
. |
任意の一文字(改行文字を除く) | ".at" |
“cat”, “hat”, “bat”, “5at” |
[] |
角括弧内のいずれか一文字 | "[bcr]at" |
“bat”, “cat”, “rat” |
[a-z] |
指定した範囲のいずれか一文字 | "[a-c]at" |
“aat”, “bat”, “cat” |
[^] |
角括弧内以外のいずれか一文字 | "[^bcr]at" |
“hat”, “mat”, “pat” (b,c,r以外で始まるat) |
-
.
(ドット)
最もシンプルで強力なメタ文字の一つです。どんな一文字にもマッチするワイルドカードとして機能します。ただし、通常は改行文字にはマッチしません。 -
[]
(角括弧)
「この中のどれか一文字」という選択肢を表現します。例えば、[aiueo]
は母音のいずれか一文字にマッチします。ハイフン-
を使うと範囲を指定でき、[a-z]
は小文字アルファベット、[0-9]
は数字、[A-Za-z]
は大小すべてのアルファベットにマッチします。 -
[^]
(否定の角括弧)
角括弧の先頭に^
を置くと、意味が反転します。「この中の文字以外のどれか一文字」という意味になります。例えば、[^0-9]
は数字以外の任意の一文字にマッチします。
2.2. 量化子 – 繰り返しの回数を指定する
直前の文字やグループが何回繰り返されるかを指定します。
量化子 | 意味 | Rでの例 | マッチする例 |
---|---|---|---|
* |
0回以上の繰り返し | "a*b" |
“b”, “ab”, “aaab” |
+ |
1回以上の繰り返し | "a+b" |
“ab”, “aaab” (”b”にはマッチしない) |
? |
0回または1回の繰り返し | "colou?r" |
“color”, “colour” |
{n} |
ちょうどn回の繰り返し | "[0-9]{3}" |
“123”, “987” |
{n,} |
n回以上の繰り返し | "[0-9]{2,}" |
“12”, “123”, “1234” |
{n,m} |
n回以上m回以下の繰り返し | "[0-9]{2,4}" |
“12”, “123”, “1234” |
*
(アスタリスク):a*
は「a
が0個以上」なので、a
が全くない場合でもマッチします。+
(プラス):a+
は「a
が1個以上」なので、最低でも1つはa
がないとマッチしません。?
(クエスチョンマーク): 「あってもなくてもよい」というオプションを表現するのに便利です。{}
(波括弧): より厳密に繰り返しの回数を指定したい場合に使います。
【コラム】最長マッチと最短マッチ
量化子は、デフォルトでは最長マッチ(Greedy Match)という挙動をします。つまり、できるだけ長い文字列にマッチしようとします。
例えば、文字列 "<a><b><c>"
に対して、正規表現 <.+>
を適用すると、"<a><b><c>"
全体にマッチしてしまいます。
もし、"<a>"
や "<b>"
のように、個々のタグにマッチさせたい場合は、最短マッチ(Lazy Match)を使います。量化子の後ろに ?
をつけると最短マッチになります。
Rでの例: "<.+?>"
-> "<a>"
, "<b>"
, "<c>"
それぞれにマッチ
最初は最長マッチだけ覚えておけば十分ですが、思ったような抽出ができない場合はこの「最短マッチ」を思い出してください。
2.3. アンカー – 文字列内の「位置」を指定する
文字そのものではなく、文字列の先頭や末尾、単語の境界といった「位置」にマッチします。
アンカー | 意味 | Rでの例 | マッチする例 |
---|---|---|---|
^ |
文字列の先頭 | "^R" |
“R is powerful” にはマッチするが、”I love R” にはマッチしない |
$ |
文字列の末尾 | "R$" |
“I love R” にはマッチするが、”R is powerful” にはマッチしない |
\b |
単語の境界(単語の始まりか終わり) | "\\bcat\\b" |
“cat”, “the cat is” にはマッチするが、”concatenate” にはマッチしない |
\B |
単語の境界以外 | "\\Bcat\\B" |
“concatenate” の “cat” にマッチする |
^
: 行頭の記号として使われることが多いです。[^]
(否定の角括弧)の^
とは意味が全く異なるので注意してください。$
: 行末の記号です。^
と$
を組み合わせることで、文字列全体がパターンに完全一致するかどうかを判定できます。例:^[0-9]+$
は、文字列全体が1文字以上の数字のみで構成されている場合にマッチします。\b
: “word boundary”のb
です。単語を構成する文字(英数字やアンダースコア)と、そうでない文字(スペース、句読点など)の間にマッチします。特定の単語だけを正確に抜き出したい場合に非常に便利です。\b
を使うにはRでは\\b
と書くことを忘れずに。
2.4. グループ化と選択
複数の文字をひとまとめに扱ったり、「AまたはB」という条件を指定したりします。
メタ文字 | 意味 | Rでの例 | マッチする例 |
---|---|---|---|
() |
パターンをグループ化する | "(ab)+" |
“ab”, “abab”, “ababab” |
| |
OR条件(いずれかにマッチ) | "cat|dog" |
“cat”, “dog” |
-
()
(丸括弧): グループ化には2つの主要な役割があります。- 量化子の適用範囲を明確にする:
abc+
はab
の後にc
が1回以上続くパターン(”abc”, “abcc”)にマッチしますが、(abc)+
はabc
という塊が1回以上続くパターン(”abc”, “abcabc”)にマッチします。 - キャプチャ:
()
で囲んだ部分にマッチした文字列を「記憶」しておき、後から参照する機能です。これは後述する置換操作(sub
,gsub
)で絶大な威力を発揮します。
- 量化子の適用範囲を明確にする:
-
|
(パイプ):or
と同じ意味です。apple|orange
は”apple”または”orange”のどちらかにマッチします。グループ化と組み合わせることで、より複雑なOR条件も作れます。例:gr(a|e)y
は “gray” と “grey” の両方にマッチします。
2.5. 文字クラスのショートカット
よく使われる文字の組み合わせ([0-9]
や[a-zA-Z0-9_]
など)は、便利なショートカットが用意されています。これらは大文字にすると意味が反転します。そして、これらはすべて \
で始まるので、Rでは \\
と書く必要があります。
ショートカット | 意味 | Rでの書き方 | 等価な表現 |
---|---|---|---|
\d |
数字 (digit) | "\\d" |
"[0-9]" |
\D |
数字以外 | "\\D" |
"[^0-9]" |
\w |
英数字とアンダースコア (word character) | "\\w" |
"[a-zA-Z0-9_]" |
\W |
\w 以外 |
"\\W" |
"[^a-zA-Z0-9_]" |
\s |
空白文字 (space character) | "\\s" |
"[ \t\r\n\f\v]" |
\S |
空白文字以外 | "\\S" |
"[^ \t\r\n\f\v]" |
これらのショートカットを使いこなせるようになると、正規表現の記述が格段にスッキリし、可読性も上がります。
2.6. エスケープ文字 \
最後に、最も重要なルールをもう一度。.
, *
, +
, ?
, ()
, []
, {}
, ^
, $
, |
, \
といったメタ文字そのものを「ただの文字」として検索したい場合は、その直前に\
を置く必要があります。
- IPアドレスのような
192.168.1.1
にマッチさせたい場合- 間違い:
"\\d+.\\d+.\\d+.\\d+"
->.
が任意の一文字になってしまう - 正しい:
"\\d+\\.\\d+\\.\\d+\\.\\d+"
->\\.
でドット文字そのものを指定
- 間違い:
Rの文字列内でのエスケープ(\\
)と、正規表現のエスケープ(\.
)が組み合わさって、"\\."
と書く点に注意してください。
第3章: Rで正規表現を使ってみよう – Base R編
文法を学んだところで、いよいよRの関数を使って実際に文字列を操作していきましょう。まずは、パッケージ不要で使えるBase Rの主要な関数を紹介します。
これから紹介する関数を試すために、以下のサンプルデータを使います。
“`R
サンプルデータ
texts <- c(
“R is a language for statistical computing.”,
“My phone number is 090-1234-5678.”,
“Please send an email to [email protected] for details.”,
“Product IDs are A-101, B-202, and C-303.”,
“No relevant information here (N/A).”
)
“`
3.1. grep()
/ grepl()
– パターンの検出
指定したパターンにマッチする要素を見つけたいときに使います。
grep(pattern, x, value = ...)
: マッチした要素のインデックスまたは値そのものを返します。grepl(pattern, x)
: マッチしたかどうかを要素ごとに論理値 (TRUE
/FALSE
) で返します。
grepl()
の使用例
grepl()
は、データフレームのフィルタリングなどで非常に便利です。
“`R
“email”という単語を含む要素を検出
grepl(“email”, texts)
[1] FALSE FALSE TRUE FALSE FALSE
数字を含む要素を検出 (正規表現: \d)
grepl(“\d”, texts)
[1] FALSE TRUE TRUE TRUE FALSE
emailアドレスのパターンにマッチする要素を検出
パターン: 英数字とドット + @ + 英数字とドット
email_pattern <- “\w+\.?\w+@\w+\.\w+”
grepl(email_pattern, texts)
[1] FALSE FALSE TRUE FALSE FALSE
“`
grep()
の使用例
grep()
は、マッチした要素を具体的に取り出したいときに使います。
“`R
数字を含む要素のインデックスを返す (デフォルトは value = FALSE)
grep(“\d”, texts)
[1] 2 3 4
数字を含む要素の値そのものを返す (value = TRUE)
grep(“\d”, texts, value = TRUE)
[1] “My phone number is 090-1234-5678.”
[2] “Please send an email to [email protected] for details.”
[3] “Product IDs are A-101, B-202, and C-303.”
“`
使い分け:
* if
文の条件やdplyr::filter()
での行の絞り込みには、TRUE
/FALSE
を返すgrepl()
が適しています。
* マッチした要素そのものを抜き出して新しいベクトルを作りたい場合は、grep(..., value = TRUE)
が便利です。
3.2. sub()
/ gsub()
– パターンの置換
正規表現の真価が発揮されるのが、この置換機能です。
sub(pattern, replacement, x)
: 各要素で最初にマッチした部分だけを置換します。gsub(pattern, replacement, x)
: 各要素ですべてマッチした部分を置換します(g
は”global”の意)。
gsub()
の使用例
“`R
すべての数字を”#”に置換する
gsub(“\d”, “#”, texts)
[1] “R is a language for statistical computing.”
[2] “My phone number is ###-####-####.”
[3] “Please send an email to [email protected] for details.”
[4] “Product IDs are A-###, B-###, and C-###.”
[5] “No relevant information here (N/A).”
“`
sub()
と gsub()
の違い
“`R
C-303 の例で違いを見る
text_c <- “Product IDs are A-101, B-202, and C-303.”
sub() は最初の数字の塊だけを置換
sub(“\d+”, “XXX”, text_c)
[1] “Product IDs are A-XXX, B-202, and C-303.”
gsub() はすべての数字の塊を置換
gsub(“\d+”, “XXX”, text_c)
[1] “Product IDs are A-XXX, B-XXX, and C-XXX.”
“`
後方参照 (\\1
, \\2
, …) を使った高度な置換
正規表現パターンの()
(グループ化)でキャプチャした文字列は、置換後の文字列(replacement
)の中で \\1
, \\2
, … という形で参照できます。\\1
が1番目の()
にマッチした部分、\\2
が2番目の()
にマッチした部分を指します。
これは非常に強力な機能です。例えば、電話番号のフォーマットを変更してみましょう。
“`R
phone_text <- “My phone number is 090-1234-5678.”
パターン: (3桁数字) – (4桁数字) – (4桁数字)
グループ1: 090, グループ2: 1234, グループ3: 5678
pattern <- “(\d{3})-(\d{4})-(\d{4})”
置換後: (グループ1)グループ2-グループ3 例: (090)1234-5678
replacement <- “(\1)\2-\3”
gsub(pattern, replacement, phone_text)
[1] “My phone number is (090)1234-5678.”
“`
このように、元の文字列の一部を再利用しながら、全く新しい形式の文字列を組み立てることができます。姓と名を入れ替える、日付のフォーマットを変換するなど、応用範囲は無限大です。
3.3. regexpr()
/ gregexpr()
– パターンの位置と長さを取得
パターンにマッチした部分の「文字列そのもの」ではなく、「どこにマッチしたか」という位置情報が欲しい場合に使います。
regexpr(pattern, text)
: 各要素で最初にマッチした部分の開始位置と長さを返します。gregexpr(pattern, text)
: 各要素ですべてのマッチ箇所の開始位置と長さを返します。
戻り値が少し特殊で、開始位置は数値ベクトルとして、長さやマッチした文字列はattributes
として格納されます。
“`R
“language” という単語の位置を調べる
text_r <- “R is a language for statistical computing.”
match_info <- regexpr(“language”, text_r)
print(match_info)
[1] 9
attr(,”match.length”)
[1] 8
attr(,”index.type”)
[1] “chars”
attr(,”useBytes”)
[1] TRUE
``
-1` が返ります。
この結果は「9文字目から始まり、長さ8文字の箇所にマッチした」ことを意味しています。マッチしなかった場合は
3.4. regmatches()
– マッチした部分を抽出
regexpr()
やgregexpr()
は位置情報を返すだけなので、実際にマッチした文字列を抜き出すにはregmatches()
と組み合わせます。
“`R
Product ID (アルファベット-数字) をすべて抽出する
ids_text <- “Product IDs are A-101, B-202, and C-303.”
id_pattern <- “\w-\d+”
まず gregexpr で全てのマッチ位置を取得
match_positions <- gregexpr(id_pattern, ids_text)
regmatches で位置情報をもとに文字列を抽出
regmatches(ids_text, match_positions)
[[1]]
[1] “A-101” “B-202” “C-303”
``
gregexprの戻り値がリストなので、
regmatches`の戻り値もリストになります。
このように、Base Rの関数を組み合わせることで、検出、置換、抽出といった一通りの操作が可能です。しかし、関数ごとに引数の順序が違ったり、戻り値の形式が異なったりと、少し一貫性に欠ける部分があるのも事実です。そこで次に、よりモダンで使いやすいstringr
パッケージを見ていきましょう。
第4章: モダンなアプローチ – stringr
パッケージを使おう
stringr
は、Hadley Wickham氏が開発したtidyverse
の中核パッケージの一つで、文字列操作をより直感的かつ一貫性のある方法で行うことを目的としています。
4.1. なぜstringr
なのか?
stringr
を使うメリットは数多くあります。
- 関数名の一貫性: すべての関数が
str_
で始まります(str_detect
,str_replace
,str_extract
など)。これにより、関数名からその役割を推測しやすくなります。 - 引数順の一貫性: 第一引数が常に対象の文字列ベクトル(データ)、第二引数がパターンです。これにより、パイプ演算子
|>
(または%>%
)と非常に相性が良くなります。 - tidyverseとの親和性:
dplyr
のmutate()
やfilter()
の中でシームレスに使うことができ、データフレーム操作の一環として自然に文字列処理を組み込めます。 - 直感的な戻り値: Base Rの関数よりも、戻り値のデータ型が予測しやすく、扱いやすいことが多いです。例えば、
str_extract_all
は常にリストを返します。 NA
の扱い:NA
(欠損値)の扱いがより一貫しており、NA
を入力するとNA
が出力されるため、パイプラインの途中で予期せぬエラーが起きにくくなっています。
まずは、stringr
パッケージをインストールして、読み込みましょう。
“`R
install.packages(“stringr”) # 未インストールの場合は実行
library(stringr)
“`
4.2. stringr
の主要関数とBase Rとの対応
stringr
の関数は、Base Rの関数と一対一で対応しているものが多く、考え方は非常に似ています。
stringr 関数 |
Base R 対応関数 | 機能概要 |
---|---|---|
str_detect() |
grepl() |
パターンの検出(論理値) |
str_subset() |
grep(value = TRUE) |
マッチした要素を抽出 |
str_which() |
grep() |
マッチした要素のインデックス |
str_replace() |
sub() |
最初にマッチした部分を置換 |
str_replace_all() |
gsub() |
すべてマッチした部分を置換 |
str_extract() |
regmatches +regexpr |
最初にマッチした部分を抽出 |
str_extract_all() |
regmatches +gregexpr |
すべてマッチした部分を抽出 |
str_match() |
(対応なし) | グループキャプチャ部分を抽出 |
stringr
を使うと、先ほどのBase Rでの操作がどれだけシンプルになるか見てみましょう。サンプルデータは同じものを使います。
“`R
サンプルデータ
texts <- c(
“R is a language for statistical computing.”,
“My phone number is 090-1234-5678.”,
“Please send an email to [email protected] for details.”,
“Product IDs are A-101, B-202, and C-303.”,
“No relevant information here (N/A).”
)
“`
検出 (str_detect
)
“`R
数字を含むか?
str_detect(texts, “\d”)
[1] FALSE TRUE TRUE TRUE FALSE
``
greplとほぼ同じですが、引数の順序が
stringr`スタイルになっています。
置換 (str_replace_all
)
“`R
すべての数字を”#”に置換
str_replace_all(texts, “\d”, “#”)
[1] “R is a language for statistical computing.”
[2] “My phone number is ###-####-####.”
[3] “Please send an email to [email protected] for details.”
[4] “Product IDs are A-###, B-###, and C-###.”
[5] “No relevant information here (N/A).”
``
gsub
後方参照もと同様に使えます:
str_replace(text, “(…)(…)”, “\2\1”)`
抽出 (str_extract
, str_extract_all
)
これがstringr
の強みが特に光る部分です。
“`R
ids_text <- “Product IDs are A-101, B-202, and C-303.”
id_pattern <- “\w-\d+”
最初にマッチしたものだけを抽出
str_extract(ids_text, id_pattern)
[1] “A-101”
すべてマッチしたものを抽出(戻り値はリスト)
str_extract_all(ids_text, id_pattern)
[[1]]
[1] “A-101” “B-202” “C-303”
``
gregexpr
Base Rのようにと
regmatchesを組み合わせる必要がなく、
str_extract_all`一発で済むのが大きな利点です。
4.3. グループキャプチャの決定版: str_match()
/ str_match_all()
stringr
が提供するstr_match()
は、Base Rにはない非常に便利な関数です。これは、パターン全体のマッチに加えて、()
でキャプチャした各グループにマッチした部分を、綺麗な行列(matrix)形式で返してくれます。
電話番号の例で見てみましょう。
“`R
phone_text <- “My phone number is 090-1234-5678.”
pattern <- “(\d{3})-(\d{4})-(\d{4})”
str_match を使用
match_result <- str_match(phone_text, pattern)
print(match_result)
[,1] [,2] [,3] [,4]
[1,] “090-1234-5678” “090” “1234” “5678”
``
()
結果が見やすい行列になっているのがわかりますか?
* 1列目: パターン全体にマッチした文字列
* 2列目: 1番目の(市外局番)にマッチした文字列
()
* 3列目: 2番目の(市内局番)にマッチした文字列
()`(加入者番号)にマッチした文字列
* 4列目: 3番目の
この形式は、データフレームに変換して後続の分析に使うのに非常に便利です。
4.4. tidyverse
との連携プレイ
stringr
の真価は、dplyr
と組み合わせたときに最大限に発揮されます。
“`R
library(dplyr)
library(stringr)
サンプルデータフレーム
sales_data <- tibble(
product_code = c(“TV-2023-001”, “AUDIO-2022-045”, “PC-2023-110”, “INVALID-CODE”),
sales = c(150000, 35000, 220000, 1000)
)
2023年の製品だけをフィルタリングし、
製品カテゴリと製造年を新しい列として追加する
sales_data %>%
# str_detect で2023年の製品をフィルタリング
filter(str_detect(product_code, “-2023-“)) %>%
# str_match でカテゴリと年を抽出して新しい列に追加
mutate(
match_cols = str_match(product_code, “(\w+)-(\d{4})-\d+”),
category = match_cols[, 2], # マッチ結果の2列目(カテゴリ)
year = match_cols[, 3] # マッチ結果の3列目(年)
) %>%
select(product_code, category, year, sales) # 必要な列を選択
# A tibble: 2 × 4
product_code category year sales
1 TV-2023-001 TV 2023 150000
2 PC-2023-110 PC 2023 220000
``
stringr`の関数を自然に組み込むことで、複雑なデータクレンジングや特徴量エンジニアリングを、可読性の高いコードでエレガントに実現できます。
このように、データフレームの操作パイプラインの中に
第5章: 実践演習 – よくある課題を解決しよう
さあ、これまで学んだ知識を総動員して、よくある課題に挑戦してみましょう。解答例も載せますが、まずは自分でパターンを考えてみてください。
課題1: テキストからのメールアドレス抽出
以下のテキストから、メールアドレス形式の文字列をすべて抜き出してください。
R
text_block <- "連絡先一覧: 太郎さん<[email protected]>, 花子さん<[email protected]>, 事務局<[email protected]>。無効なアドレス: [email protected], user@localhost"
ヒント:
* メールアドレスの@
より前(ローカル部)は、英数字、アンダースコア_
、ドット.
、ハイフン-
が使われることが多いです。
* @
より後(ドメイン部)は、英数字、ハイフン-
、ドット.
で構成されます。
* str_extract_all
が便利です。
解答例:
“`R
パターン: [ローカル部の文字]+@[ドメイン部の文字]+
ローカル部: [\w.-]+ (\wは英数字_, .はドット, -はハイフン)
ドメイン部: [\w-]+.\w+[\w.-]*
よりシンプルなパターンでも可:
email_pattern <- “[\w.-]+@[\w.-]+\.\w+”
str_extract_all(text_block, email_pattern)
[[1]]
[1] “[email protected]” “[email protected]” “[email protected]”
“`
課題2: 郵便番号のフォーマット統一
123-4567
や1234567
と混在したフォーマットを、すべて〒123-4567
の形式に統一してください。
R
zip_codes <- c("私の住所は150-0043です。", "会社は1000005です。", "〒163-8001 (東京都庁)")
ヒント:
* ハイフンはあってもなくても良い(?
が使える)。
* str_replace_all
と後方参照\\1
, \\2
を使いましょう。
* ()
で3桁の数字と4桁の数字をそれぞれキャプチャします。
解答例:
“`R
パターン: (3桁数字)(ハイフン?)(4桁数字)
グループ1: 3桁の数字, グループ2: ハイフン(オプション), グループ3: 4桁の数字
\d{3}と\d{4}をキャプチャする
zip_pattern <- “(\d{3})-?(\d{4})”
置換後: 〒[グループ1]-[グループ2]
str_replace_all(zip_codes, zip_pattern, “〒\1-\2”)
[1] “私の住所は〒150-0043です。” “会社は〒100-0005です。” “〒163-8001 (東京都庁)”
“`
課題3: ログファイルからの情報抽出
以下のようなログデータから、タイムスタンプ、ログレベル、メッセージをそれぞれ抽出してデータフレームにしてください。
R
logs <- c(
"[2023-10-27 10:30:00] [INFO] Server started successfully.",
"[2023-10-27 10:35:12] [WARN] Disk space is running low.",
"[2023-10-27 10:40:05] [ERROR] User login failed for 'user_A'."
)
ヒント:
* str_match
が最適です。
* []
はメタ文字なので、エスケープが必要です(\\[
と\\]
)。
* タイムスタンプ、レベル、メッセージをそれぞれ()
でキャプチャします。
解答例:
“`R
パターン: [ (タイムスタンプ) ] [ (レベル) ] (メッセージ)
タイムスタンプ: .+? (最短マッチ)
レベル: \w+
メッセージ: .+
log_pattern <- “\[(.+?)\]\s\[(\w+)\]\s(.+)”
log_matrix <- str_match(logs, log_pattern)
行列をデータフレームに変換
log_df <- as_tibble(log_matrix, .name_repair = “minimal”) %>%
setNames(c(“full_match”, “timestamp”, “level”, “message”)) %>%
select(-full_match)
print(log_df)
# A tibble: 3 × 3
timestamp level message
1 2023-10-27 10:30:00 INFO Server started successfully.
2 2023-10-27 10:35:12 WARN Disk space is running low.
3 2023-10-27 10:40:05 ERROR User login failed for ‘user_A’.
“`
まとめ
この記事では、R言語で正規表現を学ぶための第一歩として、その基本から実践的な応用までを駆け足で巡ってきました。
- 正規表現の基本:
.
、[]
、*
、+
、?
、^
、$
といったメタ文字が、文字列の柔軟な「パターン」を記述するための強力な言語であることを学びました。 - Base Rでの操作:
grep
,grepl
,sub
,gsub
といった伝統的な関数を使い、パターンの検出、置換、抽出を行う方法を習得しました。特に、置換における後方参照\\1
の便利さを体験しました。 stringr
によるモダンなアプローチ:str_detect
,str_extract_all
,str_replace_all
といった一貫性のある関数群と、グループキャプチャに絶大な威力を発揮するstr_match
の使い方を学びました。tidyverse
との連携がいかに強力であるかも理解できたはずです。
正規表現の世界は非常に奥深く、この記事で紹介したのはその入り口に過ぎません。先読み/後読み、再帰パターンなど、さらに高度なテクニックも存在します。しかし、今日学んだ知識だけでも、あなたが日常的に遭遇する文字列操作の課題のほとんどは解決できるはずです。
正規表現は、何よりも「習うより慣れよ」の世界です。最初は複雑に見えるパターンも、自分で書いたり、他人の書いたパターンを読んだりするうちに、自然と身についていきます。
学習を続ける上で、Regex101 のようなオンラインの正規表現テストツールは非常に役立ちます。リアルタイムでパターンのマッチ結果を確認できるので、試行錯誤しながら学ぶのに最適です。
今日、あなたは文字列操作の最強ツールを手に入れました。この新たなスキルを武器に、これまで以上に効率的で、創造的なデータ分析を楽しんでください。あなたのR言語の世界は、間違いなく、より一層広がりを見せることでしょう。