按具有多个条件的因素折叠数据框

Collapsing a data frame by factors with multiple criteria

我有一个数据框,它描述了动物的连续运动(ID 列)和在那里花费的时间(startend 列)。这些运动是在小尺度上记录的,但被分类在更大的区域内(classification 列),这样动物可以在一个区域内移动多次,然后再移动到另一个区域并四处移动。他们也可以一直呆在一个区域,或者根本不动。

每个区域内的移动顺序在 sequent_moves 列中进行跟踪(有关如何创建这些内容的更详尽的解释,请参阅 )。动物有可能回到它们之前离开的区域。还有一列化学数据,Mean_8786Sr与那个地区有关。

我想折叠这个数据框,这样我就只得到了对区域运动的描述。因此,通过 Samplesequent_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

在吗,

  1. 更有效的方法?
  2. 一种更简单或更紧凑的方式来指定 我想要 max()、min() 等的列...?

编辑:根据 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讲了很多,还没完全消化。有什么想法吗?

如果能够将最小值、最大值和平均值传递到我可以用 colnames 填充的列表,那就太好了。绝大多数列我只想要第一个元素。如果有一种方法可以直接指定最大、最小和平均列,然后说等同于“对于其他每一列,给我第一个元素”,那将会更加紧凑。

OP 询问是否有比单独指定每一列更有效的聚合 movement 数据框的方法。

恐怕免不了要指定哪些列需要通过哪个聚合函数进行聚合。但是,data.table 语法通常非常紧凑。因此,可以使用 data.table 实现对 by() 的调用,如下所示:

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 = ... 中,每个组内所有不变或常量的变量都被视为分组变量。这节省了一些输入,但将列放在其他(聚合)列的前面。