データフレームに「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を使用できないことを理解してください。それを解決する方法はありますか?

コメント

  • filter(grepl (paste(df1.MATCH、collapse = " | ")、df2.PATTERN))これはこの例では機能します。ただし、私の実際のデータフレームには最大100万行あり、このコードを使用するとエラーが発生しました。
  • コード例の最初の行で、df1.MATCH <- c("ABC", "abc" ,"ABC")と表示する必要があります。最後の文字列は"BCD"

回答

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 

ただし、出力を確認してください。おそらくマトリックスは必要ありませんでした。 grepldf1.MATCHの最初の要素として実行するとどうなりますか?

grepl("ABC",df2.PATTERN) [1] TRUE FALSE TRUE 

grepldf2.PATTERN<の各要素に対してABCをチェックしているため、ベクトルを取得します。 / div>。フィルタリングに役立つ論理ベクトルを取得するには、df1.MATCHと同じ長さの論理ベクトルを返す必要があります。 2つの方法があります。

方法1:any

df1.MATCHのどの要素がdf2.PATTERNの要素と一致するかを知りたいので、 anyを使用できます。これは、引数の要素がTRUEの場合にTRUEを返します。これを機能させるには、少し異なる構文が必要です。 grepllapplyでラップして、3つのベクトルのリストを作成する必要があります(df1.MATCH1sapplyラップされた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の内容を、abcABCのような可能な値と照合する必要があります。 、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 = TRUEgreplを使用します>:

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つの操作を実行しています:lapplygreplを3回実行しています(df2.PATTERNの各要素に1回、sapply各リスト要素(各行)に対してanyを実行します。

コメント

  • package::stringrstr_detectを使用すると greplアプローチと同様に:str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))