2013-02-25 95 views
11

數字序列我有編號的類似於下面序列的數據幀:查找和替換r中

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

我需要的東西來定位的0,其中的1,2或3次重複的所有實例程序和以下數字是相同的 - 即1或2和2(例如1,0,1或2,0,0,2但不是2,0,1)。

然後我只需要填充零周圍的值。

我已成功地定位並計算連續零

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

然後我發現這些地方連續零開頭的行:

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

但我與更換過程

難倒

我真的很感謝你的幫助!

卡爾

回答

2

由於對這個問題的答案似乎有很多興趣,所以我想我會爲後人寫一個替代的正則表達式方法。

使用'gregexpr'函數,您可以搜索出模式並使用結果位置匹配和匹配長度來調出在原始向量中更改哪些值。使用正則表達式的好處在於,我們可以明確地確定要匹配哪些模式,因此我們不會有任何排除案例需要擔心。

注意:以下示例按書面形式工作,因爲我們假定爲單位數值。我們可以很容易地適應其他模式,但我們可以採用單個字符的小捷徑。如果我們想用可能的多位數值來做到這一點,我們希望添加一個分隔符作爲第一個連接('粘貼')功能的一部分。


守則

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 

步驟1:使所有的數據值的一個字符串。

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

這倒塌下來的數據轉換成一個長字符串,所以我們可以在其上使用正則表達式。

第2步:應用正則表達式來查找字符串內任何匹配的位置和長度。

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

在這種情況下,我們使用正則表達式來查找第一圖案,一到三個零([0]{2,})與那些在兩邊(1[0]{1,3}1)。我們必須匹配整個模式,以防止在兩端檢查匹配的或兩個匹配的模式。我們將在下一步中將這些結果減掉。

步驟3:將1寫入原始向量中的所有匹配位置。

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 

我們在這裏一次完成了幾個步驟。首先,我們從正則表達式中匹配的數字創建數字序列列表。在這種情況下,有兩個匹配,分別從索引3和16開始,分別是4和3個項目。這意味着我們的零點位於索引(3 + 1):(3-2 + 4),或4:5和(16 + 1):(16-2 + 3)或17:17。如果有多個匹配,我們再次使用'collapse'選項連接('粘貼')這些序列。然後,我們使用第二個級聯將序列放入組合函數(c())中。使用'eval'和'parse'函數,我們將此文本轉換爲代碼並將其作爲索引值傳遞給[data]數組。我們寫入所有的地方。

步驟x:對每個模式重複。在這種情況下,我們需要進行第二次搜索並在兩邊找到一到三個零,然後運行與步驟3相同的語句,但分配兩個而不是一個。

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 

更新:我意識到原來的問題說成一排匹配一到三個零,而不是「兩個或更多」,我寫進原代碼。我更新了正則表達式和解釋,儘管代碼保持不變。

+0

所以,我最終選擇了這個,我喜歡對模式進行控制的能力 - 但是我很欣賞所有的建議。儘管如此,我會在不同情況下記下這些不同的方法。真的很感激它。 – 2013-04-19 11:41:49

1

有可能是沒有for循環的解決方案,但你可以試試這個:

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) 

其中給出:

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

我認爲你可以通過使用rle(as.logical(data))來「收緊」,這會使'tmp'長度爲'零'和'非零',然後你可以替換每次運行的零都有'val [i-1] *(val [i-1] == val [i + 1])''。 (如果我把它搞砸了,意圖是用'val [i-1]'替換零,但是隻有當相等檢查爲真時) - 這必須相當小心:-(unrle -d – 2013-02-25 12:52:25

+0

@CarlWitthoft嗯,如果你使用'rle(as.logical(data))'你不能用你的'rle $ values'來測試值是否相等嗎? – juba 2013-02-25 12:54:16

+0

Nevvamind - Andrie的答案是做我的正在考慮更加緊湊(可靠)。 – 2013-02-25 14:13:51

14

下面是使用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。我使用local()作爲一種簡單的機制,以便不會使用大量新臨時對象來打開工作空間。你可以創建一個function而不是使用local - 我只是發現我現在使用local這種類型的任務。


PPS。您將不得不修改此代碼以排除原始數據中的前導零或尾隨零。

+0

這就是'rle'函數應該使用的方式,我很高興你寫得很清楚。 '本地'功能也是一個很好的提示。我通過將許多代碼封裝在函數中(也適用於調試)來做大致相同的事情,我認爲這對人們總體上是一件好事。好工作,安德里。 – Dinre 2013-02-25 20:39:55