data.table按行求和、平均值、最小值、最大值(如dplyr)?
还有其他关于datatable上的行运算符的帖子。它们不是一个,就是一个 我这里的问题更一般。有一个使用dplyr的解决方案。我已经尝试过了,但没有找到使用data.table语法的等效解决方案。您能推荐一个优雅的data.table解决方案,它可以重现与dplyr版本相同的结果吗 编辑1:真实数据集(10MB,73000行,24个数字列的统计数据)上建议解决方案的基准汇总。基准结果是主观的。但是,经过的时间始终是可重复的data.table按行求和、平均值、最小值、最大值(如dplyr)?,r,data.table,dplyr,R,Data.table,Dplyr,还有其他关于datatable上的行运算符的帖子。它们不是一个,就是一个 我这里的问题更一般。有一个使用dplyr的解决方案。我已经尝试过了,但没有找到使用data.table语法的等效解决方案。您能推荐一个优雅的data.table解决方案,它可以重现与dplyr版本相同的结果吗 编辑1:真实数据集(10MB,73000行,24个数字列的统计数据)上建议解决方案的基准汇总。基准结果是主观的。但是,经过的时间始终是可重复的 | Solution By | Speed compared to dp
| Solution By | Speed compared to dplyr |
|-------------|-----------------------------|
| Metrics v1 | 4.3 times SLOWER (use .SD) |
| Metrics v2 | 5.6 times FASTER |
| ExperimenteR| 15 times FASTER |
| Arun v1 | 3 times FASTER (Map func)|
| Arun v2 | 3 times FASTER (foo func)|
| Ista | 4.5 times FASTER |
编辑2:我在一天后添加了一个计数列。这就是为什么在各种贡献者建议的解决方案中找不到本专栏的原因
数据设置
library(data.table)
dt <- data.table(ProductName = c("Lettuce", "Beetroot", "Spinach", "Kale", "Carrot"),
Country = c("CA", "FR", "FR", "CA", "CA"),
Q1 = c(NA, 61, 40, 54, NA), Q2 = c(22, 8, NA, 5, NA),
Q3 = c(51, NA, NA, 16, NA), Q4 = c(79, 10, 49, NA, NA))
# ProductName Country Q1 Q2 Q3 Q4
# 1: Lettuce CA NA 22 51 79
# 2: Beetroot FR 61 8 NA 10
# 3: Spinach FR 40 NA NA 49
# 4: Kale CA 54 5 16 NA
# 5: Carrot CA NA NA NA NA
data.table出错(计算整列而不是每行)
几乎是解决方案,但更复杂且缺少Q1、Q2、Q3、Q4输出列
dtmelt <- reshape2::melt(dt, id=c("ProductName", "Country"),
variable.name="Quarter", value.name="Qty")
dtmelt[, .(AVG = mean(Qty, na.rm=TRUE),
MIN = min (Qty, na.rm=TRUE),
MAX = max (Qty, na.rm=TRUE),
SUM = sum (Qty, na.rm=TRUE),
NAcnt= sum(is.na(Qty))), by = list(ProductName, Country)]
# ProductName Country AVG MIN MAX SUM NAcnt
# 1: Lettuce CA 50.66667 22 79 152 1
# 2: Beetroot FR 26.33333 8 61 79 1
# 3: Spinach FR 44.50000 40 49 89 2
# 4: Kale CA 25.00000 5 54 75 1
# 5: Carrot CA NaN Inf -Inf 0 4
dtmelt您可以使用来自matrixStats
包的高效行函数
library(matrixStats)
dt[, `:=`(MIN = rowMins(as.matrix(.SD), na.rm=T),
MAX = rowMaxs(as.matrix(.SD), na.rm=T),
AVG = rowMeans(.SD, na.rm=T),
SUM = rowSums(.SD, na.rm=T)), .SDcols=c(Q1, Q2,Q3,Q4)]
dt
# ProductName Country Q1 Q2 Q3 Q4 MIN MAX AVG SUM
# 1: Lettuce CA NA 22 51 79 22 79 50.66667 152
# 2: Beetroot FR 61 8 NA 10 8 61 26.33333 79
# 3: Spinach FR 40 NA 79 49 40 79 56.00000 168
# 4: Kale CA 54 5 16 NA 5 54 25.00000 75
# 5: Carrot CA NA NA NA NA Inf -Inf NaN 0
对于具有500000行的数据集(使用CRAN中的data.table
)
使用by=1:nrow(dt)
,在数据表中执行行操作
library(data.table)
dt[, `:=`(AVG= mean(as.numeric(.SD),na.rm=TRUE),MIN = min(.SD, na.rm=TRUE),MAX = max(.SD, na.rm=TRUE),SUM = sum(.SD, na.rm=TRUE)),.SDcols=c(Q1, Q2,Q3,Q4),by=1:nrow(dt)]
ProductName Country Q1 Q2 Q3 Q4 AVG MIN MAX SUM
1: Lettuce CA NA 22 51 79 50.66667 22 79 152
2: Beetroot FR 61 8 NA 10 26.33333 8 61 79
3: Spinach FR 40 NA 79 49 56.00000 40 79 168
4: Kale CA 54 5 16 NA 25.00000 5 54 75
5: Carrot CA NA NA NA NA NaN Inf -Inf 0
Warning messages:
1: In min(c(NA_real_, NA_real_, NA_real_, NA_real_), na.rm = TRUE) :
no non-missing arguments to min; returning Inf
2: In max(c(NA_real_, NA_real_, NA_real_, NA_real_), na.rm = TRUE) :
no non-missing arguments to max; returning -Inf
您收到了警告消息,因为在第5行中,您正在计算“零”的最大值、总和、最小值和最大值。例如,请参见以下内容:
min(c(NA,NA,NA,NA),na.rm=TRUE)
[1] Inf
Warning message:
In min(c(NA, NA, NA, NA), na.rm = TRUE) :
no non-missing arguments to min; returning Inf
另一种方法(虽然效率不高,因为每次都调用na.omit()
,还有许多内存分配):
但正如我提到的,一旦实现了colwise()
和rowwise()
,这将变得简单得多。本例中的语法可能类似于:
dt[, rowwise(.SD, list(MIN=min, MAX=max, SUM=sum, AVG=mean), na.rm=TRUE), by = 1:nrow(dt)]
# `by = ` is really not necessary in this case.
对于这种情况,甚至更简单:
rowwise(dt, list(...), na.rm=TRUE)
编辑:
另一个变化:
myNACount <- function(x, ...) length(attributes(x)$na.action)
foo <- function(x, ...) {
funs = c(min, max, mean, sum, myNACount)
lapply(funs, function(f) f(x, ...))
}
dt[, (new_cols) := foo(na.omit(c(Q1, Q2, Q3, Q4)), na.rm=TRUE), by=1:nrow(dt)]
# ProductName Country Q1 Q2 Q3 Q4 MIN MAX SUM AVG NAs
# 1: Lettuce CA NA 22 51 79 22 79 50.66667 152 1
# 2: Beetroot FR 61 8 NA 10 8 61 26.33333 79 1
# 3: Spinach FR 40 NA NA 49 40 49 44.50000 89 2
# 4: Kale CA 54 5 16 NA 5 54 25.00000 75 1
# 5: Carrot CA NA NA NA NA Inf -Inf NaN 0 4
myNACount可以使用apply
功能执行行计算。单独定义函数可以使事情更干净:
dstats <- function(x){
c(mean(x,na.rm=TRUE),
min(x, na.rm=TRUE),
max(x, na.rm=TRUE),
sum(x, na.rm=TRUE))
}
请注意,使用[.data.table
进行此操作的唯一优点是允许使用:=
通过引用快速添加
这比matrixStats
解决方案慢但更灵活,比@Experiator的dplyr
解决方案快,打卡时间为36秒(我对其他方法的计时与@Experiator的答案中的计时类似)。我希望其他人遇到同样的问题时,他们可能会觉得有用
第一种方法:合并基本R
第二种方法:基于@experiator思想,使用matrixStats包
dt1dt[,AVG:=rowMeans(.SD,na.rm=T),.SDcols=c(Q1,Q2,Q3,Q4)]
@实验者谢谢(SDcols应该是字符向量吗?)我试过这个dt[,(Q1,Q2,Q3,Q4,AVG=rowMeans(.SD,na.rm=T),MIN=pmin(Q1,Q2,Q3,Q3,Q4,na.rm=T),MAX=pmax(Q1,Q1,Q2,Q4,na.rm=T)),.Q1,Q3=c(“,.Q4”)
,但仍然缺少SUM,并且没有产品名称和国家/地区columns@Metrics没有计算错误的输出b/c:dt[,`:=`(AVG=rowMeans(.SD,na.rm=TRUE),MIN=MIN(.SD,na.rm=TRUE),MAX=MAX(.SD,na.rm=TRUE),SUM=SUM(.SD,na.rm=TRUE)),.SDcols=c(“Q1”,“Q2”,“Q3”,“Q4”),by=1:nrow(dt)]警告消息:1:In MIN(c(NA_real,NA_real,NA_real,NA_real,NA_real),NA.rm=TRUE):最小值没有未丢失的参数;返回Inf 2:In max(c(NA_real,NA_real,NA_real,NA_real,NA_real,NA,NA_real),NA.rm=TRUE):max没有未丢失的参数;返回-Inf
请参阅我的答案。我已更新了代码并从注释中删除。Dplyr和data.table都为NaN和-Inf发出警告。data.table尽可能使用base R函数,以避免强加“围墙花园”方法..但是基本R没有一个很好的函数来执行这个操作:-(.所以我们必须实现colwise()
和rowwise()
函数,在…下归档。我已将其标记为下一版本。同样的错误,可能是b/c我使用的是最新数据。表1.9.4(R版本3.2.0(2015-04-16))?此外,我必须将SDcols置于引号中。SDcols=c(“Q1”、“Q2”、“Q3”、“Q4”)
以避免“找不到对象Q1”。以下是我运行代码时出现的错误:1:In min(c(NA_real,NA_real,NA_real,NA_real),NA.rm=TRUE):min没有不丢失的参数;返回Inf 2:In max(c(NA(NA_real,NA_real,NA_real,NA_real),NA.rm=TRUE):max没有未丢失的参数;返回-Inf
这些是警告而不是错误(我也知道了)。您得到警告是因为您的输出返回无限值-Inf
、Inf
、和NaN
(因为您取的是无值的平均值、总和、最小值和最大值)。如果您运行自己的dplyr代码,它也会发出相同的警告。我使用的是开发版本1.9.5+(您可以从github获得)。我不确定您为什么需要加引号。它运行时没有为我加引号。请在答案中查看我的更新。哦,这是真的。我忘记打印(dt)。抱歉!顺便说一句,如果我不在中的列名周围加引号,你知道为什么我找不到对象“Q1”
。SDcols=c(Q1,Q2,Q3,Q4)
(data.table 1.9.4,R v3.2.0)刚刚在一个10MB数据集上应用了你的解决方案,73000行。dplyr版本比你建议的实现快4倍。这可能是as.numeric(.SD)吗在平均值的计算中?你不能在如此小的数据集上进行基准测试,这是毫无意义的。是的,你为什么要在行方式的潜在解决方案中添加by
?可能会出现类似dt[,if(TRUE)do_bla else rowwise(…),by=some_cols]
(就像我说的,在这种情况下,没有必要)@Arun ThatmyNACount@Arun Ahem…很抱歉,我在基准测试中犯了一个错误。您所做的第二个版本比第一个版本稍快。最快的执行时间来自于实验者的解决方案。@polymer,不用担心。我想我们在这里都学到了很多:-)。很好的问题1。apply()
convertsrequire(data.table)
new_cols = c("MIN", "MAX", "SUM", "AVG")
dt[, (new_cols) := Map(function(x, f) f(x),
list(na.omit(c(Q1,Q2,Q3,Q4))),
list(min, max, sum, mean)),
by = 1:nrow(dt)]
# ProductName Country Q1 Q2 Q3 Q4 MIN MAX SUM AVG
# 1: Lettuce CA NA 22 51 79 22 79 152 50.66667
# 2: Beetroot FR 61 8 NA 10 8 61 79 26.33333
# 3: Spinach FR 40 NA 79 49 40 79 168 56.00000
# 4: Kale CA 54 5 16 NA 5 54 75 25.00000
# 5: Carrot CA NA NA NA NA Inf -Inf 0 NaN
dt[, rowwise(.SD, list(MIN=min, MAX=max, SUM=sum, AVG=mean), na.rm=TRUE), by = 1:nrow(dt)]
# `by = ` is really not necessary in this case.
rowwise(dt, list(...), na.rm=TRUE)
myNACount <- function(x, ...) length(attributes(x)$na.action)
foo <- function(x, ...) {
funs = c(min, max, mean, sum, myNACount)
lapply(funs, function(f) f(x, ...))
}
dt[, (new_cols) := foo(na.omit(c(Q1, Q2, Q3, Q4)), na.rm=TRUE), by=1:nrow(dt)]
# ProductName Country Q1 Q2 Q3 Q4 MIN MAX SUM AVG NAs
# 1: Lettuce CA NA 22 51 79 22 79 50.66667 152 1
# 2: Beetroot FR 61 8 NA 10 8 61 26.33333 79 1
# 3: Spinach FR 40 NA NA 49 40 49 44.50000 89 2
# 4: Kale CA 54 5 16 NA 5 54 25.00000 75 1
# 5: Carrot CA NA NA NA NA Inf -Inf NaN 0 4
dstats <- function(x){
c(mean(x,na.rm=TRUE),
min(x, na.rm=TRUE),
max(x, na.rm=TRUE),
sum(x, na.rm=TRUE))
}
(dt[,
c("AVG", "MIN", "MAX", "SUM") := data.frame(t(apply(.SD, 1, dstats))),
.SDcols=c("Q1", "Q2","Q3","Q4"),
])
dt[,`:=`(MIN = apply(dt[, Q1:Q4], 1, FUN = min, na.rm=TRUE),
MAX = apply(dt[, Q1:Q4], 1, FUN = max, na.rm = TRUE),
AVG = rowMeans(dt[, Q1:Q4], na.rm = TRUE),
SUM = rowSums(dt[, Q1:Q4], na.rm = TRUE))][]
# ProductName Country Q1 Q2 Q3 Q4 MIN MAX AVG SUM
# 1: Lettuce CA NA 22 51 79 22 79 50.66667 152
# 2: Beetroot FR 61 8 NA 10 8 61 26.33333 79
# 3: Spinach FR 40 NA NA 49 40 49 44.50000 89
# 4: Kale CA 54 5 16 NA 5 54 25.00000 75
# 5: Carrot CA NA NA NA NA Inf -Inf NaN 0
dt1 <- dt[,`:=`(MIN = rowMins(as.matrix(dt[, Q1:Q4]), na.rm=TRUE),
MAX = rowMaxs(as.matrix(dt[, Q1:Q4]), na.rm = TRUE),
AVG = rowMeans(dt[, Q1:Q4], na.rm = TRUE),
SUM = rowSums(dt[, Q1:Q4], na.rm = TRUE))][]
# ProductName Country Q1 Q2 Q3 Q4 MIN MAX AVG SUM
# 1: Lettuce CA NA 22 51 79 22 79 50.66667 152
# 2: Beetroot FR 61 8 NA 10 8 61 26.33333 79
# 3: Spinach FR 40 NA NA 49 40 49 44.50000 89
# 4: Kale CA 54 5 16 NA 5 54 25.00000 75
# 5: Carrot CA NA NA NA NA Inf -Inf NaN 0