我正在将非结构化数据转换为长格式,需要创建一个ID(分组)变量。我想根据另一个变量中包含的值集来分配ID变量。更具体地说,请考虑以下数据集。
set.seed(1234); x.1 <- rep(letters[1:5], 10)
x.2 <- sample(c(0:10), 50, replace=TRUE)
x.3 <- rep(NA, 50); df <- data.frame(x.1, x.2, x.3)
df <- df[-c(2, 19),]
可以从x.1变量中识别出一个独特的案例 - 它以a
开头,以e
结尾。情况总是如此。 x.3将保存ID(分组)变量。
> head(df, 9)
x.1 x.2 x.3
a 1 NA
c 6 NA
d 6 NA
e 9 NA
a 7 NA
b 0 NA
c 2 NA
d 7 NA
e 5 NA
对于给定案例,a
和e
之间的记录数量可能会有很大差异(在实际数据文件中)。因此,我不能通过简单地将变量除以固定数量的记录来分配唯一ID。我想出了如何使用for循环进行正确的赋值:
START <- which(df$x.1== "a")
END <- which(df$x.1 == "e")
for(i in 1:length(START)){df$x.3[START[i]:END[i]] <- i}
head(df, 9)
x.1 x.2 x.3
a 1 1
c 6 1
d 6 1
e 9 1
a 7 2
b 0 2
c 2 2
d 7 2
e 5 2
这种方法的一个明显问题是,对于拥有超过一百万条记录的数据集而言,这个问题太慢了。似乎lapply
可能是另一种选择,但我似乎无法弄清楚如何在案例结束时指定新案例,并在遍历数据文件时开始新案例。并且,如果有的话,请随时指出我现有的答案 - 我没有罚款!
提前致谢。
答案 0 :(得分:7)
如果组之间没有间隙,即在每个&#34; e&#34;之后。遵循&#34; a&#34;对于下一组,您可以轻松使用cumsum
:
df$x.3 <- cumsum(df$x.1 == "a")
df
# x.1 x.2 x.3
#1 a 1 1
#3 c 6 1
#4 d 6 1
#5 e 9 1
#6 a 7 2
#7 b 0 2
#8 c 2 2
#9 d 7 2
#10 e 5 2
#11 a 7 3
#12 b 5 3
#13 c 3 3
#...
如果您的数据非常大,您可以使用data.table通过引用来更新数据:
library(data.table)
setDT(df)[, x.3 := cumsum(x.1 == "a")]
正如@nicola在评论中正确指出的那样,这假设a
仅出现在组的开头,不在其中间。根据样本数据,这似乎是一个有效的假设。
工作原理:
让我们看一下列的一部分&#34; x.1&#34;:
x <- df$x.1[1:15]
x
# [1] a c d e a b c d e a b c d e a
#Levels: a b c d e
您现在可以检查x是否等于&#34; a&#34;这将创建一个逻辑向量:
x == "a"
# [1] TRUE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE
现在,cumsum
做了什么:它累计累积所有TRUE值(基本上是1):
cumsum(x == "a")
# [1] 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4
因此,你可以使用像数字向量这样的逻辑向量,并使用它们进行数学计算,就像1和0的向量一样。