Cách nhanh nhất để tìm giá trị cao nhất / thấp thứ hai (thứ ba) trong vectơ hoặc cột


160

R cung cấp tối đa và tối thiểu, nhưng tôi không thấy một cách thực sự nhanh chóng để tìm giá trị khác theo thứ tự ngoài việc sắp xếp toàn bộ vectơ và hơn là chọn giá trị x từ vectơ này.

Có cách nào nhanh hơn để có được giá trị cao thứ hai (ví dụ) không?

Cảm ơn


Bộ sản phẩm bao bì trên Cran có một topnchức năng mà là nhanh hơn sort, ordernth. Nhìn vào tài liệu.
Suresh_Patel

Câu trả lời:


195

Sử dụng partialđối số của sort(). Đối với giá trị cao thứ hai:

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

4
Ưu điểm của phương pháp này trái ngược với sort(x, TRUE)[2]mô tả trong câu trả lời của @ Abrar là gì, ngoài việc không thỏa mãn các ràng buộc trong câu hỏi?
Hugh

5
Tôi đã sử dụng phương pháp này, nhưng nhận được lỗi sau: Error in sort.int(x, na.last = na.last, decreasing = decreasing, ...) : index 4705 outside bounds Bất kỳ ý tưởng nào có thể là vấn đề? Một số chi tiết: My x là một vectơ số có độ dài 4706 với một số NAs trong dữ liệu. Tôi đã cố gắng để có được giá trị cao thứ hai trong vectơ bằng cách sử dụng mã chính xác như @RobHyndman đề xuất.
sriramn

Tại sao bạn không sắp xếp giảm dần và chỉ lấy giá trị thứ hai của hai giá trị? Điều này sẽ không nhanh hơn?
JWG

3
Đối số giảm dần không tương thích với sắp xếp một phần.
Rob Hyndman

7
Mặc dù decreasingđối số không tương thích với sắp xếp một phần, bạn luôn có thể -sort(-x, partial=n-1)[n-1]; về mặt logic thì điều tương tự và mất ít thời gian hơn đáng kể sort(x, decreasing=TRUE)[n-1].
r2evans

52

Thay thế hơi chậm, chỉ cho các hồ sơ:

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

Sẽ có vẻ đáng ngạc nhiên nếu điều này nhanh hơn việc sắp xếp toàn bộ vectơ và lấy giá trị thứ n!
jwg

@jwg Đây là O (n) vì vậy nó phải nhanh hơn việc sắp xếp trên các bộ dữ liệu lớn.
Hồi giáo

Hoạt động tốt hơn với NA hơn các câu trả lời được chấp nhận khác - chỉ cần sử dụng 'na.rm = TRUE' làm đối số cho hàm 'min'.
Yair Daon

2
Dường như với tôi bạn có thể nhận được một số cải tiến đáng kể về tốc độ với một sửa đổi nhỏ:max(x[-which.max(x)])
sindri_baldur

31

Tôi kết hợp câu trả lời của Rob thành một hàm tổng quát hơn một chút, có thể được sử dụng để tìm tối đa thứ 2, 3, 4 (v.v.):

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
Mát mẻ. Cách sử dụng này đặc biệt hữu ích maxN(1:10, 1:3)(tôi sẽ đặt N mặc định thành 1)
PatrickT

23

Rfast có một hàm gọi là nth_element thực hiện chính xác những gì bạn yêu cầu và nhanh hơn tất cả các triển khai đã thảo luận ở trên

Ngoài ra, các phương pháp được thảo luận ở trên dựa trên sắp xếp một phần, không hỗ trợ tìm k giá trị nhỏ nhất

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

Sẽ trả về phần tử lớn thứ 5 của x, trong khi

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

Sẽ trả về phần tử nhỏ thứ 5 của x

Điểm chuẩn dưới đây so với hầu hết các câu trả lời phổ biến.

Cho 10 nghìn số:

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

Đối với 1 triệu số:

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
Đẹp! Thông thường khi tôi thấy một người dùng tương đối thấp, thêm câu trả lời cho một câu hỏi cũ phổ biến, đó là chất lượng khá thấp. Điều này, mặt khác, là một bổ sung tuyệt vời. Tôi đã thực hiện một vài chỉnh sửa dễ đọc, nhưng nó trông rất tuyệt!
Gregor Thomas

3
Nó đề cập đến việc Rfast::nthcó thể trả về nhiều yếu tố (ví dụ như các yếu tố lớn thứ 8 và 9) cũng như các chỉ số của các yếu tố đó.
Jasha

3
Điều tôi thích về giải pháp Rfast là gói cũng có một giải pháp dễ thực hiện để thực hiện điều này cho mỗi hàng hoặc cột.
Jay

16

Đây là một cách dễ dàng để tìm các chỉ số của N giá trị nhỏ nhất / lớn nhất trong một vectơ (Ví dụ cho N = 3):

N <- 3

N nhỏ nhất:

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

N lớn nhất:

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

Vì vậy, bạn có thể trích xuất các giá trị như:

x[ndx]

Điều này chạy trong L log L thời gian, trong đó L là chiều dài của x. Tôi nghĩ rằng người dùng đã hy vọng cho một phương thức chạy trong thời gian đăng nhập L.
arsmath

Đây có thể là cách nhanh thứ hai nếu các phương thức được sắp xếp theo thời gian và N được trích xuất nhanh nhất. Tôi cũng thích nó vì nó là mã rất rõ ràng so với giải pháp được chấp nhận.
Pete

1
Phương pháp tốt nhất về mặt lý thuyết và phương pháp được chấp nhận (hy vọng) chạy trong thời gian O (L), không phải O (log L). Cái này chạy trong O (L log L).
Valentas

6

Đối với giá trị cao thứ n,

sort(x, TRUE)[n]

8
OP đã nói trong bài đăng của mình rằng đây là một giải pháp mà anh ấy không muốn sử dụng: "ngoài việc sắp xếp toàn bộ vectơ và hơn là chọn giá trị x từ vectơ này".
Paul Hiemstra

3

Tôi thấy rằng việc loại bỏ phần tử max trước và sau đó thực hiện một lần chạy max khác với tốc độ tương đương:

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

Đây là cách đơn giản nhất mà tôi tìm thấy,

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

Khi gần đây tôi đang tìm kiếm một hàm R trả về các chỉ số của các số max / min N hàng đầu trong một vectơ đã cho, tôi đã ngạc nhiên khi không có hàm nào như vậy.

Và đây là một cái gì đó rất giống nhau.

Giải pháp vũ lực sử dụng hàm base :: order dường như là cách dễ nhất.

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

Nhưng nó không phải là nhanh nhất trong trường hợp giá trị N của bạn tương đối nhỏ so với chiều dài của vectơ x .

Mặt khác, nếu N thực sự nhỏ, bạn có thể sử dụng hàm base :: whichMax lặp đi lặp lại và trong mỗi lần lặp, bạn có thể thay thế giá trị tìm thấy bằng -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
}

Tôi tin rằng bạn thấy vấn đề - bản chất sao chép khi sửa đổi của R. Vì vậy, điều này sẽ hoạt động tốt hơn đối với N rất nhỏ (1,2,3) nhưng nó sẽ nhanh chóng chậm lại đối với các giá trị N lớn hơn. Và bạn đang lặp lại tất cả các phần tử trong vectơ x N lần.

Tôi nghĩ rằng giải pháp tốt nhất trong sạch R là sử dụng một phần cơ sở :: sort .

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

Sau đó, bạn có thể chọn mục ( N th) cuối cùng từ kết quả của chức năng defiend ở trên.

Lưu ý: các chức năng được xác định ở trên chỉ là ví dụ - nếu bạn muốn sử dụng chúng, bạn phải kiểm tra / đầu vào sanity (ví dụ: N> length (x) ).

Tôi đã viết một bài viết nhỏ về một cái gì đó rất giống nhau (lấy chỉ số của các giá trị N max / min hàng đầu của một vectơ) tại http://palusga.cz/?p=18 - bạn có thể tìm thấy ở đây một số điểm chuẩn của các hàm tương tự tôi đã xác định ở trê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)
}

hàm này sẽ trả về một ma trận có n giá trị trên cùng và các chỉ số của chúng. hy vọng nó sẽ giúp VDevi-Chou


0

Điều này sẽ tìm thấy chỉ số của giá trị nhỏ nhất hoặc lớn nhất thứ N trong vectơ số đầu vào x. Đặt bottom = TRUE trong các đối số nếu bạn muốn N'th từ dưới lên hoặc bottom = FALSE nếu bạn muốn N'th từ trên xuống. N = 1 và bottom = TRUE tương đương với which.min, N = 1 và bottom = FALSE tương đương với 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 có hàm thứ n, trong đó đối số đầu tiên là vectơ và thứ hai là nơi bạn muốn. Điều này đi để lặp lại các yếu tố là tốt. Ví dụ:

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

Tìm giá trị lớn thứ hai:

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

[1] 17

2
nhanh thế này ...?
Ben Bolker

2
bên trong này sử dụng x[[order(order_by)[[n]]]]- vì vậy nó yêu cầu sắp xếp toàn bộ vector. Vì vậy, nó sẽ không nhanh như câu trả lời được chấp nhận.
Ben Bolker

5
nhưng nó sử dụng sort với đối số
part

@BenBolker ngụ ý câu trả lời của Paolo hoặc Rob có thể được sử dụng để cải thiện dplyr::nth()? bench::mark(max(x[-which.max(x)]), x[[order(-x)[[2]]]] ), nth()dường như chậm hơn gần 10 lần, đâu length(x)là 3 triệu.
sindri_baldur

-1

Bạn có thể xác định giá trị cao hơn tiếp theo với cummax(). Nếu bạn muốn vị trí của mỗi giá trị mới cao hơn chẳng hạn, bạn có thể chuyển vectơ cummax()giá trị của mình cho diff()hàm để xác định vị trí tại đó cummax()giá trị thay đổi. nói rằng chúng ta có vector

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

Bây giờ, nếu bạn muốn tìm vị trí thay đổi trong cummax()bạn có nhiều tùy chọn tôi có xu hướng sử dụng sign(diff(cummax(v))). Bạn phải điều chỉnh cho phần tử đầu tiên bị mất vì diff(). Mã hoàn chỉnh cho vector vsẽ là:

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

Tôi nghĩ rằng bạn hiểu sai câu hỏi. Mục tiêu là tìm, nói, giá trị cao thứ hai. Làm thế nào điều này giúp bạn từ v đến 12 ... và cao thứ ba đến 8?
Frank

-1

Bạn có thể sử dụng sorttừ khóa như thế này:

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

Thí dụ:

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

sẽ cho 5 số tối đa đầu tiên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.