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

时间:2017-10-18 18:28:59

标签: r data.table grouping data-manipulation

我有一个数据框,描述动物的顺序移动(ID列)和在那里花费的时间(startend列)。这些运动以小尺度记录,但被分类在更大的区域(classification列)中,使得动物可以在一个区域内移动多次,然后移动到另一个区域并移动。他们也可以一直呆在一个地区,或者根本不会移动。

sequent_moves列中跟踪每个区域内的移动顺序(有关如何创建这些内容的详细说明,请参阅this question)。动物可能会移回他们之前离开的地区。还有一列化学数据,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()等......?
  3. 编辑:根据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列,然后说“对于每一个其他列,给我第一个元素”,它会更紧凑。

1 个答案:

答案 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 = ...中的分组变量。这样可以节省一些输入,但会将列放在其他(聚合)列的前面。