Rのstrsplitをマスター!効率的な文字列分割とデータ整形テクニック

2025-05-27

strsplit の基本的な使い方

strsplit の基本的な構文は以下の通りです。

strsplit(x, split, fixed = FALSE, perl = FALSE, useBytes = FALSE)

主な引数は以下の通りです。

  • useBytes: 論理値。バイト単位でマッチングを行うかどうかを指定します。
  • perl: 論理値。fixed = FALSE の場合にPerl互換の正規表現を使用するかどうかを指定します。
  • fixed: 論理値 (TRUE または FALSE)。
    • fixed = FALSE (デフォルト): split を正規表現として扱います。
    • fixed = TRUE: split を厳密な文字列として扱います。特殊文字(例: .* など)を正規表現として解釈せず、文字通りにマッチさせたい場合に便利です。
  • split: 分割の基準となる区切り文字または正規表現です。
  • x: 分割したい文字ベクトル(文字列)です。

動作のイメージ

strsplit は、入力された文字ベクトル x の各要素に対して split で指定されたパターンを探し、そのパターンが見つかるたびに文字列を分割します。結果は、リストとして返されます。これは、入力 x が複数の文字列を含む場合や、1つの文字列が複数に分割される場合に、それぞれの結果を格納するためです。

使用例

いくつか例を見てみましょう。

スペースで単語に分割する

最も一般的な使い方の1つです。

sentence <- "R programming is powerful"
strsplit(sentence, " ")

出力:

[[1]]
[1] "R"           "programming" "is"          "powerful"   

結果はリストであり、そのリストの最初の要素が、スペースで分割された単語のベクトルになっています。

カンマで数値を分割する

CSVのようなデータを処理する際によく使われます。

numbers_str <- "10,20,30,40"
strsplit(numbers_str, ",")
[[1]]
[1] "10" "20" "30" "40"

正規表現を使って分割する

fixed = FALSE の場合、split は正規表現として扱われます。例えば、数字で分割したい場合など。

text_with_numbers <- "apple123banana456cherry"
strsplit(text_with_numbers, "[0-9]+") # 1つ以上の数字で分割
[[1]]
[1] "apple"  "banana" "cherry"

fixed = TRUE を使う場合

もし split に正規表現の特殊文字(例: .)が含まれていて、それを文字通りに区切り文字として使いたい場合は fixed = TRUE を指定します。

filepath <- "folder.subfolder.file.txt"
strsplit(filepath, ".", fixed = TRUE) # ピリオドを文字通りに区切り文字として扱う
[[1]]
[1] "folder"   "subfolder" "file"    "txt"    

もし fixed = FALSE のまま strsplit(filepath, ".") とすると、ピリオドが「任意の1文字」という正規表現として解釈され、予期しない結果になる可能性があります。

複数の文字列を一度に分割する

x が複数の文字列を含む文字ベクトルの場合、strsplit はそれぞれの文字列に対して処理を行います。

dates <- c("2023-01-15", "2024-03-22", "2025-07-01")
strsplit(dates, "-")
[[1]]
[1] "2023" "01"   "15"  

[[2]]
[1] "2024" "03"   "22"  

[[3]]
[1] "2025" "07"   "01"  

各要素がそれぞれ分割された結果のベクトルを持つリストが返されます。

  • 区切り文字が複数連続する場合: デフォルトでは、連続する区切り文字は1つの区切りとして扱われます。例えば、strsplit("a__b", "_")list(c("a", "b")) を返します。間に空の要素を挟みたい場合は、正規表現の工夫が必要になることがあります。
  • 空文字列での分割: split = "" または split = character(0) とすると、文字列が1文字ずつ分割されます。
    chars <- strsplit("R言語", "")
    print(chars)
    # [[1]]
    # [1] "R" "言" "語"
    
  • 戻り値はリストである: strsplit は常にリストを返します。もし、単一の文字列を分割して結果をすぐにベクトルとして使いたい場合は、unlist() 関数と組み合わせることが一般的です。
    words <- unlist(strsplit("Hello world", " "))
    print(words)
    # [1] "Hello" "world"
    


戻り値がリストであることを忘れる

エラー/問題
strsplit の結果を直接ベクトルとして扱おうとする。

# 間違いの例
text_data <- "apple,banana,cherry"
split_result <- strsplit(text_data, ",")
# split_result[1] とアクセスするとリストの要素が返されるため、
# 期待する動作(例: "apple"という文字列を得る)にならない
print(split_result[1])
# [[1]]
# [1] "apple"  "banana" "cherry" 

# さらに、例えば split_result[1][1] とするとエラーになる(リスト内のベクトルに直接アクセスできない)
# print(split_result[1][1]) # これはエラーになる

説明
strsplit は常にリストを返します。これは、入力 x が複数の文字列を含む場合(例: c("a,b", "c,d"))、それぞれの文字列の分割結果を独立したベクトルとして保持する必要があるためです。単一の文字列を分割した場合でも、結果は1つの要素を持つリストになります。

トラブルシューティング

  • unlist() を使う
    strsplit の結果が複数の要素を持つリストである場合でも、すべての要素を一つのベクトルに結合したい場合は unlist() を使います。
    texts_data <- c("apple,banana", "cherry,date")
    split_vectors <- strsplit(texts_data, ",")
    print(split_vectors)
    # [[1]]
    # [1] "apple"  "banana"
    # [[2]]
    # [1] "cherry" "date"
    
    all_elements <- unlist(split_vectors)
    print(all_elements)
    # [1] "apple"  "banana" "cherry" "date"  
    
  • [[1]] でリストから要素を取り出す
    単一の文字列を分割した場合は、[[1]] を使ってリストの最初の要素(分割された文字列のベクトル)を取り出します。
    text_data <- "apple,banana,cherry"
    split_vector <- strsplit(text_data, ",")[[1]]
    print(split_vector)
    # [1] "apple"  "banana" "cherry"
    

split 引数の誤解 (正規表現 vs. 固定文字列)

エラー/問題
区切り文字として特殊文字(例: .* など)を使用したが、意図しない結果になる。

# 間違いの例: ピリオドを文字通りに分割したいのに、任意の1文字として解釈される
file_name <- "document.v1.txt"
strsplit(file_name, ".") # 結果が空の文字列になる

説明
strsplitsplit 引数は、デフォルトで正規表現として解釈されます(fixed = FALSE)。正規表現では、. は「任意の1文字」を意味するため、上記の例では document.v1.txt のすべての文字が分割されてしまい、空の文字列が多数含まれるリストが返されます。同様に、*+? なども正規表現の特殊文字です。

トラブルシューティング

  • 正規表現の特殊文字をエスケープする
    fixed = FALSE のままで特殊文字を文字通りに扱いたい場合は、その文字の前にバックスラッシュ (\) を付けてエスケープします。ただし、Rの文字列内でバックスラッシュを表現するためには、さらにバックスラッシュを重ねて \\ とする必要があります。
    file_name <- "document.v1.txt"
    split_parts <- strsplit(file_name, "\\.") # 正規表現としてピリオドをエスケープ
    print(split_parts[[1]])
    # [1] "document" "v1"       "txt"     
    
    これは特に複雑な正規表現を使用する場合に重要です。
  • fixed = TRUE を使う
    split 引数を文字通りに(固定文字列として)解釈させたい場合は、fixed = TRUE を指定します。
    file_name <- "document.v1.txt"
    split_parts <- strsplit(file_name, ".", fixed = TRUE)
    print(split_parts[[1]])
    # [1] "document" "v1"       "txt"     
    

区切り文字が文字列の先頭や末尾にある場合

エラー/問題
文字列の先頭や末尾に区切り文字がある場合、空の文字列が結果に含まれる。

# 問題の例
data_string <- ",item1,item2,"
strsplit(data_string, ",")
[[1]]
[1] ""      "item1" "item2" ""     

説明
strsplit は、区切り文字が見つかるたびにその位置で分割します。そのため、先頭や末尾に区切り文字がある場合、その前後に空の文字列があると解釈されます。

トラブルシューティング

  • 結果から空の文字列を除外する
    nchar()nzchar() を使って、分割後に空の文字列を除外します。
    data_string <- ",item1,item2,"
    split_result <- strsplit(data_string, ",")[[1]]
    cleaned_result <- split_result[nchar(split_result) > 0] # または nzchar(split_result)
    print(cleaned_result)
    # [1] "item1" "item2"
    
  • 前処理でトリミングする
    trimws() (R 3.2.0以降) や gsub() などを使って、事前に不要な区切り文字を削除する。
    data_string <- ",item1,item2,"
    trimmed_string <- gsub("^,|,", "", data_string) # 先頭のカンマと末尾のカンマを削除
    strsplit(trimmed_string, ",")[[1]]
    # [1] "item1" "item2"
    

連続する区切り文字の扱い

エラー/問題
複数の区切り文字が連続している場合、間に空の文字列が含まれない。

# 問題の例: 複数のスペースで分割したいが、間に空の文字列がない
sentence <- "R  programming is   powerful"
strsplit(sentence, " ")
[[1]]
[1] "R"           ""            "programming" "is"          ""            ""            "powerful"

説明
デフォルトでは、strsplit は連続する区切り文字ごとに空の要素を生成します。上記ではスペースが連続しているため、間に "" が入っています。もし「1つ以上のスペース」で分割したい場合は、正規表現を使います。

トラブルシューティング

  • 正規表現 + を使う
    split 引数で正規表現の + を使うと、「1回以上の繰り返し」にマッチします。
    sentence <- "R  programming is   powerful"
    strsplit(sentence, " +") # 1つ以上のスペースで分割
    
    出力:
    [[1]]
    [1] "R"           "programming" "is"          "powerful"   
    
    これで間に空の文字列が入らなくなります。

NA 値を含む文字列の扱い

エラー/問題
入力 xNA が含まれる場合、strsplit は警告を出すか、予期せぬ結果になる。

# 問題の例
data_vec <- c("a,b", NA, "c,d")
strsplit(data_vec, ",")
[[1]]
[1] "a" "b"

[[2]]
[1] NA

[[3]]
[1] "c" "d"

説明
strsplitNA 値をそのまま NA として扱います。これは通常期待される動作ですが、もし NA 値をスキップしたり、特定の値で置き換えたい場合は、事前に処理が必要です。

トラブルシューティング

  • NA 値を別の値で置き換える
    data_vec <- c("a,b", NA, "c,d")
    data_vec[is.na(data_vec)] <- "" # または他の適切な値
    strsplit(data_vec, ",")
    
  • NA 値をフィルタリングする
    data_vec <- c("a,b", NA, "c,d")
    clean_data_vec <- data_vec[!is.na(data_vec)]
    strsplit(clean_data_vec, ",")
    
# 問題の例 (環境依存)
japanese_text <- "R言語プログラミング"
strsplit(japanese_text, "") # 環境によっては正しく分割されない可能性

説明
Rの文字列エンコーディングの扱いは、特に異なるOSや環境間でファイルをやり取りする場合に問題になることがあります。strsplit が期待通りに動作しない場合、文字列の内部エンコーディングがRが認識しているものと異なる可能性があります。

トラブルシューティング

  • useBytes = TRUE を試す
    これはバイト単位での処理を強制するため、エンコーディングの問題を回避できる場合がありますが、意図しない結果になる可能性もあります。最終手段として検討してください。

strsplit を効果的に使うためには、以下の点を常に意識することが重要です。

  • 文字列の前後のスペースや、連続する区切り文字の扱いに注意すること。
  • split 引数が正規表現として解釈されることと、fixed = TRUE の重要性。
  • 戻り値はリストであること。


基本的な文字列の分割

最も基本的な使い方です。スペースやカンマなどの一般的な区切り文字で文字列を分割します。

# 例1: スペースで単語を分割
sentence <- "R programming is fun and powerful"
words <- strsplit(sentence, " ")[[1]] # [[1]] でリストからベクトルを取り出す
print(words)

# 出力:
# [1] "R"           "programming" "is"          "fun"         "and"         "powerful"   

# 例2: カンマ区切りの数値を分割し、数値型に変換
numbers_str <- "10,25,50,75,100"
numbers_char <- strsplit(numbers_str, ",")[[1]]
numbers_numeric <- as.numeric(numbers_char) # 数値型に変換
print(numbers_numeric)
print(class(numbers_numeric))

# 出力:
# [1]  10  25  50  75 100
# [1] "numeric"

fixed = TRUE を使った固定文字列での分割

区切り文字が正規表現の特殊文字(例: ., *, +, ? など)である場合、fixed = TRUE を使うと、その文字を文字通りに解釈して分割できます。

# 例3: ファイルパスをピリオドで分割(拡張子などを取得)
file_path <- "document.report.v2.pdf"
parts_regex <- strsplit(file_path, ".") # 間違い: ピリオドが正規表現の「任意の1文字」として解釈される
print(parts_regex) # 多数の空文字列を含むリストになる

parts_fixed <- strsplit(file_path, ".", fixed = TRUE)[[1]] # 正しい: ピリオドを固定文字列として扱う
print(parts_fixed)

# 出力:
# [[1]]
#  [1] "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" `split_vector` の結果は、各行に対して単一の文字列を期待しており、その結果をリストとして受け取ります。`[[1]]` を使用することで、リストの最初の要素(つまり、最初の文字列の分割結果)にアクセスし、それをベクトルとして扱うことができます。

### 3. 正規表現を使ったより高度な分割

`strsplit` はPerl互換の正規表現をサポートしており、非常に柔軟なパターンマッチングと分割が可能です。

```R
# 例4: 数字で分割する(1つ以上の連続する数字)
text_data <- "itemA-123-itemB_45_itemC"
split_by_numbers <- strsplit(text_data, "[0-9]+")[[1]]
print(split_by_numbers)

# 出力:
# [1] "itemA-" "itemB_" "itemC" 

# 例5: 複数の区切り文字のいずれかで分割(例: カンマまたはセミコロン)
multi_delim_data <- "apple,banana;cherry,date"
split_multi <- strsplit(multi_delim_data, "[,;]")[[1]] # カンマまたはセミコロン
print(split_multi)

# 出力:
# [1] "apple"  "banana" "cherry" "date"  

# 例6: 先頭または末尾の区切り文字を考慮しない分割
# これはよくあるケースで、strsplitの直後にトリミングを行うと便利です
messy_string <- ",value1,,value2,value3,"
# 区切り文字を正規表現として使用し、空の要素を削除
split_cleaned <- strsplit(messy_string, ",+")[[1]] # 1つ以上のカンマで分割
split_cleaned <- split_cleaned[nzchar(split_cleaned)] # 空の文字列を削除
print(split_cleaned)

# 出力:
# [1] "value1" "value2" "value3"

複数の文字列に対する適用

strsplit はベクトル化されているため、複数の文字列に対して一度に分割操作を行うことができます。戻り値は常にリストになります。

# 例7: 複数の日付文字列をハイフンで分割
dates <- c("2023-01-15", "2024-03-22", "2025-11-05")
split_dates <- strsplit(dates, "-")
print(split_dates)

# 出力:
# [[1]]
# [1] "2023" "01"   "15"  
# [[2]]
# [1] "2024" "03"   "22"  
# [[3]]
# [1] "2025" "11"   "05"  

# 必要に応じて、lapplyと組み合わせるか、後で unlist する
# 各日付の年のみを取り出す
years <- sapply(split_dates, `[`, 1) # 各リスト要素の最初の要素を取り出す
print(years)

# 出力:
# [1] "2023" "2024" "2025"

各文字への分割

split = "" (空文字列) を指定すると、文字列を1文字ずつ分割できます。

# 例8: 文字列を個々の文字に分割
word_jp <- "日本語"
chars_jp <- strsplit(word_jp, "")[[1]]
print(chars_jp)

# 出力:
# [1] "日" "本" "語"

# 英数字の場合
word_en <- "HelloWorld"
chars_en <- strsplit(word_en, "")[[1]]
print(chars_en)

# 出力:
# [1] "H" "e" "l" "l" "o" "W" "o" "r" "l" "d"

データフレームの特定の列(文字型)を分割し、新しい列を作成するなどの操作によく使われます。dplyr パッケージの separate 関数も同様の目的で使用できますが、strsplit をベースに手動で行うことも可能です。

# 例9: データフレームの列を分割し、新しい列に割り当てる
df <- data.frame(
  id = 1:3,
  full_name = c("John Doe", "Jane Smith-Jones", "Peter Pan"),
  stringsAsFactors = FALSE
)

# full_name 列をスペースで分割し、first_name と last_name を作成
split_names <- strsplit(df$full_name, " ")

# lapply を使って、各分割結果から最初の要素と2番目の要素を取り出す
df$first_name <- sapply(split_names, `[`, 1)
# 複数の単語がある場合に last_name を結合する処理(例: "Smith-Jones" をそのまま保持)
df$last_name <- sapply(split_names, function(x) paste(x[-1], collapse = " "))

print(df)

# 出力:
#   id        full_name first_name  last_name
# 1  1         John Doe       John        Doe
# 2  2 Jane Smith-Jones       Jane Smith-Jones
# 3  3        Peter Pan      Peter        Pan

strsplit は、Rでのデータクリーニングや前処理において不可欠な関数です。

  • 戻り値がリスト
    常にリストが返されることを意識し、[[1]]unlist()sapply() などで適切に処理する。
  • 正規表現の活用
    + (1回以上)、| (OR)、[] (文字クラス) などを組み合わせることで、複雑な分割パターンに対応。
  • fixed = TRUE
    正規表現の特殊文字を文字通りに扱いたい場合に必須。
  • 基本的な分割
    スペース、カンマ、ハイフンなどでシンプルに分割。


stringr パッケージ (tidyverse)

stringr パッケージは、Rでの文字列操作をより直感的かつ一貫性のあるものにするために設計された、tidyverse の一部です。strsplit に代わる便利な関数を提供します。

  • str_split(): strsplitstringr 版です。基本的な機能は同じですが、より使いやすく、特にデータフレームのパイプライン処理に統合しやすい特徴があります。

    • 特徴:
      • simplify = TRUE オプションで、リストではなく行列として結果を直接得られる。
      • n オプションで、分割する回数を指定できる。
      • より一貫した引数名とエラー処理。
    library(stringr)
    
    # str_splitの基本
    sentence <- "R programming is fun"
    words <- str_split(sentence, " ")
    print(words)
    # 出力: List of 1
    #  $ : chr [1:4] "R" "programming" "is" "fun"
    
    # simplify = TRUE で行列として取得
    words_matrix <- str_split(sentence, " ", simplify = TRUE)
    print(words_matrix)
    # 出力:
    #      [,1]          [,2]          [,3] [,4]
    # [1,] "R"           "programming" "is" "fun"
    
    # 複数行の文字列を一度に処理
    texts <- c("apple,banana", "cherry,date,grape")
    split_texts <- str_split(texts, ",")
    print(split_texts)
    # 出力: List of 2
    #  $ : chr [1:2] "apple" "banana"
    #  $ : chr [1:3] "cherry" "date" "grape"
    
    # n を指定して分割回数を制限
    path_str <- "folder/subfolder/file.txt"
    parts <- str_split(path_str, "/", n = 2) # 最初のスラッシュで一度だけ分割
    print(parts)
    # 出力: List of 1
    #  $ : chr [1:2] "folder" "subfolder/file.txt"
    
    # n を指定してsimplify = TRUE
    parts_matrix <- str_split(path_str, "/", n = 2, simplify = TRUE)
    print(parts_matrix)
    # 出力:
    #      [,1]     [,2]              
    # [1,] "folder" "subfolder/file.txt"
    

tidyr パッケージ (tidyverse)

tidyr パッケージは、データの整形に特化しており、特にデータフレームの列を分割する際に非常に強力です。

  • separate_rows(): カンマ区切りなどの文字列を、個々の要素ごとに新しい行に展開します。

    df_tags <- data.frame(
      id = 1:2,
      tags = c("R,Python,Data Science", "SQL,Excel"),
      stringsAsFactors = FALSE
    )
    
    df_tags_separated_rows <- df_tags %>%
      separate_rows(tags, sep = ",")
    print(df_tags_separated_rows)
    
    # 出力:
    #   id          tags
    # 1  1             R
    # 2  1        Python
    # 3  1  Data Science
    # 4  2           SQL
    # 5  2         Excel
    
  • separate(): データフレームの単一の文字型列を、指定された区切り文字で複数の新しい列に分割します。

    library(tidyr)
    library(dplyr) # パイプ演算子 %>% を使用するため
    
    df <- data.frame(
      id = 1:3,
      full_name = c("John Doe", "Jane Smith-Jones", "Peter Pan"),
      stringsAsFactors = FALSE
    )
    
    # full_name 列をスペースで分割し、first_name と last_name 列を作成
    df_separated <- df %>%
      separate(full_name, into = c("first_name", "last_name"), sep = " ")
    print(df_separated)
    
    # 出力:
    #   id first_name  last_name
    # 1  1       John        Doe
    # 2  2       Jane Smith-Jones
    # 3  3      Peter        Pan
    
    # n を指定して分割回数を制限することも可能
    df_paths <- data.frame(
      id = 1:2,
      path = c("home/user/document.txt", "data/raw/log.csv"),
      stringsAsFactors = FALSE
    )
    
    df_path_separated <- df_paths %>%
      separate(path, into = c("dir1", "remaining_path"), sep = "/", extra = "merge") # extra = "merge" で残りを結合
    print(df_path_separated)
    
    # 出力:
    #   id dir1 remaining_path
    # 1  1 home user/document.txt
    # 2  2 data    raw/log.csv
    

ベースRの他の関数との組み合わせ

strsplit 以外にも、gsub, gregexpr, regmatches などを組み合わせることで、より複雑な文字列抽出や分割を実現できます。

  • gregexpr()regmatches(): 正規表現にマッチするすべての部分を抽出する場合や、特定のパターンに基づいて分割されたくない部分を維持したい場合など。

    text_with_quotes <- 'He said "Hello World" and then "Goodbye".'
    
    # 引用符で囲まれた部分を抽出
    # gregexprでパターンにマッチする開始位置と長さを取得
    matches <- gregexpr("\"[^\"]+\"", text_with_quotes)
    # regmatchesでマッチした文字列を抽出
    quoted_phrases <- regmatches(text_with_quotes, matches)[[1]]
    print(quoted_phrases)
    # 出力:
    # [1] "\"Hello World\"" "\"Goodbye\""
    
    # 引用符で囲まれていない部分で分割 (少し複雑だが、より柔軟な制御が可能)
    # これは strsplit とは逆のアプローチで、「マッチしない部分」で分割するイメージ
    # まず、引用符で囲まれた部分を一時的にプレースホルダーに置き換える
    temp_text <- text_with_quotes
    for (phrase in quoted_phrases) {
      temp_text <- sub(phrase, "___PLACEHOLDER___", temp_text, fixed = TRUE)
    }
    
    # その後、プレースホルダーで分割
    parts <- strsplit(temp_text, "___PLACEHOLDER___")[[1]]
    # 空の要素を削除し、結果を調整
    parts <- parts[nzchar(parts)]
    print(parts)
    # 出力:
    # [1] "He said " " and then " "."
    
  • gsub()strsplit() の組み合わせ: 不要な部分を事前に置換してから分割する場合など。

    dirty_data <- "  ItemA   _ItemB_ _ItemC  "
    # 前後のスペースと連続するアンダースコアを削除してから分割
    cleaned_data <- gsub("^\\s+|\\s+$", "", dirty_data) # 前後のスペースを削除
    cleaned_data <- gsub("_+", "_", cleaned_data) # 連続するアンダースコアを1つに
    
    split_result <- strsplit(cleaned_data, "_")[[1]]
    print(split_result)
    # 出力:
    # [1] "ItemA" "ItemB" "ItemC"
    

stringi パッケージ

stringi パッケージは、Rの文字列処理のバックエンドとして ICU (International Components for Unicode) ライブラリを使用しており、非常に高速で堅牢な国際化対応の文字列操作機能を提供します。

  • stri_split_fixed() / stri_split_regex(): strsplit と同様の機能を提供し、より詳細な制御オプションや優れたパフォーマンスを持つことが多いです。

    library(stringi)
    
    # 固定文字列での分割 (fixed = TRUE に相当)
    file_path <- "document.v1.txt"
    parts_stri_fixed <- stri_split_fixed(file_path, ".")[[1]]
    print(parts_stri_fixed)
    # 出力:
    # [1] "document" "v1"       "txt"
    
    # 正規表現での分割 (デフォルトの strsplit に相当)
    text_data <- "itemA-123-itemB_45_itemC"
    split_by_numbers_stri_regex <- stri_split_regex(text_data, "[0-9]+")[[1]]
    print(split_by_numbers_stri_regex)
    # 出力:
    # [1] "itemA-" "itemB_" "itemC"
    
    # n を指定して分割回数を制限
    path_str <- "folder/subfolder/file.txt"
    parts_n <- stri_split_fixed(path_str, "/", n = 2)[[1]]
    print(parts_n)
    # 出力:
    # [1] "folder"             "subfolder/file.txt"
    
    # simplify オプションもあり、行列として結果を返せる
    parts_matrix_stri <- stri_split_fixed(c("a;b", "c;d;e"), ";", simplify = TRUE)
    print(parts_matrix_stri)
    # 出力:
    #      [,1] [,2] [,3] 
    # [1,] "a"  "b"  NA   
    # [2,] "c"  "d"  "e"
    
  • 複雑な正規表現パターンからの抽出・置換と組み合わせる:ベースRの gregexpr, regmatches, gsub などを組み合わせるか、stringrstringi のより高度な関数(例: str_extract_all)を検討します。
  • パフォーマンスが重要、または国際化対応stringi パッケージが最適です。
  • よりモダンで一貫性のあるAPIstringr::str_split() が推奨されます。特に simplify = TRUEn オプションが役立ちます。
  • データフレームの列分割tidyr::separate() が最も効率的で読みやすいコードになります。
  • シンプルな分割strsplit で十分です。