按具有多个条件的因素折叠数据框
Collapsing a data frame by factors with multiple criteria
我有一个数据框,它描述了动物的连续运动(ID
列)和在那里花费的时间(start
和 end
列)。这些运动是在小尺度上记录的,但被分类在更大的区域内(classification
列),这样动物可以在一个区域内移动多次,然后再移动到另一个区域并四处移动。他们也可以一直呆在一个区域,或者根本不动。
每个区域内的移动顺序在 sequent_moves
列中进行跟踪(有关如何创建这些内容的更详尽的解释,请参阅 )。动物有可能回到它们之前离开的区域。还有一列化学数据,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
在吗,
- 更有效的方法?
- 一种更简单或更紧凑的方式来指定
我想要 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 = ...
中,每个组内所有不变或常量的变量都被视为分组变量。这节省了一些输入,但将列放在其他(聚合)列的前面。
我有一个数据框,它描述了动物的连续运动(ID
列)和在那里花费的时间(start
和 end
列)。这些运动是在小尺度上记录的,但被分类在更大的区域内(classification
列),这样动物可以在一个区域内移动多次,然后再移动到另一个区域并四处移动。他们也可以一直呆在一个区域,或者根本不动。
每个区域内的移动顺序在 sequent_moves
列中进行跟踪(有关如何创建这些内容的更详尽的解释,请参阅 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
在吗,
- 更有效的方法?
- 一种更简单或更紧凑的方式来指定 我想要 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 = ...
中,每个组内所有不变或常量的变量都被视为分组变量。这节省了一些输入,但将列放在其他(聚合)列的前面。