R为不使用循环的记录分配ID
R assign ID to records NOT using loops
我需要遍历数据 table "A" 并根据条件为该记录或记录组分配增量 ID,例如:
library(data.table)
A <- data.table(x = c(1,2,3,4,5,6,7,8,9,10,11,12,13,14),
y = c(2,2,2,2,2,2,2,2,3,3,3,3,3,3), z = 0)
for(i in 1:nrow(A))
{
if((A[i]$x %% A[i]$y) == 0) {A[i]$z <- i}
print(i)
}
z 列变成了一种滚动 ID。
我需要在不使用循环的情况下执行相同的操作。
你可以获取索引 where %%
operator returns 0 并在该位置分配索引值。
inds <- A$x %% A$y == 0
A$z[inds] <- which(inds)
A
# x y z
# 1: 1 2 0
# 2: 2 2 2
# 3: 3 2 0
# 4: 4 2 4
# 5: 5 2 0
# 6: 6 2 6
# 7: 7 2 0
# 8: 8 2 8
# 9: 9 3 9
#10: 10 3 0
#11: 11 3 0
#12: 12 3 12
#13: 13 3 0
#14: 14 3 0
或者你可以试试这个(sinde x 已经包含索引值)
在满足条件 x %% y == 0
的行上用 x-value 更新(通过引用)z-value。在所有其他行上 z
保持其原始值(即 0)。
A[ x %% y == 0, z:=x]
# x y z
# 1: 1 2 0
# 2: 2 2 2
# 3: 3 2 0
# 4: 4 2 4
# 5: 5 2 0
# 6: 6 2 6
# 7: 7 2 0
# 8: 8 2 8
# 9: 9 3 9
# 10:10 3 0
# 11:11 3 0
# 12:12 3 12
# 13:13 3 0
# 14:14 3 0
当然你也可以使用.I
来获取一行的索引
A[ x %% y == 0, z := .I]
也可以...根据您的 column-classes,您必须将一些 integer-columns 设置为 class 两倍,以避免出现警告消息。
基准
最多50000行,Ronaks的答案更快,在此之上,.I
的解决方案是'winning'。
用于基准测试的代码
vec <- c( seq( 1,10000, by = 1000), seq( 1,100000, by = 10000),
seq( 1,1000000, by = 100000), seq( 1,10000000, by = 1000000) )
l <- lapply( vec, function(x){
A <- data.table(x = as.double( 1:x ),
y = as.double( sample(2:3, x, replace = TRUE) ),
z = as.double(0) )
m <- microbenchmark::microbenchmark(
Ronak = {
DT <- copy(A)
inds <- DT$x %% DT$y == 0
DT$z[inds] <- which(inds)
},
Wimpel = {
DT <- copy(A)
DT[ x %% y == 0, z:=as.double(.I)]
},
times = 10 )
setDT(m)[, .(n = x, median = median(time)), by = .(expr)][]
})
library(scales)
library(ggplot2)
ggplot( data = rbindlist(l), aes( x = n, y = median/1000000, group = expr, colour = expr )) +
geom_smooth( se = FALSE ) +
labs( x = "rows",
y = "median [ms]" )
我需要遍历数据 table "A" 并根据条件为该记录或记录组分配增量 ID,例如:
library(data.table)
A <- data.table(x = c(1,2,3,4,5,6,7,8,9,10,11,12,13,14),
y = c(2,2,2,2,2,2,2,2,3,3,3,3,3,3), z = 0)
for(i in 1:nrow(A))
{
if((A[i]$x %% A[i]$y) == 0) {A[i]$z <- i}
print(i)
}
z 列变成了一种滚动 ID。 我需要在不使用循环的情况下执行相同的操作。
你可以获取索引 where %%
operator returns 0 并在该位置分配索引值。
inds <- A$x %% A$y == 0
A$z[inds] <- which(inds)
A
# x y z
# 1: 1 2 0
# 2: 2 2 2
# 3: 3 2 0
# 4: 4 2 4
# 5: 5 2 0
# 6: 6 2 6
# 7: 7 2 0
# 8: 8 2 8
# 9: 9 3 9
#10: 10 3 0
#11: 11 3 0
#12: 12 3 12
#13: 13 3 0
#14: 14 3 0
或者你可以试试这个(sinde x 已经包含索引值)
在满足条件 x %% y == 0
的行上用 x-value 更新(通过引用)z-value。在所有其他行上 z
保持其原始值(即 0)。
A[ x %% y == 0, z:=x]
# x y z
# 1: 1 2 0
# 2: 2 2 2
# 3: 3 2 0
# 4: 4 2 4
# 5: 5 2 0
# 6: 6 2 6
# 7: 7 2 0
# 8: 8 2 8
# 9: 9 3 9
# 10:10 3 0
# 11:11 3 0
# 12:12 3 12
# 13:13 3 0
# 14:14 3 0
当然你也可以使用.I
来获取一行的索引
A[ x %% y == 0, z := .I]
也可以...根据您的 column-classes,您必须将一些 integer-columns 设置为 class 两倍,以避免出现警告消息。
基准
最多50000行,Ronaks的答案更快,在此之上,.I
的解决方案是'winning'。
用于基准测试的代码
vec <- c( seq( 1,10000, by = 1000), seq( 1,100000, by = 10000),
seq( 1,1000000, by = 100000), seq( 1,10000000, by = 1000000) )
l <- lapply( vec, function(x){
A <- data.table(x = as.double( 1:x ),
y = as.double( sample(2:3, x, replace = TRUE) ),
z = as.double(0) )
m <- microbenchmark::microbenchmark(
Ronak = {
DT <- copy(A)
inds <- DT$x %% DT$y == 0
DT$z[inds] <- which(inds)
},
Wimpel = {
DT <- copy(A)
DT[ x %% y == 0, z:=as.double(.I)]
},
times = 10 )
setDT(m)[, .(n = x, median = median(time)), by = .(expr)][]
})
library(scales)
library(ggplot2)
ggplot( data = rbindlist(l), aes( x = n, y = median/1000000, group = expr, colour = expr )) +
geom_smooth( se = FALSE ) +
labs( x = "rows",
y = "median [ms]" )