如何在两个特定值之间重新编码行范围

时间:2019-05-09 10:23:35

标签: r

我有以下数据框:

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

现在,我要做的是重新编码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

到目前为止,我还没有任何有效的代码。我尝试使用which()软件包中的between()inrange()data.table进行了一些操作,但是我真的无法弄清楚。有什么想法可以解决这个问题吗?

2 个答案:

答案 0 :(得分: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

对于“开始”和“结束”之间的元素,

idxTRUE

当我们打电话给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"

答案 1 :(得分: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

旧循环答案

您可以使用如下功能,首先定义所有起点和终点,然后遍历并将其更改为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