목록 요소가 있는지 테스트하는 방법은 무엇입니까?


113

문제

목록의 요소가 있는지 테스트하고 싶습니다. 여기에 예가 있습니다.

foo <- list(a=1)
exists('foo') 
TRUE   #foo does exist
exists('foo$a') 
FALSE  #suggests that foo$a does not exist
foo$a
[1] 1  #but it does exist

이 예에서는 그것이 foo$a존재 한다는 것을 알고 있지만 테스트는FALSE .

나는 들여다보고 ?existswith(foo, exists('a')반환 을 찾았 TRUE지만 exists('foo$a')반환 이유를 이해하지 못합니다.FALSE .

질문

  • exists('foo$a')돌아 오나요 FALSE?
  • with(...)선호하는 접근 방식을 사용 하고 있습니까?

1
어쩌면 !is.null(foo$a)(또는 !is.null(foo[["a"]])안전한 편이 되려면)? (또는 exists("a",where=foo))
Ben Bolker 2011

1
@BenBolker 감사합니다-좋은 대답을 할 것입니다; 후자의 옵션이 선호되는 이유는 무엇입니까?
David LeBauer 2010

3
@ 데이비드 부분 일치 ...와 위의 시도foo <- list(a1=1)
밥 티스트

답변:


151

이것은 실제로 생각보다 약간 까다 롭습니다. 목록은 실제로 (약간의 노력으로) NULL 요소를 포함 할 수 있으므로 is.null(foo$a). 더 엄격한 테스트는 이름이 실제로 목록에 정의되어 있는지 확인하는 것입니다.

foo <- list(a=42, b=NULL)
foo

is.null(foo[["a"]]) # FALSE
is.null(foo[["b"]]) # TRUE, but the element "exists"...
is.null(foo[["c"]]) # TRUE

"a" %in% names(foo) # TRUE
"b" %in% names(foo) # TRUE
"c" %in% names(foo) # FALSE

... 후자는 부분 일치를 사용하므로 더 긴 이름과 일치 할 수도 있기 때문에 foo[["a"]]보다 안전합니다 foo$a.

x <- list(abc=4)
x$a  # 4, since it partially matches abc
x[["a"]] # NULL, no match

[업데이트] 그래서, 왜 exists('foo$a')작동하지 않는지 질문으로 돌아갑니다 . 이 exists함수는 개체의 일부가 존재하는지가 아닌 환경에 변수가 있는지 만 확인합니다. 문자열 "foo$a"은 문학적으로 해석됩니다. "foo $ a"라는 변수가 있습니까? ... 그리고 대답은 FALSE...

foo <- list(a=42, b=NULL) # variable "foo" with element "a"
"bar$a" <- 42   # A variable actually called "bar$a"...
ls() # will include "foo" and "bar$a" 
exists("foo$a") # FALSE 
exists("bar$a") # TRUE

2
아직 명확하지 않습니다. 이유가 exists('foo$a') == FALSE있습니까?
David LeBauer

이것은 일반적으로 R에서 이에 대한 좋은 해결책이 없음을 의미합니다! $mylist[[12]]$out$mcerror현재 지옥 처럼 복잡 할 더 복잡한 것 (예 : 정의 된 경우 테스트 )을 원할 수 있습니다.
TMS

@Jim 의 답변 에서 지적한 where주장을 알고 계 셨습니까? exists
David LeBauer 2014 년

"bar$a" <- 42나는 이것이 유효하지 않은 구문이었고 exist ( "foo $ a")가 순진한 의미로 작동하기를 바랍니다.
Andy V

44

명명 된 요소를 확인하는 가장 좋은 방법 exist()은를 사용하는 것이지만 위의 답변은 함수를 제대로 사용하지 않습니다. 목록 에서 where변수를 확인 하려면 인수 를 사용해야 합니다.

foo <- list(a=42, b=NULL)

exists('a', where=foo) #TRUE
exists('b', where=foo) #TRUE
exists('c', where=foo) #FALSE

8
exists()목록에서 사용 하는 것은 작동하지만 R이 해당 이름의 개체를 확인하기 전에 내부적으로 환경에 강제로 적용한다고 믿습니다. 이는 비효율적이며 이름이 지정되지 않은 요소가 있으면 오류가 발생할 수 있습니다. 예를 들어를 실행 exists('a', list(a=1, 2))하면 오류가 발생 Error in list2env(list(a = 1, 2), NULL, <environment>) : attempt to use zero-length variable name합니다.. 변환은 여기에서 발생합니다 : github.com/wch/r-source/blob/…
wch

5

다음은 다른 답변에서 제안 된 방법의 성능 비교입니다.

> foo <- sapply(letters, function(x){runif(5)}, simplify = FALSE)
> microbenchmark::microbenchmark('k' %in% names(foo), 
                                 is.null(foo[['k']]), 
                                 exists('k', where = foo))
Unit: nanoseconds
                     expr  min   lq    mean median   uq   max neval cld
      "k" %in% names(foo)  467  933 1064.31    934  934 10730   100  a 
      is.null(foo[["k"]])    0    0  168.50      1  467  3266   100  a 
 exists("k", where = foo) 6532 6998 7940.78   7232 7465 56917   100   b

목록을 여러 번 액세스하는 빠른 사전으로 사용할 계획이라면 is.null접근 방식이 유일한 실행 가능한 옵션 일 수 있습니다. 나는 그것이 O (1)이라고 가정하고 %in%접근법은 O (n)입니까?


4

@ salient.salamander의 약간 수정 된 버전은 전체 경로를 확인하려는 경우 사용할 수 있습니다.

Element_Exists_Check = function( full_index_path ){
  tryCatch({
    len_element = length(full_index_path)
    exists_indicator = ifelse(len_element > 0, T, F)
      return(exists_indicator)
  }, error = function(e) {
    return(F)
  })
}

3

아직 나오지 않은 한 가지 해결책은 NULL을 성공적으로 처리하는 length를 사용하는 것입니다. 내가 알 수있는 한 NULL을 제외한 모든 값의 길이는 0보다 큽니다.

x <- list(4, -1, NULL, NA, Inf, -Inf, NaN, T, x = 0, y = "", z = c(1,2,3))
lapply(x, function(el) print(length(el)))
[1] 1
[1] 1
[1] 0
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
[1] 3

따라서 우리는 명명 된 인덱스와 번호 인덱스 모두에서 작동하는 간단한 함수를 만들 수 있습니다.

element.exists <- function(var, element)
{
  tryCatch({
    if(length(var[[element]]) > -1)
      return(T)
  }, error = function(e) {
    return(F)
  })
}

요소가 존재하지 않으면 tryCatch 블록에서 포착 된 범위를 벗어난 조건이 발생합니다.


3

rlang::has_name() 이것도 할 수 있습니다 :

foo = list(a = 1, bb = NULL)
rlang::has_name(foo, "a")  # TRUE
rlang::has_name(foo, "b")  # FALSE. No partial matching
rlang::has_name(foo, "bb")  # TRUE. Handles NULL correctly
rlang::has_name(foo, "c")  # FALSE

보시다시피 @Tommy가 기본 R을 사용하여 처리하는 방법을 보여주고 이름이 지정되지 않은 항목이있는 목록에 대해 작동하는 모든 경우를 본질적으로 처리합니다. exists("bb", where = foo)가독성을 위해 다른 답변에서 제안한대로 여전히 권장 하지만 has_name이름이없는 항목이있는 경우 대안입니다.


0

목록 요소 purrr::has_element 을 확인하는 데 사용 합니다 .

> x <- list(c(1, 2), c(3, 4))
> purrr::has_element(x, c(3, 4))
[1] TRUE
> purrr::has_element(x, c(3, 5))
[1] FALSE

요소가 중첩 된 경우 / 중첩 수준에서 작동합니까? 나는 문서를 확인하고 그것은 분명하지 않다
데이비드 LeBauer

@DavidLeBauer, 아니. 이 경우, 내가 사용하는 것 rapply(같은 any(rapply(x, function(v) identical(v, c(3, 4)), how = 'unlist')))
드미트리 Zotikov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.