京橋のバイオインフォマティシャンの日常

南国のビーチパラソルの下で、Rプログラムを打ってる日常を求めて、、Daily Life of Bioinformatician in Kyobashi of Osaka

【Rでの文字列処理シリーズ(その1)】テキストファイルの読み込み・文字列の分割

はじめに

文字列処理・テキスト処理とは、プログラミングを行うなかで、文字列・テキストに対する色々な操作のことを指します。それら処理をうまく使いこなすことで、文字列を自由に処理できるようになります。文字列処理の活用事例は、キーワード抽出、テキスト分類、テキストマイニングの前処理など、多岐に渡ります。 今回の「Rでの文字列処理」シリーズで扱う、文字列処理のライブラリ・関数群やプログラムコードは、R環境上で無料で提供されている、オープン・ソフトウェアを用います。

『その1: テキストファイルの読み込み・文字列の分割』では、Rでのテキストファイルの読み込み、および文字列の分割について、いろいろと試してまとめてみました。主に、baseやstringrとかのパッケージ内の関数群を扱います。

結論としては、ファイル読み込み関数は用途によって使い分けが必要である。また、文字列の分割については、stringr::str_split関数を使うのが全般的に良い印象である。

テキスト処理の関連記事

skume.net

skume.net

skume.net

skume.net

skume.net

skume.net

下準備について

########################
#シリーズ共通
########################
#必要なパッケージの読み込み
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" ""