데이터 프레임에 “MATCH”라는 열이 있고 이름이 지정된 패턴 목록이 있습니다. “PATTERN”.
df1.MATCH <- c("ABC", "abc" ,"BCD") df1 <- as.data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc")
grepl을 사용하여 MATCH 열을 PATTERN과 비교하고 싶습니다. 참이면 내 기능을 적용합니다. 원하는 결과는 “ABC”가 “ABC”및 “ABC abc”와 일치합니다. 다음은 내가 사용한 코드입니다.
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을 사용할 수 없습니다. 해결 방법이 있습니까?
댓글
Answer
TL; DR :
grepl
는 첫 번째 인수가 벡터가 아닌 문자열 (길이 1)이 될 것으로 예상합니다.이 문제는sapply
및lapply
(아래 참조), 캡투 형식의 단일 정규 표현식을 사용하는 것이 더 좋습니다.df1.MATCH
에서 일치시키고 자하는 것과df2.PATTERN
를 전혀 사용하지 않습니다. 이 두 번째 옵션은 큰 데이터 세트의 경우 훨씬 더 빠릅니다 (간략한 경우). 이러한 유형의 작업에서는 정규 표현식을 최대한 활용하는 방법을 배우는 것이 좋습니다.
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
인수가 첫 번째이며이 인수는 문자열 (하나의 요소)이어야합니다. 당신은 벡터 인이 인수에 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
그러나 출력을보십시오! 당신은 아마도 행렬을 원하지 않았을 것입니다. df1.MATCH
의 첫 번째 요소 인 grepl
하나를 실행하면 어떻게 되나요?
grepl("ABC",df2.PATTERN) [1] TRUE FALSE TRUE
grepl
가 df2.PATTERN
<의 각 요소에 대해 ABC
를 확인하기 때문에 벡터를 얻습니다. / div>. 필터링에 유용한 논리 벡터를 얻으려면 df1.MATCH
와 동일한 길이의 논리 벡터를 반환해야합니다. 두 가지 방법이 있습니다.
방법 1 :
any
사용
df1.MATCH
의 어떤 요소가 df2.PATTERN
의 요소와 일치하는지 알고 싶으므로 인수의 요소가 TRUE
이면 TRUE
를 반환하는 any
를 사용할 수 있습니다. 이 작업을 수행하려면 약간 다른 구문이 필요합니다. 세 개의 벡터 목록을 만들기 위해 grepl
를 lapply
로 래핑해야합니다 (df1.MATCH1
)는 sapply
포장 된 any
로 공급됩니다. sapply
만 사용하면 any
는 행렬 입력이 있으므로 하나의 값만 반환합니다.
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
벤치마킹
대규모 데이터 세트에서는이 중 하나가 더 빠르게 수행됩니다. 확인해 보겠습니다. 벤치 마크 결과는 시스템 리소스에 따라 다릅니다.
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
개선 된 정규식 방법이 훨씬 더 빠른 것 같습니다. 이는 필터링 전에 행당 하나의 작업 (grepl
) 만 수행하기 때문입니다. 다른 방법은 행당 네 가지 작업을 수행합니다. lapply
는 grepl
세 번 (df2.PATTERN
의 각 요소에 대해 하나씩, 그리고 sapply
각 목록 요소 (각 행)에 대해 any
를 수행합니다.
댓글
-
package::stringr
에서str_detect
사용grepl
접근 방식과 유사 :str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))
df1.MATCH <- c("ABC", "abc" ,"ABC")
가 표시되어야합니다. 마지막 문자열은"BCD"
?