如何重新编码两个特定值之间的一系列行?

9

I have following data frame:

a <- seq(1:14)
b <- c(0, 0, "start", 0, 0, 0, "end", 0, 0, "start", 0, "end", 0, 0)
df <- data.frame(a, b)

 df
a      b
1      0
2      0
3   start
4      0
5      0
6      0
7    end
8      0
9      0
10  start
11     0
12   end
13     0
14     0

现在,我想要做的是重新编码位于“start”和“end”之间的b中的值,使得:
 df
a      b
1      0
2      0
3   start
4      1
5      1
6      1
7    end
8      0
9      0
10  start
11     1
12   end
13     0
14     0

到目前为止,我还没有得到任何可行的代码。我尝试使用data.table包中的which()between()inrange(),但我无法真正弄清楚它们的用法。有什么想法可以解决这个问题吗?


2
如果您感兴趣,可以查看类似的问题:https://dev59.com/BLLma4cB1Zd3GeqPYUYU - Andrew
2个回答

12

鉴于

df <- data.frame(a, b, stringsAsFactors = FALSE)
#                      ^^^^^^^^^^^^^^^^^^^^^^^^

我们可以做到

idx <- (cumsum(b == "start") - cumsum(b == "end") - (b == "start")) == 1
df <- transform(df, b = replace(b, idx, "1"))
df
#    a     b
#1   1     0
#2   2     0
#3   3 start
#4   4     1
#5   5     1
#6   6     1
#7   7   end
#8   8     0
#9   9     0
#10 10 start
#11 11     1
#12 12   end
#13 13     0
#14 14     0

idx是指在"start"和"end"之间的元素为TRUE

当我们调用cumsum(b == "start") - cumsum(b == "end")时,我们已经接近目标了。

cumsum(b == "start") - cumsum(b == "end")
# [1] 0 0 1 1 1 1 0 0 0 1 1 0 0 0

我们只需要将 b == "start" 的位置设为零,即

cumsum(b == "start") - cumsum(b == "end") - b == "start"
# [1] 0 0 0 1 1 1 0 0 0 0 1 0 0 0

测试这个向量是否为1,使它变成逻辑值

idx <- (cumsum(b == "start") - cumsum(b == "end") - (b == "start")) == 1

结果

idx
[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE
我们使用这个逻辑向量来替换b相应的元素为"1"

3
获取输出的好方法 - akrun

4
@RonakShah评论中更简洁的答案是:
df$b[unlist(mapply(`:`, which(df$b == "start") + 1, which(df$b == "end") - 1))] <- 1

与上述简洁的答案类似的逻辑,使用 lapply,我们在这里找到起始和结束位置,将其映射到列表并找到索引,然后用 1 替换该索引。
starting <- which(b == "start")
ending <- which(b == "end")
my.ls <- lapply(Map(c, starting, ending), function(x) (x[1]+1):(x[2]-1))

index <- unlist(my.ls)
b[index] <- 1


df <- data.frame(a, b)
df
a     b
1   1     0
2   2     0
3   3 start
4   4     1
5   5     1
6   6     1
7   7   end
8   8     0
9   9     0
10 10 start
11 11     1
12 12   end
13 13     0
14 14     0

旧循环答案
您可以使用以下的which函数,首先定义所有的起始和结束点,然后循环遍历并将它们改为1...
a <- seq(1:14)
b <- c(0, 0, "start", 0, 0, 0, "end", 0, 0, "start", 0, "end", 0, 0)

starting <- which(b == "start")
ending <- which(b == "end")

for (i in 1:length(starting)){
  index <- (starting[i]+1):(ending[i]-1)
  b[index] <- 1
}
df <- data.frame(a, b)
df

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接