在 R 中查找和替换数字序列

find and replace numeric sequence in r

提问人:C_psy 提问时间:2/25/2013 最后编辑:EDiC_psy 更新时间:5/25/2020 访问量:3104

问:

我有一个数据帧,其数字序列如下所示:

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)

但我对更换过程感到困惑

我非常感谢您的帮助!

卡尔

r 替换 序列

评论


答:

1赞 juba 2/25/2013 #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赞 Carl Witthoft 2/25/2013
我认为你可以通过这样做来“收紧”它,这将用“零”和“非零”的长度填充你的长度,之后你可以用类似的东西替换每一次零。(如果我搞砸了,目的是用零替换零,但只有当相等检查为 TRUE 时)——这必须相当小心:-(un-rle-ed .rle(as.logical(data))tmpval[i-1]*(val[i-1]==val[i+1])val[i-1]
0赞 juba 2/25/2013
@CarlWitthoft 嗯,如果你使用,你就不能再用你的来测试值相等性了?rle(as.logical(data))rle$values
0赞 Carl Witthoft 2/25/2013
Nevvamind -- Andrie 的回答更紧凑(更可靠)地完成了我所想的。
0赞 C_psy 2/26/2013
循环是我的第一个想法,但就是无法到达那里!非常感谢 - 工作得很愉快!
1赞 juba 2/26/2013
也许你应该接受@Andrie的答案而不是我的答案?它更清楚,而且在很大程度上被点赞了......
14赞 Andrie 2/25/2013 #2

这是一个使用 和 的无循环解决方案。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()functionlocallocal


缴费灵。您必须修改此代码以排除原始数据中的前导或尾随零。

评论

0赞 Dinre 2/26/2013
这正是“rle”函数的使用方式,我很高兴你写得这么清楚。“本地”功能也是一个不错的提示。我通过将大量代码包装在函数中来做大致相同的事情(也有利于调试),我认为这对人们来说是一件好事。干得好,安德里。
2赞 Dinre 2/26/2013 #3

由于似乎对这个问题的答案很感兴趣,我想我会为后代写一种替代的正则表达式方法。

使用“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

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

评论

0赞 C_psy 4/19/2013
所以,我最终选择了这个,我喜欢控制模式的能力 - 但我感谢所有的建议。不过,我会记下这些针对不同情况的不同方法。真的很感激。
0赞 RafaelRS 5/25/2020 #4

对于那些在 2020 年对此进行研究的人,我仅使用 gsub 进行了序列替换。

str.values <- paste(YOUR$COLUMN, collapse="") 
str.values2 <- gsub("ORIGINAL PATTERN","PATTERN TO REPLACE", str.values)