按其他列因子聚合列[重复]

Posted

技术标签:

【中文标题】按其他列因子聚合列[重复]【英文标题】:Aggregate columns by other column factor [duplicate] 【发布时间】:2021-01-30 02:18:18 【问题描述】:

我有一些数据,其中包含每个样本中的单个物种及其数量。每个物种也按其营养模式标记。它看起来像这样:

OTU_ID Trophic.Mode  Sample1 Sample2 Sample3 Sample4
 gatca   Symbiotroph   4         5      6      1
 atca    Pathotroph    4         3      4      4
 gatac   Pathotroph    7         1      2      1

以此类推,它有 2700 行,带有标记营养模式的物种计数。 我想聚合数据,以便每个样本都有一个更简单的功能社区,按 Trophic.Mode 列(它有 5 个因素,所以我期待一个有 5 行的数据框),所以我最终得到这个:

Trophic.Mode  Sample1 Sample2 Sample3 Sample4
  Symbiotroph   4         5      6      1
  Pathotroph    13        18     15     11

因此我使用了以下代码:

agg = aggregate(data,
            by = list(data$Trophic.Mode),
            FUN = sum)

这只是返回一个错误

"Error in Summary.factor(c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L,  : 
  ‘sum’ not meaningful for factors

但我不确定我是否理解 - 我想按这些因素对其他列求和。

【问题讨论】:

它试图对 data 参数中的所有列求和。您需要告诉 aggregate 它不需要对 OTU_IDTrophic.Mode 列求和 - 这是因素。将 data 更改为 data[-(1:2)] 以省略前两列。 (编辑:需要删除前 2 列) 谢谢,Gregor 成功了,我有点尴尬,我没有意识到这一点 “我有点尴尬,我没有意识到这一点” 别这样。这就是为什么这个网站上有成千上万的 R 问题。 【参考方案1】:

我更喜欢聚合的公式语法,因为公式右侧的分组列将自动从摘要中省略:

aggregate(. ~ Trophic.Mode, data = data[-1], FUN = sum)

by 的非公式接口在您想按数据中不是列的内容进行分组时非常有用。

【讨论】:

【参考方案2】:

我想按这些因素对其他列求和。

这就是rowsum 的用途:

rowsum(data[, -(1:2)], data$Trophic.Mode)
#R>             Sample1 Sample2 Sample3 Sample4
#R> Pathotroph       11       4       6       5
#R> Symbiotroph       4       5       6       1

这在击键和计算时间方面都很难被击败:

# simulate your ~2700 row data set
set.seed(1)
n <- 2700L
sim_dat <- data.frame(
  Trophic.Mode = sample.int(5, n, replace = TRUE), 
  sample       = matrix(sample.int(10, n * 4L, replace = TRUE), n))
colnames(sim_dat)[-1] <- paste0("sample", 1:4)
head(sim_dat, 3)
#R>   Trophic.Mode sample1 sample2 sample3 sample4
#R> 1            1       9       6       9       6
#R> 2            4       1       6       3       1
#R> 3            1       9       9      10       9

# check that we get the same
r1 <- aggregate(. ~ Trophic.Mode, data = sim_dat, FUN = sum)
r2 <- rowsum(sim_dat[, -1], sim_dat$Trophic.Mode)
all.equal(r1[, -1], r2, check.attributes = FALSE)
#R> [1] TRUE

library(tidyverse)
r3 <- sim_dat %>% 
  group_by(Trophic.Mode) %>%
  summarise_all(sum) %>%
  ungroup()
all.equal(r3[, -1], r2, check.attributes = FALSE)
#R> [1] TRUE

# check the computation time
bench::mark(
  aggregate = aggregate(. ~ Trophic.Mode, data = sim_dat, FUN = sum), 
  rowsum    = rowsum(sim_dat[, -1], sim_dat$Trophic.Mode), 
  tidy      = sim_dat %>% 
    group_by(Trophic.Mode) %>%
    summarise_all(sum) %>%
    ungroup(),
  min_time = 2, check = FALSE)
#R> # A tibble: 3 x 13
#R>   expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time 
#R>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> 
#R> 1 aggregate    2.28ms   2.45ms      396.    1.37MB    17.4    612    27      1.55s 
#R> 2 rowsum     110.93µs  129.8µs     7449.   53.23KB    11.2   9985    15      1.34s 
#R> 3 tidy         3.49ms   4.63ms      209.   93.41KB     6.56   383    12      1.83s

这比其他方法快了近 20 倍,但几毫秒的开始不算什么......

更新

根据要求,这是带有n &lt;- 500000L(500k 行)和不带ungroup 的基准:

bench::mark(
  aggregate = aggregate(. ~ Trophic.Mode, data = sim_dat, FUN = sum), 
  rowsum    = rowsum(sim_dat[, -1], sim_dat$Trophic.Mode), 
  tidy      = sim_dat %>% 
    group_by(Trophic.Mode) %>%
    summarise_all(sum),
  min_time = 2, check = FALSE)
#R> # A tibble: 3 x 13
#R>   expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time 
#R>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> 
#R> 1 aggregate   203.5ms  264.5ms      3.64  234.56MB     25.5     8    56      2.19s 
#R> 2 rowsum         11ms   11.2ms     84.8     7.82MB     14.0   170    28         2s 
#R> 3 tidy         12.8ms   13.4ms     64.8    17.36MB     25.9   130    52         2s

对于n &lt;- 10000000L (10M):

bench::mark(
  aggregate = aggregate(. ~ Trophic.Mode, data = sim_dat, FUN = sum), 
  rowsum    = rowsum(sim_dat[, -1], sim_dat$Trophic.Mode), 
  tidy      = sim_dat %>% 
    group_by(Trophic.Mode) %>%
    summarise_all(sum),
  min_time = 30, check = FALSE)
#R>   expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time 
#R>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> 
#R> 1 aggregate     5.82s    5.92s     0.168    5.05GB    2.18      6    78      35.8s 
#R> 2 rowsum     275.46ms 285.37ms     3.41   204.29MB    0.796   103    24      30.2s 
#R> 3 tidy       233.38ms 264.47ms     3.66   331.03MB    2.03    110    61      30.1s

事实证明,对于较大的数据集而言,差异很小。

【讨论】:

如果您要运行基准测试,请在足够重要的数据版本上进行 - 我建议至少 500k 行。如果您要进行基准测试(如果性能很重要),您也应该输入data.table 我也会摆脱ungroup() - 总结自动取消分组。 OP 状态有 2700 行。但是,我将在更新版本中以 500k 运行它。 是的,2700 行的 20 倍加速是... 2.32 毫秒。 OP 需要运行代码数十万次,以节省阅读此答案和更改代码所需的计算时间。除非 OP 正在使用高度调整的生产系统,否则性能差异在 2700 行时无关紧要。但是,如果数据在性能可能很重要的地方变得足够大,那么排名和相对性能可能会发生变化。 我喜欢rowsum 解决方案——它是解决这个问题最好的(虽然最不通用)base 方法。我的观点是对于这种大小的数据,所有建议的方法都很快 - 最慢的方法只需要几分之一秒。新的 R 用户应该专注于理解方法和代码的可读性,而不是过早的优化。即使在 500k 行中,差异也会变得很明显。【参考方案3】:
library(tidyverse)

df %>% 
select(-OTU_ID) %>%
group_by(Trophic.Mode) %>%
summarise_all(sum) %>%
ungroup()

我的首选是tidyverse

【讨论】:

为了帮助 OP 入门,而不是广泛推荐 tidyverse,我将 OP 直接指向 dplyr - 这就是这里所需要的。 (summarise_all 即将退出,summarise(across(everything(), sum)) 是现代方式。这很好,因为我们可以切换到 summarise(across(where(is.numeric), sum)),而不是手动 select(-OTU_ID)【参考方案4】:

也许试试这个选项。这是一个base R 选项。正如 @GregorThomas 在 cmets 中所说,您必须告诉函数必须考虑哪些变量。在这种情况下,在公式中我们避免使用OTU_ID。代码如下:

#Option 1
Out1 <- aggregate(cbind(Sample1,Sample2,Sample3,Sample4)~Trophic.Mode,data=df,sum,na.rm=T)

输出:

  Trophic.Mode Sample1 Sample2 Sample3 Sample4
1   Pathotroph      11       4       6       5
2  Symbiotroph       4       5       6       1

使用的一些数据:

#Data
df <- structure(list(OTU_ID = c("gatca", "atca", "gatac"), Trophic.Mode = c("Symbiotroph", 
"Pathotroph", "Pathotroph"), Sample1 = c(4L, 4L, 7L), Sample2 = c(5L, 
3L, 1L), Sample3 = c(6L, 4L, 2L), Sample4 = c(1L, 4L, 1L)), class = "data.frame", row.names = c(NA, 
-3L))

【讨论】:

【参考方案5】:

试试这个

library(dplyr)
read_table("TU_ID  Trophic.Mode  Sample1 Sample2 Sample3 Sample4
  gatca   Symbiotroph   4         5      6      1
  atca    Pathotroph    4         3      4      4
  gatac   Pathotroph    7         1      2      1") %>% 
   group_by(Trophic.Mode) %>% 
   summarise(across(where(is.numeric), sum))

`summarise()` ungrouping output (override with `.groups` argument)
# A tibble: 2 x 5
  Trophic.Mode Sample1 Sample2 Sample3 Sample4
  <chr>          <dbl>   <dbl>   <dbl>   <dbl>
1 Pathotroph        11       4       6       5
2 Symbiotroph        4       5       6       1

【讨论】:

【参考方案6】:

考虑aggregate 的公式版本,可以说它更具可读性,因为您可以明确地看到数字和分组列。但您可能仍需要过滤公式列。

还要注意aggregate 公式与非公式方法在处理缺失值方面可能不同。见aggregate methods treat missing values (NA) differently。 @Rorschach 的fix 应用在下面。

# EXPLICIT NUMERIC COLUMNS
agg <- aggregate(cbind(Sample1, Sample2, Sample3, Sample4) ~ Trophic.Mode,
                 data = data,
                 FUN = sum,
                 na.rm = TRUE, 
                 na.action = na.pass)

# IMPLICIT "ALL" NUMERIC COLUMNS
agg <- aggregate(. ~ Trophic.Mode,
                 data = data[,grep("Trophic|Sample", names(data))],
                 na.rm = TRUE, 
                 na.action = na.pass)

【讨论】:

以上是关于按其他列因子聚合列[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Pandas数据框:按一列分组,但由其他列连接和聚合[重复]

执行聚合函数时如何检索其他列?

使用其他(不同的)过滤器聚合列

如何通过使用数据框中的其他列来聚合熊猫数据框中的列

将平均 2 列聚合为每行的一列 [重复]

根据其他列 SQL 的最大值更新列 [重复]