在r中查找并替换数字序列

C_p*_*psy 11 replace r sequence

我有一个数据帧,其序列号与下面类似:

data <- c(1,1,1,0,0,1,1,2,2,2,0,0,0,2,1,1,0,1,0,2)
Run Code Online (Sandbox Code Playgroud)

我需要的是找到1,2或3次重复0的所有实例,其中前进和后续数字是相同的 - 即1或2都是2(例如1,0,1或2,0,0,2但是不是2,0,1).

然后我需要用周围的值填充零.

我设法找到并计算连续的零

consec <- (!data) * unlist(lapply(rle(data)$lengths, seq_len))
Run Code Online (Sandbox Code Playgroud)

然后我找到了这些连续零开始的行:

consec <- as.matrix(consec)
first_na <- which(consec==1,arr.ind=TRUE)
Run Code Online (Sandbox Code Playgroud)

但我对替换过程感到困惑

我非常感谢你对此的帮助!

卡尔

And*_*rie 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
Run Code Online (Sandbox Code Playgroud)

PS.我使用local()一个简单的机制,不用大量的新临时对象来破坏工作区.你可以创建一个function而不是使用local- 我发现我现在使用local很多这类任务.


PPS.您必须修改此代码以排除原始数据中的前导或尾随零.


Din*_*nre 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
Run Code Online (Sandbox Code Playgroud)

步骤 1:制作所有数据值的单个字符串。

str.values <- paste(data, collapse="")
# "11100112220002110102"
Run Code Online (Sandbox Code Playgroud)

这会将数据折叠成一个长字符串,因此我们可以对其使用正则表达式。

步骤 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
Run Code Online (Sandbox Code Playgroud)

在本例中,我们使用正则表达式来查找第一个模式,即一到三个零 ( [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
Run Code Online (Sandbox Code Playgroud)

我们在这里同时执行几个步骤。首先,我们根据正则表达式中匹配的数字创建一个数字序列列表。在本例中,有两个匹配项,它们从索引 3 和 16 开始,长度分别为 4 和 3 项。这意味着我们的零点位于索引 (3+1):(3-2+4) 或 4:5 处,以及 (16+1):(16-2+3) 或 17:17 处。我们再次使用“折叠”选项连接(“粘贴”)这些序列,以防存在多个匹配项。然后,我们使用第二个串联将序列放入组合(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
Run Code Online (Sandbox Code Playgroud)

更新:我意识到最初的问题是匹配连续的一到三个零,而不是我写入原始代码的“两个或更多”。我已经更新了正则表达式和解释,尽管代码保持不变。