はじめに
文字列処理・テキスト処理とは、プログラミングを行うなかで、文字列・テキストに対する色々な操作のことを指します。それら処理をうまく使いこなすことで、文字列を自由に処理できるようになります。文字列処理の活用事例は、キーワード抽出、テキスト分類、テキストマイニングの前処理など、多岐に渡ります。 今回の「Rでの文字列処理」シリーズで扱う、文字列処理のライブラリ・関数群やプログラムコードは、R環境上で無料で提供されている、オープン・ソフトウェアを用います。
『その1: テキストファイルの読み込み・文字列の分割』では、Rでのテキストファイルの読み込み、および文字列の分割について、いろいろと試してまとめてみました。主に、baseやstringrとかのパッケージ内の関数群を扱います。
結論としては、ファイル読み込み関数は用途によって使い分けが必要である。また、文字列の分割については、stringr::str_split関数を使うのが全般的に良い印象である。
テキスト処理の関連記事
下準備について
######################## #シリーズ共通 ######################## #必要なパッケージの読み込み require(readr) require(stringr) require(stringdist) #テストファイルのダウンロード utils::download.file(url="https://raw.githubusercontent.com/kumeS/Blog/master/TXT_proc/test.txt", destfile="test.txt")
テキストファイルの読み込み について
【1】テキストファイルを1行ごと読み込んで、ベクトルにする。
まずは、file関数、readLines関数を使って試してみる。
file_obj <- file("./test.txt") a <- readLines(file_obj, n=10); close(file_obj) a #[1] "abc ABC abc" "ABC abc ABC" "def DEF def" "DEF def DEF" "abc.ABC.abc" #[6] "ABC.abc.ABC"
【2】テキストファイルを文字列で読み込んで、ベクトルにする。
read_file関数を使った、ファイル読み込むを試してみる。
b <- readr::read_file(file="./test.txt") b #[1] "abc ABC abc\nABC abc ABC\ndef DEF def\nDEF def DEF\nabc.ABC.abc\nABC.abc.ABC\n"
このままでは何とも使いづらいので、\n
で文字列の分割を行う。
b1 <- base::strsplit(b, split="\n")[[1]] #引数 #split: 分割起点となる文字列ベクトル b1 #[1] "abc ABC abc" "ABC abc ABC" "def DEF def" "DEF def DEF" "abc.ABC.abc" #[6] "ABC.abc.ABC"
【3】テキストファイルを読み込んで、データフレームにする。
read_tsv関数を使ってファイルを読み込むで、その後、データフレームに変換する。
c <- data.frame(readr::read_tsv(file="./test.txt", col_names=F)) #─ Column specification ───────────────────────── #cols( # X1 = col_character() #) c # X1 #1 abc ABC abc #2 ABC abc ABC #3 def DEF def #4 DEF def DEF #5 abc.ABC.abc #6 ABC.abc.ABC
文字列の分割 について
【1】スペースごとで分割して、各文字列をカラムごとに分ける
ベクトル c 内の各要素(特に、c$X1[1:4]
)について、
スペース区切りで文字列を分割して、別カラムに分ける場合を考えてみる。
まずは、base::strsplit関数での実行例を示す。
ベクトル全体に対する文字列の分割では、list形式で出力されるので、 一度、unlist関数でベクトルに変換して、その後にデータ型を調整する。
c1 <- data.frame(X1=c$X1[1:4]) c1 #base::strsplitでの実行例 #文字列の分割 base::strsplit(c1$X1[1], split=" ") #[[1]] #[1] "abc" "ABC" "abc" #ベクトル全体に対する文字列の分割 base::strsplit(c1$X1, split=" ") #[[1]] #[1] "abc" "ABC" "abc" #[[2]] #[1] "ABC" "abc" "ABC" #[[3]] #[1] "def" "DEF" "def" #[[4]] #[1] "DEF" "def" "DEF" #ベクトル全体に対する文字列の分割を行い、データフレームあるいは行列で返す data.frame(Y=matrix(unlist(base::strsplit(c1$X1, split=" ")), ncol=3, byrow=T)) # Y.1 Y.2 Y.3 #1 abc ABC abc #2 ABC abc ABC #3 def DEF def #4 DEF def DEF #行列で返す場合 matrix(unlist(base::strsplit(c1$X1, split=" ")), ncol=3, byrow=T) # [,1] [,2] [,3] #[1,] "abc" "ABC" "abc" #[2,] "ABC" "abc" "ABC" #[3,] "def" "DEF" "def" #[4,] "DEF" "def" "DEF"
次に、stringr::str_split関数での実行例を示す。
stringr::str_split関数は、デフォルト出力ではベクトルでの出力だが、引数をsimplify = T
にすることで、
文字列分割後の出力形式を「行列」で返してくれる。
c1 <- data.frame(X1=c$X1[1:4]) c1 #stringr::str_splitでの実行例 #文字列の分割 stringr::str_split(c1$X1[1], pattern = " ") #[[1]] #[1] "abc" "ABC" "abc" #ベクトル全体に対する文字列の分割 stringr::str_split(c1$X1, pattern = " ",) #[[1]] #[1] "abc" "ABC" "abc" #[[2]] #[1] "ABC" "abc" "ABC" #[[3]] #[1] "def" "DEF" "def" #[[4]] #[1] "DEF" "def" "DEF" #ベクトル全体に対する文字列の分割を行い、データフレームで返す場合 data.frame(stringr::str_split(c1$X1, pattern = " ", simplify = T)) # X1 X2 X3 #1 abc ABC abc #2 ABC abc ABC #3 def DEF def #4 DEF def DEF #そのまま、行列で返す場合 stringr::str_split(c1$X1, pattern = " ", simplify = T) # [,1] [,2] [,3] #[1,] "abc" "ABC" "abc" #[2,] "ABC" "abc" "ABC" #[3,] "def" "DEF" "def" #[4,] "DEF" "def" "DEF"
【2】ピリオド(.)で分割して、文字列をカラムごとに分ける
ベクトル内の各要素(特に、c$X1[5:6]
)について、
ピリオド区切りで文字列を分割して、別カラムに分けたい場合を考えてみる。
この場合、少しだけ工夫が必要で、パターン文字列を"."
ではなく、"[.]"
あるいは"\\."
とする。
c2 <- data.frame(X1=c$X1[5:6]) c2 #base::strsplitについては省略 #stringr::str_splitでの実行例 #文字列の分割 #失敗例 stringr::str_split(c2$X1[1], pattern = ".") #[[1]] # [1] "" "" "" "" "" "" "" "" "" "" "" "" #成功例(1): [ ]括弧をつける stringr::str_split(c2$X1[1], pattern = "[.]") #[[1]] #[1] "abc" "ABC" "abc" #成功例(2): バックスラッシュ(\)x2 stringr::str_split(c2$X1[1], pattern = "\\.") #[[1]] #[1] "abc" "ABC" "abc" #ベクトル全体に対する文字列の分割を行い、データフレームで返す data.frame(stringr::str_split(c2$X1, pattern = "[.]", simplify = T)) # X1 X2 X3 #1 abc ABC abc #2 ABC abc ABC
【3】(応用編)区切るパターンが異なる、文字列ベクトルでの処理
次に、スペースとピリオドでの文字列の分割を同時に行う事例を取りあげる。
それぞれの文字列パターンをベクトルで与える、あるいは、| (パイプ)を使ってOR処理をする場合がある。
#スペース区切りでの出力例: 5, 6行目は分割されない stringr::str_split(c$X1, pattern = " ", simplify = T) # [,1] [,2] [,3] #[1,] "abc" "ABC" "abc" #[2,] "ABC" "abc" "ABC" #[3,] "def" "DEF" "def" #[4,] "DEF" "def" "DEF" #[5,] "abc.ABC.abc" "" "" #[6,] "ABC.abc.ABC" "" "" #ピリオド区切りでの出力例: 1-4行目は分割されない stringr::str_split(c$X1, pattern = "[.]", simplify = T) # [,1] [,2] [,3] #[1,] "abc ABC abc" "" "" #[2,] "ABC abc ABC" "" "" #[3,] "def DEF def" "" "" #[4,] "DEF def DEF" "" "" #[5,] "abc" "ABC" "abc" #[6,] "ABC" "abc" "ABC" #複数の区切りパターンを設定する場合 #行ごとに、区切りパターン(スペースあるいはピリオド)を変えて、文字列を分割する stringr::str_split(c$X1, pattern = c(" ", " ", " ", " ", "[.]", "[.]"), simplify = T) # [,1] [,2] [,3] #[1,] "abc" "ABC" "abc" #[2,] "ABC" "abc" "ABC" #[3,] "def" "DEF" "def" #[4,] "DEF" "def" "DEF" #[5,] "abc" "ABC" "abc" #[6,] "ABC" "abc" "ABC" #スペースあるいはピリオドの区切りパターンで、文字列を分割する stringr::str_split(c$X1, pattern = " |[.]", simplify = T) # [,1] [,2] [,3] #[1,] "abc" "ABC" "abc" #[2,] "ABC" "abc" "ABC" #[3,] "def" "DEF" "def" #[4,] "DEF" "def" "DEF" #[5,] "abc" "ABC" "abc" #[6,] "ABC" "abc" "ABC"
また、出力するカラム数を指定したい場合には、stringr::str_split_fixed関数を用いる。
#スペースあるいはピリオドの区切りパターンで、文字列を分割して、2列で返す stringr::str_split_fixed(c$X1, pattern=" |[.]", n=2) # [,1] [,2] #[1,] "abc" "ABC abc" #[2,] "ABC" "abc ABC" #[3,] "def" "DEF def" #[4,] "DEF" "def DEF" #[5,] "abc" "ABC.abc" #[6,] "ABC" "abc.ABC" #スペースあるいはピリオドの区切りパターンで、文字列を分割して、3列で返す stringr::str_split_fixed(c$X1, pattern=" |[.]", n=3) # [,1] [,2] [,3] #[1,] "abc" "ABC" "abc" #[2,] "ABC" "abc" "ABC" #[3,] "def" "DEF" "def" #[4,] "DEF" "def" "DEF" #[5,] "abc" "ABC" "abc" #[6,] "ABC" "abc" "ABC" #スペースあるいはピリオドの区切りパターンで、文字列を分割して、4列で返す #4列目は、空列で出力される。 stringr::str_split_fixed(c$X1, pattern=" |[.]", n=4) # [,1] [,2] [,3] [,4] #[1,] "abc" "ABC" "abc" "" #[2,] "ABC" "abc" "ABC" "" #[3,] "def" "DEF" "def" "" #[4,] "DEF" "def" "DEF" "" #[5,] "abc" "ABC" "abc" "" #[6,] "ABC" "abc" "ABC" ""
補足
(
とか、 )
とか、の括弧で、文字列を分割する場合
[(]
と[)]
を組み合わせることで、括弧を指定できる。
# ( ) を区切りパターンとして、文字列を分割する Ex <- "AAA(BBB)" stringr::str_split(Ex, pattern = "[(]|[)]", simplify = T) # [,1] [,2] [,3] #[1,] "AAA" "BBB" "" stringr::str_split_fixed(Ex, pattern="[(]|[)]", n=3) # [,1] [,2] [,3] #[1,] "AAA" "BBB" ""