2013-02-25 59 views
11

Tôi có một dataframe với một chuỗi các con số tương tự như dưới đây:tìm và thay thế chuỗi số trong r

data <- c(1,1,1,0,0,1,1,2,2,2,0,0,0,2,1,1,0,1,0,2) 

Những gì tôi cần là một cái gì đó để xác định tất cả các trường hợp 1, 2 hoặc 3 lần lặp lại từ 0 nơi số tiến hành và các số sau giống hệt nhau - tức là cả 1 hoặc cả 2 (ví dụ: 1,0,1 hoặc 2,0,0,2 nhưng KHÔNG phải 2,0,1).

Sau đó, tôi chỉ cần điền vào các số 0 với giá trị xung quanh.

Tôi đã cố gắng xác định vị trí và đếm số không liên tiếp

consec <- (!data) * unlist(lapply(rle(data)$lengths, seq_len)) 

sau đó tôi đã tìm thấy hàng nơi những số không liên tiếp bắt đầu với:

consec <- as.matrix(consec) 
first_na <- which(consec==1,arr.ind=TRUE) 

Nhưng tôi bối rối với quá trình thay thế

Tôi thực sự đánh giá cao sự trợ giúp của bạn về điều này!

Carl

Trả lời

2

Vì dường như có rất nhiều sự quan tâm trong câu trả lời cho câu hỏi này, tôi nghĩ tôi sẽ viết một phương thức biểu thức chính quy thay thế cho hậu thế.

Sử dụng chức năng 'gregexpr', bạn có thể tìm kiếm các mẫu và sử dụng kết quả vị trí kết quả và độ dài phù hợp để gọi ra giá trị nào cần thay đổi trong vectơ gốc. Lợi thế của việc sử dụng các biểu thức thông thường là chúng ta có thể rõ ràng về các mẫu mà chúng ta muốn khớp, và kết quả là chúng ta sẽ không có bất kỳ trường hợp loại trừ nào cần lo lắng.

Lưu ý: Ví dụ sau hoạt động như được viết bởi vì chúng tôi giả định giá trị một chữ số. Chúng tôi có thể dễ dàng điều chỉnh nó cho các mẫu khác, nhưng chúng tôi có thể thực hiện một lối tắt nhỏ với các ký tự đơn. Nếu chúng ta muốn làm điều này với các giá trị nhiều chữ số có thể, chúng ta sẽ muốn thêm một ký tự phân tách như là một phần của hàm nối đầu tiên ('dán').


Bộ luật

str.values <- paste(data, collapse="") # String representation of vector 
str.matches <- gregexpr("1[0]{1,3}1", str.values) # Pattern 101/1001/10001 
data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 1 # Replace zeros with ones 
str.matches <- gregexpr("2[0]{1,3}2", str.values) # Pattern 202/2002/20002 
data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 2 # Replace zeros with twos 

Bước 1: Thực hiện một chuỗi duy nhất của tất cả các giá trị dữ liệu.

str.values <- paste(data, collapse="") 
# "11100112220002110102" 

Điều này thu gọn dữ liệu thành một chuỗi dài, vì vậy chúng tôi có thể sử dụng cụm từ thông dụng trên đó.

Bước 2: Áp dụng cụm từ thông dụng để tìm vị trí và độ dài của bất kỳ kết quả phù hợp nào trong chuỗi.

str.matches <- gregexpr("1[0]{1,3}1", str.values) 
# [[1]] 
# [1] 3 16 
# attr(,"match.length") 
# [1] 4 3 
# attr(,"useBytes") 
# [1] TRUE 

Trong trường hợp này, chúng tôi đang sử dụng một biểu thức chính quy để tìm kiếm các mô hình đầu tiên, 1-3 số không ([0]{2,}) với những người ở hai bên (1[0]{1,3}1). Chúng tôi sẽ phải phù hợp với toàn bộ mô hình, để ngăn chặn việc phải kiểm tra các mẫu phù hợp hoặc twos trên đầu. Chúng tôi sẽ trừ các kết thúc đó trong bước tiếp theo.

Bước 3: Viết vào tất cả các vị trí phù hợp trong vectơ gốc.

data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 1 
# 1 1 1 1 1 1 1 2 2 2 0 0 0 2 1 1 1 1 0 2 

Chúng tôi đang thực hiện một vài bước cùng một lúc tại đây. Đầu tiên, chúng tôi đang tạo một danh sách các chuỗi số từ các số phù hợp trong cụm từ thông dụng. Trong trường hợp này, có hai kết quả phù hợp, bắt đầu từ các chỉ mục 3 và 16 và tương ứng là 4 và 3 mục. Điều này có nghĩa là số không của chúng ta được đặt tại các chỉ mục (3 + 1): (3-2 + 4), hoặc 4: 5 và tại (16 + 1): (16-2 + 3), hoặc 17:17. Chúng tôi nối ('dán') các chuỗi này bằng cách sử dụng tùy chọn 'thu gọn' một lần nữa, trong trường hợp có nhiều kết quả phù hợp. Sau đó, chúng tôi sử dụng phép nối thứ hai để đặt các chuỗi bên trong hàm kết hợp (c()). Sử dụng các hàm 'eval' và 'parse', chúng ta chuyển văn bản này thành mã và chuyển nó thành các giá trị chỉ mục cho mảng [data]. Chúng tôi viết tất cả những người vào những địa điểm đó.

Bước x: Lặp lại cho từng mẫu. Trong trường hợp này, chúng ta cần phải thực hiện tìm kiếm thứ hai và tìm một đến ba số không với các twos ở hai bên và sau đó chạy câu lệnh tương tự như Bước 3, nhưng gán các twos, thay vì các từ.

str.matches <- gregexpr("2[0]{1,3}2", str.values) 
# [[1]] 
# [1] 10 
# attr(,"match.length") 
# [1] 5 
# attr(,"useBytes") 
# [1] TRUE 

data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 2 
# 1 1 1 1 1 1 1 2 2 2 2 2 2 2 1 1 1 1 0 2 

Cập nhật: Tôi nhận ra vấn đề ban đầu cho biết để phù hợp với 1-3 số không liên tiếp, chứ không phải là "hai hoặc nhiều" mà tôi viết vào mã gốc. Tôi đã cập nhật các biểu thức chính quy và giải thích, mặc dù mã vẫn giữ nguyên.

+0

vì vậy, tôi thực sự đã đi cho điều này cuối cùng, tôi yêu khả năng có quyền kiểm soát các mô hình - nhưng tôi đánh giá cao tất cả các đề xuất. Tôi sẽ lưu ý các phương pháp khác nhau này cho các hoàn cảnh khác nhau. Thực sự đánh giá cao nó. –

1

Có thể có một giải pháp mà không có một vòng lặp for, nhưng bạn có thể thử điều này:

tmp <- rle(data) 
val <- tmp$values 
for (i in 2:(length(val)-1)) { 
    if (val[i]==0 & val[i-1]==val[i+1]) val[i] <- val[i-1] 
} 
tmp$values <- val 
inverse.rle(tmp) 

Mà cho:

[1] 1 1 1 1 1 1 1 2 2 2 2 2 2 2 1 1 1 1 0 2 
+0

Tôi nghĩ bạn có thể "thắt chặt" điều này bằng cách thực hiện 'rle (as.logical (data))' sẽ điền vào 'tmp' của bạn với độ dài 'zero' và 'not-zero', sau đó bạn có thể thay thế mỗi lần chạy các số không với một cái gì đó như 'val [i-1] * (val [i-1] == val [i + 1])'. (Trong trường hợp tôi vặn nó lên, ý định là thay thế các zero bằng 'val [i-1]' nhưng chỉ khi kiểm tra bình đẳng là TRUE) - tho 'điều này sẽ phải khá cẩn thận :-(un-rle –

+0

@CarlWitthoft Hmm nếu bạn sử dụng 'rle (as.logical (data))' bạn không thể sử dụng 'rle $ values' để kiểm tra các giá trị bằng nhau nữa không? – juba

+0

Nevvamind - Câu trả lời của Andrie làm những gì tôi đã suy nghĩ thậm chí còn gọn gàng hơn (và đáng tin cậy). –

14

Dưới đây là một giải pháp loopless sử dụng rle()inverse.rle().

data <- c(1,1,1,0,0,1,1,2,2,2,0,0,0,2,1,1,0,1,0,2) 

local({ 
    r <- rle(data) 
    x <- r$values 
    x0 <- which(x==0) # index positions of zeroes 
    xt <- x[x0-1]==x[x0+1] # zeroes surrounded by same value 
    r$values[x0[xt]] <- x[x0[xt]-1] # substitute with surrounding value 
    inverse.rle(r) 
}) 

[1] 1 1 1 1 1 1 1 2 2 2 2 2 2 2 1 1 1 1 0 2 

PS. Tôi sử dụng local() như một cơ chế đơn giản để không che khuất không gian làm việc với vô số các đối tượng tạm thời mới. Bạn có thể tạo một function thay vì sử dụng local - Tôi chỉ thấy tôi sử dụng local rất nhiều hiện nay cho loại nhiệm vụ này.


PPS. Bạn sẽ phải sửa đổi mã này để loại trừ số 0 đầu hoặc cuối trong dữ liệu gốc của bạn.

+0

Đây chính xác là cách sử dụng chức năng 'rle' và tôi rất vui vì bạn đã viết nó một cách rõ ràng. Hàm 'cục bộ' cũng là một mẹo hay. Tôi làm điều tương tự bằng cách gói rất nhiều mã của tôi trong các chức năng (cũng tốt cho việc gỡ lỗi), và tôi nghĩ rằng đó là một điều tốt cho mọi người học nói chung. Làm tốt lắm, Andrie. – Dinre

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