วิธีที่เร็วที่สุดในการค้นหาค่าสูงสุด / ต่ำสุดที่สอง (สาม ... ) ในเวกเตอร์หรือคอลัมน์


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) ดังนั้นจะต้องเร็วกว่าการเรียงลำดับบนชุดข้อมูลขนาดใหญ่
Muse

ทำงานได้ดีกับ NAs มากกว่าคำตอบที่ยอมรับอื่น ๆ - เพียงใช้ 'na.rm = TRUE' เป็นอาร์กิวเมนต์สำหรับฟังก์ชัน 'min'
Yair Daon

2
ดูเหมือนว่าคุณจะได้รับการปรับปรุงความเร็วอย่างมากด้วยการดัดแปลงเล็กน้อย:max(x[-which.max(x)])
sindri_baldur

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 ซึ่งทำสิ่งที่คุณขออย่างแน่นอนและเร็วกว่าการนำไปใช้ทั้งหมดที่กล่าวถึงข้างต้น

ยังมีวิธีการที่กล่าวข้างต้นว่าจะขึ้นอยู่กับการจัดเรียงบางส่วนไม่สนับสนุนการหาเคที่เล็กที่สุดค่า

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

จะส่งคืนองค์ประกอบที่ใหญ่ที่สุดอันดับ 5 ของ x ในขณะที่

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

จะส่งคืนองค์ประกอบที่เล็กที่สุดที่ 5 ของ 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 ฉันคิดว่าผู้ใช้หวังว่าจะมีวิธีที่ใช้ในเวลา log L
arsmath

นี่อาจเป็นวิธีที่เร็วที่สุดที่สองหากวิธีการนั้นได้รับคำสั่งตามเวลาและการสกัด N ที่เร็วที่สุด ฉันก็ชอบเพราะมันเป็นรหัสที่ชัดเจนมากเมื่อเทียบกับโซลูชั่นที่ได้รับการยอมรับ
Pete

1
ทฤษฎีที่ดีที่สุดและวิธีที่ยอมรับ (หวังว่า) จะทำงานในเวลา O (L) ไม่ใช่ O (log L) อันนี้ทำงานใน O (L log L)
Valentas

6

สำหรับค่าสูงสุดที่ n

sort(x, TRUE)[n]

9
OP กล่าวแล้วในโพสต์ของเขาว่านี่เป็นวิธีการแก้ปัญหาที่เขาไม่ต้องการใช้: "นอกเหนือจากการเรียงลำดับเวกเตอร์ทั้งหมดและเลือกหยิบค่า x จากเวกเตอร์นี้"
Paul Hiemstra

3

ฉันพบว่าการลบองค์ประกอบสูงสุดก่อนแล้วจึงดำเนินการอีกค่าสูงสุดด้วยความเร็วที่เทียบเคียงได้:

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

เมื่อเร็ว ๆ นี้ฉันกำลังมองหาฟังก์ชันR ที่ส่งคืนดัชนีของจำนวนสูงสุด / นาที N สูงสุดในเวกเตอร์ที่กำหนดฉันประหลาดใจที่ไม่มีฟังก์ชั่นดังกล่าว

และนี่คือสิ่งที่คล้ายกันมาก

วิธีการแก้ปัญหากำลังดุร้ายโดยใช้ฟังก์ชัน:: baseดูเหมือนจะเป็นวิธีที่ง่ายที่สุด

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

แต่มันไม่ได้เป็นหนึ่งที่เร็วที่สุดในกรณีของคุณไม่มีค่าค่อนข้างเล็กเมื่อเทียบกับความยาวของเวกเตอร์x

ในอีกด้านหนึ่งถ้าNมีขนาดเล็กมากคุณสามารถใช้base :: ซึ่งฟังก์ชั่นmaximer iteratively และในแต่ละการทำซ้ำคุณสามารถแทนที่ค่าที่พบโดย-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คือการใช้บางส่วนฐาน :: การจัดเรียง

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

จากนั้นคุณสามารถเลือกรายการสุดท้าย ( N th) จากผลลัพธ์ของฟังก์ชั่น defiend ด้านบน

หมายเหตุ: ฟังก์ชั่นที่กำหนดไว้ด้านบนเป็นเพียงตัวอย่าง - หากคุณต้องการใช้พวกเขาคุณต้องตรวจสอบ / อินพุตที่มีสติ (เช่นN> length (x) )

ฉันเขียนบทความเล็ก ๆ เกี่ยวกับสิ่งที่คล้ายกันมาก (รับดัชนีของค่าสูงสุด N / min สูงสุดของเวกเตอร์) ที่http://palusga.cz/?p=18 - คุณสามารถหาเกณฑ์มาตรฐานของฟังก์ชั่นที่คล้ายกันที่ฉันกำหนดไว้ด้านบน



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

นี่จะหาดัชนีของค่าที่น้อยที่สุดหรือมากที่สุดของ N ในเวกเตอร์ตัวเลขอินพุต x กำหนด bottom = TRUE ในอาร์กิวเมนต์หากคุณต้องการ N'th จากด้านล่างหรือล่าง = FALSE หากคุณต้องการ N'th จากด้านบน 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 มีฟังก์ชัน nth โดยที่อาร์กิวเมนต์แรกคือเวกเตอร์และอันที่สองคือตำแหน่งที่คุณต้องการ สิ่งนี้จะไปสำหรับองค์ประกอบที่ทำซ้ำเช่นกัน ตัวอย่างเช่น:

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

@BenBolker ซึ่งแสดงถึงคำตอบของ Paolo หรือ Rob สามารถนำไปปรับปรุงได้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
แฟรงค์

-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.