2016-12-13 8 views
5

숫자 요소의 벡터와 간격의 시작점과 끝점을 정의하는 두 개의 열이있는 데이터 프레임이 있습니다. 데이터 프레임의 각 행은 하나의 간격입니다. 벡터의 각 요소가 속하는 간격을 찾고 싶습니다. 그것을 할 수벡터의 각 요소가 속한 데이터 프레임의 간격 행을 찾으십시오.

elements <- c(0.1, 0.2, 0.5, 0.9, 1.1, 1.9, 2.1) 

intervals <- structure(list(phase = c("a", "b", "c"), 
          start = c(0, 1, 2), 
          end = c(0.5, 1.9, 2.5)), 
         .Names = c("phase", "start", "end"), 
         row.names = c(NA, -3L), 
         class = "data.frame") 

여기 한 가지 방법 :

library(intrval) 
    phases_for_elements <- 
    map(elements, ~.x %[]% data.frame(intervals[, c('start', 'end')])) %>% 
     map(., ~unlist(intervals[.x, 'phase'])) 

여기 년대 tidyverse에 반대하는 사람들을위한

# Find which interval that each element of the vector belongs in 

    library(tidyverse) 
    elements <- c(0.1, 0.2, 0.5, 0.9, 1.1, 1.9, 2.1) 

    intervals <- frame_data(~phase, ~start, ~end, 
           "a",  0,  0.5, 
           "b",  1,  1.9, 
           "c",  2,  2.5) 

같은 예제 데이터 : 다음

몇 가지 예를 들어 데이터의 출력 :

[[1]] 
    phase 
     "a" 

    [[2]] 
    phase 
     "a" 

    [[3]] 
    phase 
     "a" 

    [[4]] 
    character(0) 

    [[5]] 
    phase 
     "b" 

    [[6]] 
    phase 
     "b" 

    [[7]] 
    phase 
     "c" 

하지만 적은 타이핑으로 더 간단한 방법을 찾고 있습니다. 관련 질문에서 findInterval을 본 적이 있지만이 상황에서 어떻게 사용할 수 있는지 잘 모르겠습니다.

+1

, purrrfurrr 당신이 만약 @ 벤의 길 예제 데이터를 작성하려면 기본 기능을 마스킹하는 것으로 유명한 많은 수의 패키지를 설치하고로드해야합니다. 'dput'은 기본 R 만 필요하며 데이터를받는 사람은 물론 데이터를 공유하는 사람도 매우 편리합니다. – Roland

+2

@Roland, 내 업데이트를 참조하십시오. 하지만 당신이 내 질문을 편리하게 찾지는 못했고, 자신을 괴롭힐 필요가 없습니다. – Ben

답변

14

data.table (v> = 1.9.8)의 새로운 "비공식"조인을 사용할 수 있습니다. 구문이 마음에 들지는 않을지 모르지만 매우 효율적인 솔루션이어야합니다.

또한이 함수는 여기에 해당하지 않지만이 함수는 간격에 연속성을 가정하므로이 함수를 사용하는 간단한 해결책이있을 것입니다. on 연산자로 지정된 상태로 intervalselements 조인 상기 코드에 관해서는

library(data.table) #v1.10.0 
setDT(intervals)[data.table(elements), on = .(start <= elements, end >= elements)] 
# phase start end 
# 1:  a 0.1 0.1 
# 2:  a 0.2 0.2 
# 3:  a 0.5 0.5 
# 4: NA 0.9 0.9 
# 5:  b 1.1 1.1 
# 6:  b 1.9 1.9 
# 7:  c 2.1 2.1 

, I는 별다른 설명이 발견. 그것은 거의 그것입니다.

그 중 하나가 integer 경우는 처음 numeric로 변환해야하므로 start, endelements이 같은 유형의 모든해야한다,하지만 여기에 특정주의가있다.

+1

정말 멋지 네요. 꽤 많이 롤링 조인과 오버랩 등으로 일하는 나의 초기 방법 중 일부를 무효화합니다. – thelatemail

4

cut이 여기에서 유용 할 수 있습니다. @ thelatemail의 cut 솔루션에서 영감을

out <- cut(elements, t(intervals[c("start","end")])) 
levels(out)[c(FALSE,TRUE)] <- NA 
intervals$phase[out] 
#[1] "a" "a" "a" NA "b" "b" "c" 
+0

너무 매끄 럽습니다. –

3

, 여전히 여기에 입력이 많이 필요 findInterval를 사용하여 하나입니다cutfindInterval가 왼쪽으로 오픈 한 간격

out <- findInterval(elements, t(intervals[c("start","end")]), left.open = TRUE) 
out[!(out %% 2)] <- NA 
intervals$phase[out %/% 2L + 1L] 
#[1] "a" "a" "a" NA "b" "b" "c" 

주의 할. 따라서 cutfindInterval을 사용하는 솔루션은 intrval을 사용하는 Ben의 것과 같고 data.table을 사용하는 David의 비 등식 합계와 foverlaps을 사용하는 다른 솔루션과 동일한 이 아닌입니다.

+0

감사합니다. 나는 '컷'을위한 '오른쪽'인수로 왼쪽 개방 간격을 변경할 수 있다고 생각합니다. 맞습니까? – Ben

+0

두면이 바뀔까 봐 걱정됩니다. 도움 말 : "오른쪽"논리적, 간격을 오른쪽에 닫아야 (그리고 왼쪽에서 열림) 또는 그 반대의 경우를 나타냅니다. " – Uwe

+0

다시 한번 고마워. – Ben

4

비동기 조인에 대한 David Arenburg의 언급은 이것이 일반적인 종류의 문제를 이해하는 데 매우 도움이되었습니다 (감사합니다!). 이제는 not implemented for dplyr임을 알 수 있습니다. this answer 덕분에 동일한 이디엄에서이 작업을 수행 할 수있는 fuzzyjoin 패키지가 있음을 알 수 있습니다.하지만 위의 내 map 솔루션보다 간결하게 (내보기에는 더 읽기 쉽지만) 간결하게 답변 한 편지함에 cut 답변을 표시하지 않습니다.

상기 제 예컨대

상기 fuzzyjoin 용액 것이 제공

library(fuzzyjoin) 
library(tidyverse) 

fuzzy_left_join(data.frame(elements), intervals, 
       by = c("elements" = "start", "elements" = "end"), 
       match_fun = list(`>=`, `<=`)) %>% 
    distinct() 

:

여기
elements phase start end 
1  0.1  a  0 0.5 
2  0.2  a  0 0.5 
3  0.5  a  0 0.5 
4  0.9 <NA> NA NA 
5  1.1  b  1 1.9 
6  1.9  b  1 1.9 
7  2.1  c  2 2.5 
2

은 (어긋나는)로부터 foverlaps를 사용하는 "원 라이너"의 일종 그러나 다윗의 비 동등 가입 data.table 패키지는 여전히 더 간결 : 들어

library(data.table) #v1.10.0 
foverlaps(data.table(start = elements, end = elements), 
      setDT(intervals, key = c("start", "end"))) 
# phase start end i.start i.end 
#1:  a  0 0.5  0.1 0.1 
#2:  a  0 0.5  0.2 0.2 
#3:  a  0 0.5  0.5 0.5 
#4: NA NA NA  0.9 0.9 
#5:  b  1 1.9  1.1 1.1 
#6:  b  1 1.9  1.9 1.9 
#7:  c  2 2.5  2.1 2.1 
+0

감사합니다. 나는'foverlaps'가'IRanges :: findOverlaps'에 의해 영감을 받았음을 봅니다. 나는이 문제에도 사용했습니다. 그것은 훨씬 더 [어색한] (http://stackoverflow.com/questions/41132081/find-which-intervalrow-in-a-data-frame-that-each-element-of-a-vector-belongs- # comment69468517_41132081에서) 내 Q에서 보는 것보다 – Ben

2

library(tidyverse) 
elements <- c(0.1, 0.2, 0.5, 0.9, 1.1, 1.9, 2.1) 

intervalsDF <- 
    frame_data( ~phase, ~start, ~end, 
       "a",  0,  0.5, 
       "b",  1,  1.9, 
       "c",  2,  2.5 
) 

library(intervals) 
library(rlist) 

interval_overlap(
    Intervals(intervalsDF %>% select(-phase) %>% as.matrix, closed = c(TRUE, TRUE)), 
    Intervals(data_frame(start = elements, end = elements), closed = c(TRUE, TRUE)) 
) %>% 
    list.map(data_frame(interval_index = .i, element_index = .)) %>% 
    do.call(what = bind_rows) 

# A tibble: 6 × 2 
# interval_index element_index 
#   <int>   <int> 
#1    1    1 
#2    1    2 
#3    1    3 
#4    2    5 
#5    2    6 
#6    3    7 
3

그냥 lapply 작품 : 완료 술, 여기에 intervals 패키지를 사용하는 또 다른 방법입니다,

l <- lapply(elements, function(x){ 
    intervals$phase[x >= intervals$start & x <= intervals$end] 
}) 

str(l) 
## List of 7 
## $ : chr "a" 
## $ : chr "a" 
## $ : chr "a" 
## $ : chr(0) 
## $ : chr "b" 
## $ : chr "b" 
## $ : chr "c" 

또는 purrr의를

elements %>% 
    map(~intervals$phase[.x >= intervals$start & .x <= intervals$end]) %>% 
    # Clean up a bit. Shorter, but less readable: map_chr(~.x[1] %||% NA) 
    map_chr(~ifelse(length(.x) == 0, NA, .x)) 
## [1] "a" "a" "a" NA "b" "b" "c"