带索引的for循环的替代方案 - R.

时间:2015-01-26 20:53:28

标签: r lapply

我正在将非结构化数据转换为长格式,需要创建一个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

对于给定案例,ae之间的记录数量可能会有很大差异(在实际数据文件中)。因此,我不能通过简单地将变量除以固定数量的记录来分配唯一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可能是另一种选择,但我似乎无法弄清楚如何在案例结束时指定新案例,并在遍历数据文件时开始新案例。并且,如果有的话,请随时指出我现有的答案 - 我没有罚款!

提前致谢。

1 个答案:

答案 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的向量一样。