2011-12-11 21 views
8

Tôi khá chắc chắn rằng tất cả các bạn đều đồng ý rằng rle là một trong những hàm "gotcha" trong R. Có chức năng tương tự nào có thể "bắt" một "chạy không "của các giá trị số nguyên liền kề?chức năng giống như rle bắt "chạy" các số nguyên liền kề

Vì vậy, nếu tôi có một véc tơ như thế này:

x <- c(3:5, 10:15, 17, 22, 23, 35:40) 

và tôi gọi đó là chức năng bí truyền, tôi sẽ nhận được câu trả lời như thế này:

lengths: 3, 6, 1, 2, 6 
values: (3,4,5), (10,11,12... # you get the point 

Nó không phải là khó viết một chức năng như thế này, nhưng vẫn ... bất cứ ý tưởng nào?

+1

Tôi tin rằng bạn có nghĩa là độ dài 3, 6, 1, 2, 6 ... ngoài ra, bạn sẽ làm gì với C (4,4,5,6,9)? – John

+0

Tôi nghĩ những người chơi golf có thể có một ngày với cái này! – Spacedman

+0

có thể trùng lặp [khoảng thời gian phát hiện các chuỗi số nguyên quả hậu quả] (http://stackoverflow.com/questions/8400901/detect-intervals-of-the-consequent-integer-sequences) –

Trả lời

8

1) Tính giá trị và sau đó độ dài dựa trên giá trị

s <- split(x, cumsum(c(0, diff(x) != 1))) 
run.info <- list(lengths = unname(sapply(s, length)), values = unname(s)) 

Chạy nó bằng cách sử x từ câu hỏi cho này:

> str(run.info) 
List of 2 
$ lengths: int [1:5] 3 6 1 2 6 
$ values :List of 5 
    ..$ : num [1:3] 3 4 5 
    ..$ : num [1:6] 10 11 12 13 14 15 
    ..$ : num 17 
    ..$ : num [1:2] 22 23 
    ..$ : num [1:6] 35 36 37 38 39 40 

2) Tính độ dài và sau đó giá trị dựa trên độ dài

Đây là giải pháp thứ hai d trên Gregor's length calculation:

lens <- rle(x - seq_along(x))$lengths 
list(lengths = lens, values = unname(split(x, rep(seq_along(lens), lens)))) 

3) Tính độ dài và các giá trị mà không sử dụng khác

này ai có vẻ không hiệu quả vì nó tính toán mỗi lengthsvalues từ đầu và nó cũng có vẻ hơi quá phức tạp nhưng nó quản lý để có được tất cả xuống một tuyên bố duy nhất vì vậy tôi nghĩ rằng tôi sẽ thêm nó là tốt. Về cơ bản nó chỉ là một kết hợp của hai giải pháp trước được đánh dấu 1) và 2) ở trên. Không có gì thực sự mới liên quan đến hai.

list(lengths = rle(x - seq_along(x))$lengths, 
      values = unname(split(x, cumsum(c(0, diff(x) != 1))))) 

EDIT: Đã thêm giải pháp thứ hai.

EDIT: Đã thêm giải pháp thứ ba.

+1

Rất đẹp. Và nếu bạn sẵn sàng sử dụng 'rle', điều này có thể được đơn giản hóa thành' rle (cumsum (c (0, diff (x)! = 1))) $ length' –

+0

@Josh, Điều đó chỉ tính toán độ dài và dường như không thực sự đơn giản hơn. –

+0

OK - Tôi nên đọc câu hỏi cẩn thận hơn, và nó làm cho giải pháp của bạn trở nên ấn tượng hơn. –

5

Như bạn nói, thật dễ dàng để viết một cái gì đó tương tự như rle. Trên thực tế, việc điều chỉnh mã cho rle bằng cách thêm + 1 có thể cung cấp một cái gì đó giống như

rle_consec <- function(x) 
{ 
    if (!is.vector(x) && !is.list(x)) 
     stop("'x' must be an atomic vector") 
    n <- length(x) 
    if (n == 0L) 
    return(structure(list(lengths = integer(), values = x), 
      class = "rle_consec")) 
    y <- x[-1L] != x[-n] + 1 
    i <- c(which(y | is.na(y)), n) 
    structure(list(lengths = diff(c(0L, i)), values = x[i]), 
       class = "rle_consec") 
} 

và sử dụng ví dụ của bạn

> x <- c(3:5, 10:15, 17, 22, 23, 35:40) 
> rle_consec(x) 
$lengths 
[1] 3 6 1 2 6 

$values 
[1] 5 15 17 23 40 

attr(,"class") 
[1] "rle_consec" 

đó là những gì John mong đợi.

Bạn có thể điều chỉnh mã thêm để cung cấp cho đầu tiên của mỗi chuỗi liên tiếp thay vì sau cùng.

6

Làm thế nào về

rle(x - 1:length(x))$lengths 
# 3 6 1 2 6 

Chiều dài là những gì bạn muốn, mặc dù tôi tẩy trống trên một cách bình đẳng thông minh để có được các giá trị thích hợp, nhưng với cumsum() và bản gốc x chúng rất dễ tiếp cận.

Các vấn đề liên quan