我有以下数据框:
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
进行了一些操作,但是我真的无法弄清楚。有什么想法可以解决这个问题吗?
答案 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
idx
是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"
。
答案 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