データフレームに「MATCH」という名前の列と名前のあるパターンのリストがあります「PATTERN」。
df1.MATCH <- c("ABC", "abc" ,"BCD") df1 <- as.data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc")
greplを使用してMATCH列をPATTERNと比較したいのですが、trueの場合は関数を適用します。望ましい結果は、「ABC」が「ABC」および「ABCabc」と一致することです。これは私が使用したコードです:
df1 %>% filter(grepl(df1.MATCH,df2.PATTERN ))%>% ...
エラーが発生します:
"Warning message: In grepl(TXN_GROUP, parm[3]) :argument "pattern" has length > 1 and only the first element will be used"
Iベクトルのリストにgreplを使用できないことを理解してください。それを解決する方法はありますか?
コメント
回答
TL; DR:
grepl
は、最初の引数がベクトルではなく文字列(長さ1)であると想定しています。これは、sapply
およびlapply
(以下を参照)が、captuという単一の正規式を使用した方が適切です。df1.MATCH
で一致させたいものを解像度し、df2.PATTERN
をまったく使用しないでください。この2番目のオプションは、大規模なデータセットの場合ははるかに高速です(インテリジェントでない場合)。この種の作業では、正規表現を最大限に活用する方法を学ぶ価値があります。
df1 %>% filter(grepl(pattern = "^((ABC)( )*)+$", x = df1.MATCH, ignore.case = TRUE))
説明
grepl
のドキュメントには、次の使用法が示されています。
grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
pattern
引数が最初であり、この引数は文字列(1つの要素)である必要があります。ベクトルであるこの引数にdf1.MATCH
を指定しています。
sapply
を使用して
をdf1.MATCH
の各要素に追加します。
sapply(df1.MATCH, grepl, x = df2.PATTERN) ABC abc BCD [1,] TRUE FALSE FALSE [2,] FALSE TRUE FALSE [3,] TRUE TRUE FALSE
ただし、出力を確認してください。おそらくマトリックスは必要ありませんでした。 grepl
をdf1.MATCH
の最初の要素として実行するとどうなりますか?
grepl("ABC",df2.PATTERN) [1] TRUE FALSE TRUE
grepl
がdf2.PATTERN
<の各要素に対してABC
をチェックしているため、ベクトルを取得します。 / div>。フィルタリングに役立つ論理ベクトルを取得するには、df1.MATCH
と同じ長さの論理ベクトルを返す必要があります。 2つの方法があります。
方法1:
any
df1.MATCH
のどの要素がdf2.PATTERN
の要素と一致するかを知りたいので、 any
を使用できます。これは、引数の要素がTRUE
の場合にTRUE
を返します。これを機能させるには、少し異なる構文が必要です。 grepl
をlapply
でラップして、3つのベクトルのリストを作成する必要があります(df1.MATCH1
)sapply
ラップされたany
にフィードします。 sapply
を使用する場合、行列入力があるため、any
は1つの値のみを返します。
any(grepl("ABC", df2.PATTERN)) [1] TRUE sapply( lapply(df1.MATCH, grepl, x = df2.MATCH), any) [1] TRUE TRUE FALSE
方法2:より適切な正規表現を記述します。
df1.MATCH
の内容を、abc
、ABC
のような可能な値と照合する必要があります。 、ABC ABC
、またはABC abc
など。これらすべてを単一の正規表現文字列に含めることができます。必要な文字列は
"^((ABC)( )*)+$" ^ # Nothing else before this (ABC) # Must contain ABC together as a group ( )* # followed by any number of spaces (including 0) ((ABC)( )*)+ # Look for the ABC (space) pattern repeated one or more times $ # And nothing else after it
次に、ignore.case = TRUE
divでgrepl
を使用します>:
grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE) [1] TRUE TRUE FALSE
ベンチマーク
大規模なデータセットでは、これらの1つがより高速に実行されます。調べてみましょう。ベンチマークの結果は、マシンのリソースによって異なります。
df1.MATCH <- sample(c("ABC", "abc" ,"BCD"), size = 100000, replace = TRUE) df1 <- data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc") library(rbenchmark) benchmark("any lapply" = { df1 %>% filter(sapply(lapply(df1.MATCH, grepl, x=df2.PATTERN), any) ) }, "better regex" = { df1 %>% filter(grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE)) } ) test replications elapsed relative user.self sys.self user.child sys.child 1 any lapply 100 149.13 70.678 147.67 0.39 NA NA 2 better regex 100 2.11 1.000 2.10 0.02 NA NA
改良された正規表現メソッドの方がはるかに高速であるようです。これは、フィルタリングの前に行ごとに1つの操作(grepl
)のみを実行しているためです。もう1つの方法は、行ごとに4つの操作を実行しています:lapply
はgrepl
を3回実行しています(df2.PATTERN
の各要素に1回、sapply
各リスト要素(各行)に対してany
を実行します。
コメント
-
package::stringr
のstr_detect
を使用するとgrepl
アプローチと同様に:str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))
df1.MATCH <- c("ABC", "abc" ,"ABC")
と表示する必要があります。最後の文字列は"BCD"
?