使用 := 处理复杂公式(data.table 通过引用赋值)
Handling complex formulas with := (data.table assignment by reference)
要在 label
上计算 x
的一些聚合并将其添加到数据中,我可以使用以下代码,对于 mean
是:
library(data.table)
setDT(data)[, y := mean(x), label]
但是如何计算意味着只有当label
给出的组的大小超过5时,否则输入0。我首先尝试使用 length
、nrow
而不是 mean
关键字来计算组的大小,但这不是正确的方法并且不起作用。我使用的示例数据集:
set.seed(123)
data<-data.frame(label=sample(c("A","B"),10,replace=TRUE),x=rnorm(10))
data
# label x
#1 A 1.7150650
#2 B 0.4609162
#3 A -1.2650612
#4 B -0.6868529
#5 B -0.4456620
#6 A 1.2240818
#7 B 0.3598138
#8 B 0.4007715
#9 B 0.1106827
#10 A -0.5558411
我看到像这样的尝试代码:
setDT(data)[, y := ifelse(nrow(x)>10,mean(x),0), label] # don't run
方向错误
在此示例中,x
是一个向量,因此 nrow()
是用于查看有多少元素的错误函数。使用 length
set.seed(123)
data<-data.frame(label=sample(c("A","B"),10,replace=TRUE),x=rnorm(10))
setDT(data)[, y := ifelse(length(x)>4,mean(x),0), label]
data
# label x y
# 1: A 1.7150650 0.00000000
# 2: B 0.4609162 0.03327823
# 3: A -1.2650612 0.00000000
# 4: B -0.6868529 0.03327823
# 5: B -0.4456620 0.03327823
# 6: A 1.2240818 0.00000000
# 7: B 0.3598138 0.03327823
# 8: B 0.4007715 0.03327823
# 9: B 0.1106827 0.03327823
# 10: A -0.5558411 0.00000000
你可以试试
setDT(data)[, y:=if(.N>4) mean(x) else 0, label][]
基准
尝试在“1e7”数据集上使用 as.data.table
和 setDT
,并为 "label" 设置了两组。所有方法都显示出相似的效率(尽管当组数增加时这可能会改变)
set.seed(198)
data <- data.frame(label=sample(LETTERS[1:2], 1e7, replace=TRUE), x=rnorm(1e7))
data1 <- copy(data)
data2 <- copy(data)
data3 <- copy(data)
n <- 5e6
David1 <- function() {setDT(data)[, y := mean(x)[.N > n] , label]}
David2 <- function() {setDT(data1)[, y := 0][, y := mean(x)[.N > n], label]}
akrun <- function() {setDT(data2)[, y:=if(.N>n) mean(x) else 0, label]}
MrFlick <- function() {setDT(data3)[, y := ifelse(length(x)>n,
mean(x),0), label]}
library(microbenchmark)
microbenchmark(David1(), David2(), akrun(), MrFlick(),
unit='relative', times=20L)
#Unit: relative
# expr min lq mean median uq max neval cld
# David1() 0.9226054 1.005485 0.9975527 1.006531 0.9897817 0.9738954 20 ab
# David2() 1.0722181 1.058603 1.0388910 1.060785 1.0449793 0.9334972 20 b
# akrun() 0.9843013 1.000373 0.9899616 1.001635 0.9917036 0.9492853 20 a
#MrFlick() 1.0000000 1.000000 1.0000000 1.000000 1.0000000 1.0000000 20 ab
将 setDT
更改为 as.data.table
microbenchmark(David1(), David2(), akrun(), MrFlick(),
unit='relative', times=20L)
#Unit: relative
# expr min lq mean median uq max neval cld
# David1() 0.9963619 1.0014244 0.9973844 1.006967 1.010804 1.015443 20 a
# David2() 1.1682075 1.1817214 1.1982023 1.185832 1.280648 1.176238 20 b
# akrun() 0.9885094 0.9986409 1.0085403 1.002375 1.004836 1.007429 20 a
#MrFlick() 1.0000000 1.0000000 1.0000000 1.000000 1.000000 1.000000 20 a
我建议你避免 ifesle
一起,因为效率和当你不想计算平均值时放置 0
是错误的,如果其中之一会发生什么这些组的均值也为零,您将如何区分它们?我会做
setDT(data)[, y := mean(x)[.N > 4] , label][]
# label x y
# 1: A 1.7150650 NA
# 2: B 0.4609162 0.03327823
# 3: A -1.2650612 NA
# 4: B -0.6868529 0.03327823
# 5: B -0.4456620 0.03327823
# 6: A 1.2240818 NA
# 7: B 0.3598138 0.03327823
# 8: B 0.4007715 0.03327823
# 9: B 0.1106827 0.03327823
# 10: A -0.5558411 NA
要在 label
上计算 x
的一些聚合并将其添加到数据中,我可以使用以下代码,对于 mean
是:
library(data.table)
setDT(data)[, y := mean(x), label]
但是如何计算意味着只有当label
给出的组的大小超过5时,否则输入0。我首先尝试使用 length
、nrow
而不是 mean
关键字来计算组的大小,但这不是正确的方法并且不起作用。我使用的示例数据集:
set.seed(123)
data<-data.frame(label=sample(c("A","B"),10,replace=TRUE),x=rnorm(10))
data
# label x
#1 A 1.7150650
#2 B 0.4609162
#3 A -1.2650612
#4 B -0.6868529
#5 B -0.4456620
#6 A 1.2240818
#7 B 0.3598138
#8 B 0.4007715
#9 B 0.1106827
#10 A -0.5558411
我看到像这样的尝试代码:
setDT(data)[, y := ifelse(nrow(x)>10,mean(x),0), label] # don't run
方向错误
在此示例中,x
是一个向量,因此 nrow()
是用于查看有多少元素的错误函数。使用 length
set.seed(123)
data<-data.frame(label=sample(c("A","B"),10,replace=TRUE),x=rnorm(10))
setDT(data)[, y := ifelse(length(x)>4,mean(x),0), label]
data
# label x y
# 1: A 1.7150650 0.00000000
# 2: B 0.4609162 0.03327823
# 3: A -1.2650612 0.00000000
# 4: B -0.6868529 0.03327823
# 5: B -0.4456620 0.03327823
# 6: A 1.2240818 0.00000000
# 7: B 0.3598138 0.03327823
# 8: B 0.4007715 0.03327823
# 9: B 0.1106827 0.03327823
# 10: A -0.5558411 0.00000000
你可以试试
setDT(data)[, y:=if(.N>4) mean(x) else 0, label][]
基准
尝试在“1e7”数据集上使用 as.data.table
和 setDT
,并为 "label" 设置了两组。所有方法都显示出相似的效率(尽管当组数增加时这可能会改变)
set.seed(198)
data <- data.frame(label=sample(LETTERS[1:2], 1e7, replace=TRUE), x=rnorm(1e7))
data1 <- copy(data)
data2 <- copy(data)
data3 <- copy(data)
n <- 5e6
David1 <- function() {setDT(data)[, y := mean(x)[.N > n] , label]}
David2 <- function() {setDT(data1)[, y := 0][, y := mean(x)[.N > n], label]}
akrun <- function() {setDT(data2)[, y:=if(.N>n) mean(x) else 0, label]}
MrFlick <- function() {setDT(data3)[, y := ifelse(length(x)>n,
mean(x),0), label]}
library(microbenchmark)
microbenchmark(David1(), David2(), akrun(), MrFlick(),
unit='relative', times=20L)
#Unit: relative
# expr min lq mean median uq max neval cld
# David1() 0.9226054 1.005485 0.9975527 1.006531 0.9897817 0.9738954 20 ab
# David2() 1.0722181 1.058603 1.0388910 1.060785 1.0449793 0.9334972 20 b
# akrun() 0.9843013 1.000373 0.9899616 1.001635 0.9917036 0.9492853 20 a
#MrFlick() 1.0000000 1.000000 1.0000000 1.000000 1.0000000 1.0000000 20 ab
将 setDT
更改为 as.data.table
microbenchmark(David1(), David2(), akrun(), MrFlick(),
unit='relative', times=20L)
#Unit: relative
# expr min lq mean median uq max neval cld
# David1() 0.9963619 1.0014244 0.9973844 1.006967 1.010804 1.015443 20 a
# David2() 1.1682075 1.1817214 1.1982023 1.185832 1.280648 1.176238 20 b
# akrun() 0.9885094 0.9986409 1.0085403 1.002375 1.004836 1.007429 20 a
#MrFlick() 1.0000000 1.0000000 1.0000000 1.000000 1.000000 1.000000 20 a
我建议你避免 ifesle
一起,因为效率和当你不想计算平均值时放置 0
是错误的,如果其中之一会发生什么这些组的均值也为零,您将如何区分它们?我会做
setDT(data)[, y := mean(x)[.N > 4] , label][]
# label x y
# 1: A 1.7150650 NA
# 2: B 0.4609162 0.03327823
# 3: A -1.2650612 NA
# 4: B -0.6868529 0.03327823
# 5: B -0.4456620 0.03327823
# 6: A 1.2240818 NA
# 7: B 0.3598138 0.03327823
# 8: B 0.4007715 0.03327823
# 9: B 0.1106827 0.03327823
# 10: A -0.5558411 NA