我有一个数据框,描述动物的顺序移动(ID
列)和在那里花费的时间(start
和end
列)。这些运动以小尺度记录,但被分类在更大的区域(classification
列)中,使得动物可以在一个区域内移动多次,然后移动到另一个区域并移动。他们也可以一直呆在一个地区,或者根本不会移动。
在sequent_moves
列中跟踪每个区域内的移动顺序(有关如何创建这些内容的详细说明,请参阅this question)。动物可能会移回他们之前离开的地区。还有一列化学数据,Mean_8786Sr
与该地区有关。
我想要折叠这个数据框,这样我最终只能描述区域运动。因此,按Sample
和sequent_moves
进行子集化我希望保留最小start
值和最大end
值,最后得出区域内的开始和结束时间。我还想要Mean_8786Sr
中化学数据的平均值。我想要保留最小值或因子值的其余列,如下面的示例代码所示。
我可以使用by()
执行此操作,但到目前为止,它需要每列的语句。我的实际数据有更多的列和数千行。我很确定有一种更快,更优雅的方式来做这件事,也许是使用data.table(因为我喜欢到目前为止我从该软件包中看到的内容)。
以下是我的结果。有没有更有效的方法来做到这一点?
movement = data.frame(structure(list(start = c(0, 0, 110, 126, 235, 0, 17, 139, 251,
0, 35, 47, 99, 219, 232, 269, 386, 398, 414, 443, 459), end = c(782L,
110L, 126L, 235L, 612L, 17L, 139L, 251L, 493L, 35L, 47L, 99L,
219L, 232L, 269L, 386L, 398L, 414L, 443L, 459L, 765L), Mean_8786Sr = c(0.709269349163555,
0.710120935400909, 0.70934948311875, 0.71042744033211, 0.709296068424668,
0.708621911917647, 0.709358583256557, 0.710189508916071, 0.709257758963636,
0.711148891471429, 0.712470115258333, 0.713742475130769, 0.714572498375,
0.713400790353846, 0.711656338391892, 0.710380629097436, 0.711571667241667,
0.71290867871875, 0.712009033513793, 0.71104293234375, 0.709344687326471
), Sample = c("2006_3174", "2006_3185", "2006_3185", "2006_3185",
"2006_3185", "2006_3189", "2006_3189", "2006_3189", "2006_3189",
"2006_3194", "2006_3194", "2006_3194", "2006_3194", "2006_3194",
"2006_3194", "2006_3194", "2006_3194", "2006_3194", "2006_3194",
"2006_3194", "2006_3194"), ID = c("1", "1", "2", "3", "4", "1",
"2", "3", "4", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
"11", "12"), return_year = c(2006L, 2006L, 2006L, 2006L, 2006L,
2006L, 2006L, 2006L, 2006L, 2006L, 2006L, 2006L, 2006L, 2006L,
2006L, 2006L, 2006L, 2006L, 2006L, 2006L, 2006L), classification = c("CW",
"CW", "SK", "CW", "CW", "SK", "SK", "CW", "CW", "CW", "CW", "CW",
"CW", "CW", "CW", "CW", "CW", "CW", "CW", "CW", "CW"), sequent_moves = c(1L,
1L, 2L, 3L, 3L, 1L, 1L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L, 1L), Sample_cptID = c("2006_3174 1", "2006_3185 1",
"2006_3185 2", "2006_3185 3", "2006_3185 3", "2006_3189 1", "2006_3189 1",
"2006_3189 2", "2006_3189 2", "2006_3194 1", "2006_3194 1", "2006_3194 1",
"2006_3194 1", "2006_3194 1", "2006_3194 1", "2006_3194 1", "2006_3194 1",
"2006_3194 1", "2006_3194 1", "2006_3194 1", "2006_3194 1")), .Names = c("start",
"end", "Mean_8786Sr", "Sample", "ID", "return_year", "classification",
"sequent_moves", "Sample_cptID"), class = "data.frame", row.names = 6:26))
这是我使用by()的解决方案:
moves = by(movement_dput, INDICES = c(factor(movement_dput$Sample_cptID)), function (x) {
start = min(x[,"start"])
end = max(x[,"end"])
Mean_8786Sr = mean(x[,"Mean_8786Sr"])
Sample = x[1,"Sample"]
ID = min(x[,"ID"])
return_year = x[1,"return_year"]
classification = x[1,"classification"]
sequent_moves = x[1,"sequent_moves"]
move = cbind(start, end, Mean_8786Sr, Sample, ID, return_year, classification, sequent_moves)
move
}
)
regional_moves = do.call(rbind.data.frame, moves)
regional_moves
有吗,
编辑:根据Jeannie的评论添加部分data.table解决方案。
这是我到目前为止使用data.table的内容。
require('data.table')
m=setDT(movement)
m[, .(start=base::min(start),
end=base::max(end),
Mean_8786Sr=mean(Mean_8786Sr),
ID = base::min(ID),
return_year = return_year[1],
classification = classification[1],
Sample_cptID = Sample_cptID[1])
, by=c('Sample', 'sequent_moves')]
如果我在没有base::min()
的情况下运行此操作,我会收到错误消息。目前的错误是:
Error in `g[`(Sample_cptID, 1) : object 'Sample_cptID' not found
在之前的迭代中(没有用)我得到了:
Error in gmin(ID) :
GForce min can only be applied to columns, not .SD or similar. To find min of all items in a list such as .SD, either add the prefix base::min(.SD) or turn off GForce optimization using options(datatable.optimize=1). More likely, you may be looking for 'DT[,lapply(.SD,min),by=,.SDcols=]'
使用基本min()
和max()
功能运行它可以正常运行。我试图了解GForce在优化速度方面做了多少工作,我认为这与它为什么没有返回我期望的功能有关。 This thread谈论它,但我还没有彻底消化它。有什么想法吗?
能够将min,max和mean传递给我可以用colnames填充的列表会很高兴。绝大多数列我只想要第一个元素。如果有一种方法可以直接指定max,min和mean列,然后说“对于每一个其他列,给我第一个元素”,它会更紧凑。
答案 0 :(得分:1)
OP询问是否有更有效的方法来聚合movement
数据框,而不是单独指定每列。
我担心指定哪些列需要通过哪个聚合函数进行聚合是不可避免的。但是,data.table
语法通常非常紧凑。因此,by()
的调用可以使用data.table
实现,如下所示:
library(data.table)
setDT(movement)[
, .(start = min(start), end = max(end), Mean_8786Sr = mean(Mean_8786Sr), ID = min(ID)),
by = .(Sample, return_year, classification, sequent_moves)]
Sample return_year classification sequent_moves start end Mean_8786Sr ID 1: 2006_3174 2006 CW 1 0 782 0.7092693 1 2: 2006_3185 2006 CW 1 0 110 0.7101209 1 3: 2006_3185 2006 SK 2 110 126 0.7093495 2 4: 2006_3185 2006 CW 3 126 612 0.7098618 3 5: 2006_3189 2006 SK 1 0 139 0.7089902 1 6: 2006_3189 2006 CW 2 139 493 0.7097236 3 7: 2006_3194 2006 CW 1 0 765 0.7120207 1
请注意,每个组中不变或不变的所有变量都被视为by = ...
中的分组变量。这样可以节省一些输入,但会将列放在其他(聚合)列的前面。