Rのデータフレームで検索と置換をする方法

普段からエクセルを使い慣れている人にとっては、「検索と置換」はよく使う機能だと思います。 Rのデータフレームで同様の処理をする機会があり、素人なりに方法を調べたので、せっかくなのでまとめてみました。

データフレームの準備

適当なデータでデータフレームを作ります。ベクトルを合成してデータフレームを作ると、デフォルトでは文字列がfactor型になってしまうので、引数でstringsAsFactors = FALSEを指定します。

> x <- c("abc","cde","efg","ghi","ijk","klm")
> x2 <- c("Alice","Bob","Charles","Deven","Eve","Feldman")
> y <- c(TRUE,FALSE,TRUE,TRUE,TRUE,FALSE)
> z <- c(1,3,5,2,4,7)
> df<- data.frame(X=x, X2=x2, Y=y, Z=z, stringsAsFactors = FALSE)
> df
    X      X2     Y Z
1 abc   Alice  TRUE 1
2 cde     Bob FALSE 3
3 efg Charles  TRUE 5
4 ghi   Deven  TRUE 2
5 ijk     Eve  TRUE 4
6 klm Feldman FALSE 7

列を指定して検索&置換

まずは、データフレーム中の特定の列だけ検索対象にしたいときの方法です。試しにX2列を対象に、「e」を「***」に置換してみます。

#Rの基本パッケージを使う方法
> df1 <- df
> df1["X2"] <- lapply(df1["X2"], gsub, pattern="e", replacement = "***")
> df1
    X        X2     Y Z
1 abc   Alic***  TRUE 1
2 cde       Bob FALSE 3
3 efg Charl***s  TRUE 5
4 ghi D***v***n  TRUE 2
5 ijk     Ev***  TRUE 4
6 klm F***ldman FALSE 7
> 
#dplyrを使う方法
> library(dplyr)
> library(stringr)
> df2 <- dplyr::mutate(df,X2=gsub(X2,pattern="e",replacement = "***", ignore.case = TRUE))
> df2
    X        X2     Y Z
1 abc   Alic***  TRUE 1
2 cde       Bob FALSE 3
3 efg Charl***s  TRUE 5
4 ghi D***v***n  TRUE 2
5 ijk   ***v***  TRUE 4
6 klm F***ldman FALSE 7
> 
> df3 <- dplyr::mutate(df,X2=gsub(X2,pattern="e",replacement = "***", ignore.case = FALSE))
> df3
    X        X2     Y Z
1 abc   Alic***  TRUE 1
2 cde       Bob FALSE 3
3 efg Charl***s  TRUE 5
4 ghi D***v***n  TRUE 2
5 ijk     Ev***  TRUE 4
6 klm F***ldman FALSE 7
> 
> df4 <- dplyr::mutate(df,X2=str_replace(X2,pattern="e",replacement = "***"))
> df4
    X        X2     Y Z
1 abc   Alic***  TRUE 1
2 cde       Bob FALSE 3
3 efg Charl***s  TRUE 5
4 ghi   D***ven  TRUE 2
5 ijk     Ev***  TRUE 4
6 klm F***ldman FALSE 7

gsubでは、ignore.case = TRUEを指定することで大文字小文字区別なく置換されます。 ここで、gsubstr_replaceでは置換のされ方が違うことに注意してください(df3とdf4)。前者では要素内でヒットするすべての文字列が置換されますが、後者では最初のヒットのみ置換されます。 仕様なのかどうか私にはよくわからなかったので、詳しい方がいれば教えてください。

データフレーム全体を検索&置換

次に、データフレーム全体を検索対象にしたいときの方法です。

#Rの基本パッケージを使う方法
> df5 <- data.frame(lapply(df, function(x) {
+   gsub(pattern="e", replacement = "***", x)
+   }),stringsAsFactors = FALSE)
> df5
      X        X2     Y Z
1   abc   Alic***  TRUE 1
2 cd***       Bob FALSE 3
3 ***fg Charl***s  TRUE 5
4   ghi D***v***n  TRUE 2
5   ijk     Ev***  TRUE 4
6   klm F***ldman FALSE 7
> 
#dplyrを使う方法
> df6 <- dplyr::mutate_all(df,funs(gsub(.,pattern="e",replacement = "***")))
> df6
      X        X2     Y Z
1   abc   Alic***  TRUE 1
2 cd***       Bob FALSE 3
3 ***fg Charl***s  TRUE 5
4   ghi D***v***n  TRUE 2
5   ijk     Ev***  TRUE 4
6   klm F***ldman FALSE 7
> 
> df7 <- dplyr::mutate_all(df,funs(str_replace(.,pattern="e",replacement = "***")))
> df7
      X        X2     Y Z
1   abc   Alic***  TRUE 1
2 cd***       Bob FALSE 3
3 ***fg Charl***s  TRUE 5
4   ghi   D***ven  TRUE 2
5   ijk     Ev***  TRUE 4
6   klm F***ldman FALSE 7

Rの基本パッケージを使う方法でdata.frameをかぶせているのは、lapplyで返されるのがリスト型だからです。

注意点

データフレーム全体を検索対象にすると、factor型やdouble型の列もすべてcharacter型にされてしまいます。

> mode(df$Y)
[1] "logical"
> mode(df$Z)
[1] "numeric"
> mode(df5$Y)
[1] "character"
> mode(df6$Z)
[1] "character"
> mode(df5$Y)
[1] "character"
> mode(df6$Z)
[1] "character"

よって必要に応じて型の変換を行います(もっといい方法があるかもしれませんが)。

df3$Y = as.logical(df6$Y)
df3$Z = as.numeric(df6$Z)

参考サイト様

r - Replace all occurrences of a string in a data frame - Stack Overflow

dplyrのmutate_if()とかについて - Technically, technophobic.

{dplyr}のパイプの中でNAを0に置換する - Note of Pediatric Surgery