首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在列子集(.SDcols)上应用函数,而对另一列(组内)应用不同的函数

在列子集(.SDcols)上应用函数,而对另一列(组内)应用不同的函数
EN

Stack Overflow用户
提问于 2013-12-08 21:44:52
回答 2查看 35K关注 0票数 38

这非常类似于将公共函数应用于data.table uning .SDcols answered thoroughly here的多列的问题。

不同之处在于,我希望在另一列上同时应用不同的函数,该列不是.SD子集的一部分。我在下面发布了一个简单的例子,展示了我解决这个问题的尝试:

代码语言:javascript
运行
复制
dt = data.table(grp = sample(letters[1:3],100, replace = TRUE),
                v1 = rnorm(100), 
                v2 = rnorm(100), 
                v3 = rnorm(100))
sd.cols = c("v2", "v3")
dt.out = dt[, list(v1 = sum(v1),  lapply(.SD,mean)), by = grp, .SDcols = sd.cols]

产生以下错误:

代码语言:javascript
运行
复制
Error in `[.data.table`(dt, , list(v1 = sum(v1), lapply(.SD, mean)), by = grp,  
: object 'v1' not found

现在,这是有意义的,因为v1列不包括在必须首先计算的列子集中。因此,我进一步探讨了如何将它包含在专栏的子集中:

代码语言:javascript
运行
复制
sd.cols = c("v1","v2", "v3")
dt.out = dt[, list(sum(v1), lapply(.SD,mean)), by = grp, .SDcols = sd.cols]

现在,这不会导致错误,但它提供了一个包含9行(3个组)的答案,在V1列中重复了三次和,并将所有3列(如预期但不需要)的平均值放置在V2中,如下所示:

代码语言:javascript
运行
复制
> dt.out 
   grp        V1                  V2
1:   c -1.070608 -0.0486639841313638
2:   c -1.070608  -0.178154270921521
3:   c -1.070608  -0.137625003604012
4:   b -2.782252 -0.0794929150464099
5:   b -2.782252  -0.149529237116445
6:   b -2.782252   0.199925178109264
7:   a  6.091355   0.141659419355985
8:   a  6.091355 -0.0272192037753071
9:   a  6.091355 0.00815760216214876

使用两个步骤的解决方案

显然,通过对列的子集按组计算mean并按组将其加入到单个列的sum中,可以通过多个步骤解决这个问题,如下所示:

代码语言:javascript
运行
复制
dt.out1 = dt[, sum(v1), by = grp]
dt.out2 = dt[, lapply(.SD,mean), by = grp, .SDcols = sd.cols]
dt.out = merge(dt.out1, dt.out2, by = "grp")

> dt.out
   grp        V1         v2           v3
1:   a  6.091355 -0.0272192  0.008157602
2:   b -2.782252 -0.1495292  0.199925178
3:   c -1.070608 -0.1781543 -0.137625004

我相信这是一个相当简单的事情,我错过了,谢谢您的任何指导。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-12-08 23:10:01

更新:问题#495现在已经用this recent commit解决了,我们现在可以很好地做到这一点:

代码语言:javascript
运行
复制
require(data.table) # v1.9.7+
set.seed(1L)
dt = data.table(grp = sample(letters[1:3],100, replace = TRUE),
                v1 = rnorm(100), 
                v2 = rnorm(100), 
                v3 = rnorm(100))
sd.cols = c("v2", "v3")
dt.out = dt[, list(v1 = sum(v1),  lapply(.SD,mean)), by = grp, .SDcols = sd.cols]

但是,请注意,在本例中,v2将作为列表返回。这是因为您正在有效地执行list(val, list())。也许你打算做的是:

代码语言:javascript
运行
复制
dt[, c(list(v1=sum(v1)), lapply(.SD, mean)), by=grp, .SDcols = sd.cols]
#    grp        v1          v2         v3
# 1:   a -6.440273  0.16993940  0.2173324
# 2:   b  4.304350 -0.02553813  0.3381612
# 3:   c  0.377974 -0.03828672 -0.2489067

更古老的答案请参阅历史。

票数 33
EN

Stack Overflow用户

发布于 2013-12-08 23:03:57

试试这个:

代码语言:javascript
运行
复制
dt[,list(sum(v1), mean(v2), mean(v3)), by=grp]

data.table中,在第二个参数中使用list()可以描述导致最终data.table的一组列。

就其价值而言,.SD可能非常慢^1,因此您可能希望避免它,除非您确实需要在子设置的data.table中提供的所有数据,就像您可能需要更复杂的函数那样。

如果.SDcols有许多列,则另一个选项是使用data.table合并语法在一行中进行合并。

例如:

代码语言:javascript
运行
复制
dt[, sum(v1), by=grp][dt[,lapply(.SD,mean), by=grp, .SDcols=sd.cols]]

为了使用来自mergedata.table,您需要首先在data.table上使用setkey(),以便它知道如何匹配。

所以,首先,你需要:

代码语言:javascript
运行
复制
setkey(dt, grp)

然后,您可以使用上面的行产生一个等效的结果。

^1:当您的组数接近总行数时,我发现这是特别正确的。例如,如果您的密钥是单个ID,而且许多个人只有一两个观察,则可能会发生这种情况。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20459519

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档