提问人:C_psy 提问时间:2/25/2013 最后编辑:EDiC_psy 更新时间:5/25/2020 访问量:3104
在 R 中查找和替换数字序列
find and replace numeric sequence in r
问:
我有一个数据帧,其数字序列如下所示:
data <- c(1,1,1,0,0,1,1,2,2,2,0,0,0,2,1,1,0,1,0,2)
我需要的是找到 1、2 或 3 个重复的 0 的所有实例,其中后续数字和后续数字相同 - 即 1 或两个 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)
但我对更换过程感到困惑
我非常感谢您的帮助!
卡尔
答:
可能有一个没有循环的解决方案,但您可以尝试以下方法: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
评论
rle(as.logical(data))
tmp
val[i-1]*(val[i-1]==val[i+1])
val[i-1]
rle(as.logical(data))
rle$values
这是一个使用 和 的无循环解决方案。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
缴费灵。您必须修改此代码以排除原始数据中的前导或尾随零。
评论
由于似乎对这个问题的答案很感兴趣,我想我会为后代写一种替代的正则表达式方法。
使用“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。我们再次使用“折叠”选项连接(“粘贴”)这些序列,以防有多个匹配项。然后,我们使用第二个串联将序列放在 combine () 函数中。使用 'eval' 和 'parse' 函数,我们将此文本转换为代码,并将其作为索引值传递给 [data] 数组。我们将所有内容都写入这些位置。c()
步骤x:对每个模式重复。在这种情况下,我们需要进行第二次搜索,找到一到三个零,两边都有 2,然后运行与步骤 3 相同的语句,但分配 2,而不是 1。
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
更新:我意识到原来的问题说要连续匹配一到三个零,而不是我写到原始代码中的“两个或更多”。我已经更新了正则表达式和解释,尽管代码保持不变。
评论
对于那些在 2020 年对此进行研究的人,我仅使用 gsub 进行了序列替换。
str.values <- paste(YOUR$COLUMN, collapse="")
str.values2 <- gsub("ORIGINAL PATTERN","PATTERN TO REPLACE", str.values)
评论