벡터 또는 열에서 두 번째 (세 번째…) 최고 / 최저 값을 찾는 가장 빠른 방법


161

R은 최대 및 최소를 제공하지만 전체 벡터를 정렬 한 다음이 벡터에서 값 x를 선택하는 것과는 별도로 순서대로 다른 값을 찾는 빠른 방법은 없습니다.

예를 들어 두 번째로 높은 값을 얻는 더 빠른 방법이 있습니까?


CRAN에서 패키지 키트는이 topn빠르게보다 기능을 sort, order그리고 nth. 설명서를보십시오.
Suresh_Patel

답변:


195

partial인수를 사용하십시오 sort(). 두 번째로 높은 값의 경우 :

n <- length(x)
sort(x,partial=n-1)[n-1]

4
sort(x, TRUE)[2]질문의 제약 조건을 만족시키지 않는 것 외에 @Abrar의 답변에 설명 된 것과는 달리이 방법의 장점은 무엇입니까 ?

5
이 방법을 사용했지만 다음과 같은 오류가 발생합니다. Error in sort.int(x, na.last = na.last, decreasing = decreasing, ...) : index 4705 outside bounds 문제가 무엇인지 알 수 있습니까? 일부 세부 사항 : 내 x는 길이가 4706 인 숫자 벡터로 NA데이터에 일부 가 있습니다. @RobHyndman이 제안한 것과 동일한 코드를 사용하여 벡터에서 두 번째로 높은 값을 얻으려고했습니다.
sriramn

왜 내림차순으로 정렬하고 두 값 중 두 번째 값만 가져 가지 않습니까? 이것이 더 빠르지 않습니까?
jwg

3
설명 인수는 부분 정렬과 호환되지 않습니다.
Rob Hyndman

7
decreasing인수가 부분 정렬과 호환되지 않지만 항상 가능합니다 -sort(-x, partial=n-1)[n-1]. 논리적으로 동일하며보다 시간이 덜 걸립니다 sort(x, decreasing=TRUE)[n-1].
r2evans

52

기록을 위해 약간 느린 대안 :

x <- c(12.45,34,4,0,-234,45.6,4)
max( x[x!=max(x)] )
min( x[x!=min(x)] )

이것이 전체 벡터를 정렬하고 n-1 번째 값을 취하는 것보다 빠르면 놀라운 것 같습니다!
jwg

@jwg 이것은 O (n)이므로 큰 데이터 집합을 정렬하는 것보다 빠릅니다.
Museful

허용되는 다른 답변보다 NA와 더 잘 작동합니다. 'na.rm = TRUE'를 'min'함수의 인수로 사용하십시오.
Yair Daon

2
약간의 수정 max(x[-which.max(x)])
만으로도

31

Rob의 답변을 약간 더 일반적인 함수로 묶었습니다.이 함수는 2, 3, 4 등 (최대 등)을 찾는 데 사용할 수 있습니다.

maxN <- function(x, N=2){
  len <- length(x)
  if(N>len){
    warning('N greater than length(x).  Setting N=length(x)')
    N <- length(x)
  }
  sort(x,partial=len-N+1)[len-N+1]
}

maxN(1:10)

1
멋있는. 이 사용법은 특히 유용합니다 maxN(1:10, 1:3)(기본 N을 1로 설정했을 것입니다)
PatrickT

23

Rfast 에는 nth_element 라는 함수가 있습니다.이 함수는 사용자가 요청한 것을 정확하게 수행하며 위에서 설명한 모든 구현보다 빠릅니다.

또한 부분 정렬을 기반으로 위에서 설명한 방법은 k 개의 가장 작은 값을 찾는 것을 지원하지 않습니다.

Rfast::nth(x, 5, descending = T)

x의 5 번째로 큰 요소를 반환합니다.

Rfast::nth(x, 5, descending = F)

x의 다섯 번째로 작은 요소를 반환합니다

가장 인기있는 답변에 대한 아래의 벤치 마크.

10,000 개의 숫자 :

N = 10000
x = rnorm(N)

maxN <- function(x, N=2){
    len <- length(x)
    if(N>len){
        warning('N greater than length(x).  Setting N=length(x)')
        N <- length(x)
    }
    sort(x,partial=len-N+1)[len-N+1]
}

microbenchmark::microbenchmark(
    Rfast = Rfast::nth(x,5,descending = T),
    maxn = maxN(x,5),
    order = x[order(x, decreasing = T)[5]]
)

Unit: microseconds
  expr      min       lq      mean   median        uq       max neval
 Rfast  160.364  179.607  202.8024  194.575  210.1830   351.517   100
  maxN  396.419  423.360  559.2707  446.452  487.0775  4949.452   100
 order 1288.466 1343.417 1746.7627 1433.221 1500.7865 13768.148   100

1 만 개 번호 :

N = 1e6 #evaluates to 1 million
x = rnorm(N)

microbenchmark::microbenchmark(
    Rfast = Rfast::nth(x,5,descending = T),
    maxN = maxN(x,5),
    order = x[order(x, decreasing = T)[5]]
)

Unit: milliseconds
  expr      min        lq      mean   median        uq       max neval
 Rfast  89.7722  93.63674  114.9893 104.6325  120.5767  204.8839   100
  maxN 150.2822 207.03922  235.3037 241.7604  259.7476  336.7051   100
 order 930.8924 968.54785 1005.5487 991.7995 1031.0290 1164.9129   100

8
좋은! 일반적으로 비교적 낮은 응답 사용자가 인기있는 오래된 질문에 대한 답변을 추가하면 품질이 매우 낮습니다. 반면에 이것은 훌륭한 추가 기능입니다. 몇 가지 가독성을 수정했지만 멋지게 보입니다!
Gregor Thomas

3
Rfast::nth여러 요소 (예 : 8 번째 및 9 번째로 큰 요소)와 해당 요소의 인덱스를 반환 할 수 있다고 언급합니다 .
Jasha

3
Rfast 솔루션에 대해 내가 좋아하는 점은 패키지에 각 행 또는 열에 대해이 작업을 수행하기위한 솔루션이 쉽게 구현되어 있다는 것입니다.
Jay

16

다음은 벡터에서 N 가장 작은 / 가장 큰 값의 인덱스를 찾는 쉬운 방법입니다 (예 : N = 3).

N <- 3

N 최소 :

ndx <- order(x)[1:N]

N 최대 :

ndx <- order(x, decreasing = T)[1:N]

따라서 다음과 같이 값을 추출 할 수 있습니다.

x[ndx]

이것은 L log L 시간으로 실행되며, 여기서 L은 x의 길이입니다. 사용자가 로그 L 시간으로 실행되는 메소드를 원한다고 생각합니다.
arsmath

방법이 시간 순서로 정렬되고 가장 빠른 N이 추출 된 경우 이것이 두 번째로 빠른 방법 일 수 있습니다. 또한 수용 된 솔루션과 비교하여 매우 명확한 코드이기 때문에 그것을 좋아합니다.
Pete

1
이론적으로 가장 좋고 수용된 방법은 O (log L)가 아니라 O (L) 시간에 실행됩니다. 이것은 O (L log L)에서 실행됩니다.
Valentas

6

n 번째로 높은 값의 경우

sort(x, TRUE)[n]

9
OP는 이미 자신의 게시물에서 이것이 사용하고 싶지 않은 해결책이라고 말했다. "전체 벡터를 정렬하고이 벡터에서 x 값을 고르는 것 말고"
Paul Hiemstra가

3

max 요소를 먼저 제거한 다음 비슷한 속도로 다른 max 실행을 수행합니다.

system.time({a=runif(1000000);m=max(a);i=which.max(a);b=a[-i];max(b)})
   user  system elapsed 
  0.092   0.000   0.659 

system.time({a=runif(1000000);n=length(a);sort(a,partial=n-1)[n-1]})
   user  system elapsed 
  0.096   0.000   0.653 

2

내가 찾은 가장 간단한 방법은 다음과 같습니다.

num <- c(5665,1615,5154,65564,69895646)

num <- sort(num, decreasing = F)

tail(num, 1)                           # Highest number
head(tail(num, 2),1)                   # Second Highest number
head(tail(num, 3),1)                   # Third Highest number
head(tail(num, n),1)                   # Generl equation for finding nth Highest number

1

최근 에 주어진 벡터에서 최고 N 최대 / 최소 숫자의 인덱스를 반환 하는 R 함수를 찾고 있었을 때 그러한 함수가 없다는 것에 놀랐습니다.

그리고 이것은 매우 비슷한 것입니다.

base :: order 함수를 사용하는 무차별 대입 솔루션 이 가장 쉬운 것 같습니다.

topMaxUsingFullSort <- function(x, N) {
  sort(x, decreasing = TRUE)[1:min(N, length(x))]
}

그러나 N 값이 벡터 x 길이에 비해 상대적으로 작은 경우 가장 빠르지 않습니다 .

반면에 N 이 실제로 작은 경우 base :: whichMax 함수를 반복적으로 사용할 수 있으며 각 반복에서 찾은 값을 -Inf로 바꿀 수 있습니다

# the input vector 'x' must not contain -Inf value 
topMaxUsingWhichMax <- function(x, N) {
  vals <- c()
  for(i in 1:min(N, length(x))) {
    idx      <- which.max(x)
    vals     <- c(vals, x[idx]) # copy-on-modify (this is not an issue because idxs is relative small vector)
    x[idx]   <- -Inf            # copy-on-modify (this is the issue because data vector could be huge)
  }
  vals
}

나는 당신이 R의 복사시 수정 특성이라는 문제를 믿습니다. 그래서 이것은 매우 작은 N (1,2,3)에 대해 더 잘 수행되지만 더 큰 N 값에 대해서는 빠르게 느려질 것입니다. 그리고 벡터 x N 번의 모든 요소를 ​​반복 합니다.

깨끗한 R 에서 가장 좋은 해결책은 partial base :: sort 사용하는 입니다.

topMaxUsingPartialSort <- function(x, N) {
  N <- min(N, length(x))
  x[x >= -sort(-x, partial=N)[N]][1:N]
}

그런 다음 위의 기능 결과에서 마지막 ( N 번째) 항목을 선택할 수 있습니다 .

참고 : 위에서 정의한 함수는 예제 일뿐입니다. 사용하려는 경우 입력을 확인 / 위생해야합니다 (예 : N> length (x) ).

나는 http://palusga.cz/?p=18 에서 매우 비슷한 것 (벡터의 최고 N 최대 / 최소 값의 색인을 얻음)에 대한 작은 기사를 썼습니다. 여기서 위에서 정의한 유사한 함수의 벤치 마크를 찾을 수 있습니다.



0
topn = function(vector, n){
  maxs=c()
  ind=c()
  for (i in 1:n){
    biggest=match(max(vector), vector)
    ind[i]=biggest
    maxs[i]=max(vector)
    vector=vector[-biggest]
  }
  mat=cbind(maxs, ind)
  return(mat)
}

이 함수는 상위 n 개의 값과 해당 인덱스가있는 행렬을 반환합니다. 그것이 VDevi-Chou에 도움이되기를 바랍니다.


0

입력 숫자 형 벡터 x에서 N 번째로 작은 값 또는 가장 큰 값의 인덱스를 찾습니다. 맨 아래에서 N을 원하면 인수에 bottom = TRUE를 설정하고 맨 위에서 N을 원하면 bottom = FALSE를 설정하십시오. N = 1이고 bottom = TRUE는 which.min과 같습니다. N = 1과 bottom = FALSE는 which.max와 같습니다.

FindIndicesBottomTopN <- function(x=c(4,-2,5,-77,99),N=1,bottom=FALSE)
{

  k1 <- rank(x)
  if(bottom==TRUE){
    Nindex <- which(k1==N)
    Nindex <- Nindex[1]
  }

  if(bottom==FALSE){
    Nindex <- which(k1==(length(x)+1-N))
    Nindex <- Nindex[1]
  }

  return(Nindex)
}

0

dplyr에는 n 번째 함수가 있으며, 첫 번째 인수는 벡터이고 두 번째 인수는 원하는 위치입니다. 이것은 반복되는 요소에도 적용됩니다. 예를 들면 다음과 같습니다.

x = c(1,2, 8, 16, 17, 20, 1, 20)

두 번째로 큰 값 찾기 :

 nth(unique(x),length(unique(x))-1)

[1] 17

2
이게 빠른가요?
Ben Bolker

2
내부적으로 이것은 사용 x[[order(order_by)[[n]]]]하므로 전체 벡터를 정렬해야합니다. 따라서 허용되는 답변만큼 빠르지는 않습니다.
Ben Bolker

5
하지만 사용 sort 부분 = 인수 (모든 것을 변화하는)
벤 Bolker

Paolo 또는 Rob의 답변을 암시하는 @BenBolker는 개선에 사용될 수 dplyr::nth()있습니까? bench::mark(max(x[-which.max(x)]), x[[order(-x)[[2]]]] ), nth()거의 10 배 더 느린 것 같습니다 length(x). 3 백만입니다.
sindri_baldur

-1

로 다음으로 높은 값을 식별 할 수 있습니다 cummax(). 당신은 예를 들어 각각의 새로운 높은 값의 위치를 원하는 경우에 당신은 당신의 벡터 전달할 수 있습니다 cummax()받는 값을 diff()하는 위치 식별 기능 cummax()값을 변경합니다. 벡터가 있다고 해

v <- c(4,6,3,2,-5,6,8,12,16)
cummax(v) will give us the vector
4  6  6  6  6  6  8 12 16

이제 변경 위치를 찾으려면 사용하려는 cummax()많은 옵션이 있습니다 sign(diff(cummax(v))). 로 인해 손실 된 첫 번째 요소를 조정해야합니다 diff(). 벡터의 완전한 코드 v는 다음과 같습니다.

which(sign(diff(cummax(v)))==1)+1

나는 당신이 그 질문을 오해한다고 생각합니다. 목표는 두 번째로 높은 값을 찾는 것입니다. 이것은 v에서 12로, 그리고 세 번째로 높은 8로 당신을 얻는 데 어떻게 도움이됩니까?
Frank

-1

다음 sort과 같이 키워드 를 사용할 수 있습니다 .

sort(unique(c))[1:N]

예:

c <- c(4,2,44,2,1,45,34,2,4,22,244)
sort(unique(c), decreasing = TRUE)[1:5]

처음 5 개의 최대 숫자를 제공합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.