2013-05-03 42 views
15

Tôi đang tự hỏi về những nhiệm vụ đơn giản của việc tách một vector thành hai tại một chỉ số nhất định:R chia vector và chữ số ở vị trí

splitAt <- function(x, pos){ 
    list(x[1:pos-1], x[pos:length(x)]) 
} 

a <- c(1, 2, 2, 3) 

> splitAt(a, 4) 
[[1]] 
[1] 1 2 2 

[[2]] 
[1] 3 

Câu hỏi của tôi: Phải có một số chức năng hiện có cho điều này, nhưng tôi có thể' Tôi tìm thấy nó? Có phải là split một khả năng? Việc triển khai ngây thơ của tôi cũng không hoạt động nếu pos=0 hoặc pos>length(a).

Trả lời

23

Một cải tiến sẽ là:

splitAt <- function(x, pos) unname(split(x, cumsum(seq_along(x) %in% pos))) 

bây giờ mà có thể mất một vector của các vị trí:

splitAt(a, c(2, 4)) 
# [[1]] 
# [1] 1 
# 
# [[2]] 
# [1] 2 2 
# 
# [[3]] 
# [1] 3 

Và nó cư xử đúng (chủ quan) nếu pos <= 0 hoặc pos >= length(x) theo nghĩa là nó trả về toàn bộ vector gốc trong một mục danh sách duy nhất. Thay vào đó, nếu bạn muốn lỗi đó xảy ra, hãy sử dụng stopifnot ở đầu chức năng.

+0

Cảm ơn, công trình này tốt cho tôi! Tôi vẫn ngạc nhiên rằng không có chức năng 'splitAt' được thực hiện trong cơ sở R ... – user1981275

+0

Chức năng này rất chậm với' x' rất lớn, có lẽ do 'seq_along (x)' tạo ra một vectơ rất dài và sau đó '% in%' phải khớp với vectơ rất dài này. – Calimo

+0

@Calimo: không, nếu bạn cấu hình nó, bạn sẽ thấy rằng hầu hết thời gian được dùng bên trong 'split' chậm chạp. Bạn chắc chắn có thể tránh nó nhưng bạn sẽ mất rất nhiều về khả năng đọc và mã nhỏ gọn. – flodel

4

Tôi đã cố gắng sử dụng flodel's answer, nhưng nó quá chậm trong trường hợp của tôi với số lượng rất lớn x (và hàm này phải được gọi nhiều lần). Vì vậy, tôi tạo ra các chức năng sau đây là nhanh hơn nhiều, nhưng cũng rất xấu xí và không hoạt động đúng. Đặc biệt, nó không kiểm tra bất cứ điều gì và sẽ trả về kết quả lỗi ít nhất cho pos >= length(x) hoặc pos <= 0 (bạn có thể tự mình kiểm tra nếu bạn không chắc về đầu vào của mình và không quá lo lắng về tốc độ) và có thể một số trường hợp khác , hãy cẩn thận.

splitAt2 <- function(x, pos) { 
    out <- list() 
    pos2 <- c(1, pos, length(x)+1) 
    for (i in seq_along(pos2[-1])) { 
     out[[i]] <- x[pos2[i]:(pos2[i+1]-1)] 
    } 
    return(out) 
} 

Tuy nhiên, splitAt2 chạy khoảng 20 lần nhanh hơn với một x chiều dài 10 :

library(microbenchmark) 
W <- rnorm(1e6) 
splits <- cumsum(rep(1e5, 9)) 
tm <- microbenchmark(
        splitAt(W, splits), 
        splitAt2(W, splits), 
        times=10) 
tm 
+0

Cảm ơn! Cũng với ví dụ đơn giản từ trên, 'splitAt2' hoạt động tốt hơn. – user1981275

+2

+1 - một phần khá viết lại có thể là: 'function (x, pos) {pos <- c (1L, pos, length (x) + 1L); Bản đồ (hàm (x, i, j) x [i: j], danh sách (x), đầu (pos, -1L), đuôi (pos, -1L) - 1L)} '. Nó cũng có vẻ nhanh hơn một chút vì số lượng phân chia tăng lên, không chắc chắn tại sao. – flodel

+0

@ user1981275 xác định "tốt hơn". Nếu tốt hơn = nhanh hơn tôi đồng ý, nhưng như là một chức năng mục đích chung mạnh mẽ là chìa khóa, trong đó trường hợp flodel của phiên bản là tốt hơn. – Calimo

1

Một lựa chọn khác mà có thể nhanh hơn và/hoặc dễ đọc hơn/thanh lịch hơn flodel's solution:

splitAt <- function(x, pos) { 
    unname(split(x, findInterval(x, pos))) 
} 
Các vấn đề liên quan