【问题标题】:R- Aggregate rows according to several values, but apply sum function only to some columnsR-根据几个值聚合行,但仅对某些列应用求和函数
【发布时间】:2021-06-28 15:05:23
【问题描述】:

我知道有一个类似的问题 (r aggregate dataframe: some columns unchanged, some columns aggregated),但它不能满足我的需要。

我有一个数据框,其中包含不同物种的观察结果,但许多观察结果来自同一点(相同的经度和纬度)。

df=data.frame(sampleID=c("a","b","c","d","e","f"),latitude=c(46.4,46.4,73.2,36.6,36.6,55.3), longitud=c(66.4,66.4,34.6,46.4,46.4,50.7), material=c("soil","soil","water","water","water","water"), biome=c("forest", "forest", "sea","sea","sea","lake"), sp1=c(2,1,0,4,0,0),sp2=c(0,2,3,1,0,1), sp3=c(0,1,1,4,3,0))

df
  sampleID latitude longitud material  biome sp1 sp2 sp3
1        a     46.4     66.4     soil forest   2   0   0
2        b     46.4     66.4     soil forest   1   2   1
3        c     73.2     34.6    water    sea   0   3   1
4        d     36.6     46.4    water    sea   4   1   4
5        e     36.6     46.4    water    sea   0   0   3
6        f     55.3     50.7    water   lake   0   1   0



我想对来自相同位置和相同类型材料的观察结果(列 sp1、sp2 和 sp3,在提供的示例中)求和,但其他列保持不变。在“生物群系”的情况下,它应该是相同的值,所以就一样,在样本 ID 的情况下,保留一个 ID。比如:

  sampleID latitude longitud material  biome sp1 sp2 sp3
1        a     46.4     66.4     soil forest   3   2   1
3        c     73.2     34.6    water    sea   0   3   1
4        d     36.6     46.4    water    sea   4   1   7
6        f     55.3     50.7    water   lake   0   1   0

我尝试过使用聚合、ddply 以及 data.table 和 lapply。但我不知道如何将 sum 函数仅应用于一组列(6:8),因此它失败了。聚合给了我一个错误,ddply 删除了它无法求和的其他列(ID 和生物群系)。并且它根据纬度对观察结果进行排序,因此找出每个 ID 是一团糟。

我尝试过的一些事情:

ddply(df, .(latitude, longitude, material), numcolwise(sum))   #This one deletes the columns which it cannot sum, ID and biome (and many other variables I haven't shown in this example)

aggregate(.~latitude+longitude+material,data=df,sum) #Gives an error
aggregate(cbind(df[,c(6:8)]) ~ as[,c(2,3,4)], data=df, sum) #Gives an error

另外,这是我拥有的数据框的简化版本,我有更多变量和 200 多个物种,每个物种都有其拉丁语的实际名称。我不能全部输入,我需要通过 df[,c(18:220)] 或类似的方式进行选择。

任何帮助将不胜感激。

【问题讨论】:

    标签: r


    【解决方案1】:

    我们可以在基础 R 中做到这一点:

    do.call(rbind, 
            DF |>
              split(DF$longitud, DF$latitude, DF$material) |> 
              lapply(function(x) { 
                cbind(x[1:5][1,], t(apply(x[6:8], 2, sum, na.rm = TRUE)))})) |>
      `rownames<-`(1:4)
    
      sampleID latitude longitud material  biome sp1 sp2 sp3
    1        d     36.6     46.4    water    sea   4   1   7
    2        a     46.4     66.4     soil forest   3   2   1
    3        f     55.3     50.7    water   lake   0   1   0
    4        c     73.2     34.6    water    sea   0   3   1
    

    上面没有原生管道的解决方案:

    DF <- do.call(rbind, 
                  lapply(split(df, df$longitud, df$latitude, df$material), 
                         function(x) { 
                           cbind(x[1:5][1,], t(apply(x[6:8], 2, sum, na.rm = TRUE)))})) 
    
    `rownames<-`(DF, seq_len(nrow(DF)))
    
      sampleID latitude longitud material  biome sp1 sp2 sp3
    1        c     73.2     34.6    water    sea   0   3   1
    2        d     36.6     46.4    water    sea   4   1   7
    3        f     55.3     50.7    water   lake   0   1   0
    4        a     46.4     66.4     soil forest   3   2   1
    

    我们也可以使用aggregate

    aggregate(DF[startsWith(names(DF), "sp")], 
              list(latitude = DF$latitude, 
                   longitude = DF$longitud, 
                   material = DF$material), 
              sum, na.rm = TRUE)
    
      latitude longitude material sp1 sp2 sp3
    1     46.4      66.4     soil   3   2   1
    2     73.2      34.6    water   0   3   1
    3     36.6      46.4    water   4   1   7
    4     55.3      50.7    water   0   1   0
    

    或者,如果您想使用公式,我们将 cbind 用于数值变量,因为它们不止一个:

    aggregate(cbind(DF$sp1, DF$sp2, DF$sp3) ~ longitud + latitude + material, 
              data = DF, sum)
    
      longitud latitude material V1 V2 V3
    1     66.4     46.4     soil  3  2  1
    2     46.4     36.6    water  4  1  7
    3     50.7     55.3    water  0  1  0
    4     34.6     73.2    water  0  3  1
    

    如果你愿意使用包dplyr,它会让工作变得更容易:

    library(dplyr)
    
    df %>%
      group_by(latitude, longitud, material) %>%
      mutate(across(starts_with("sp"), ~ sum(.x))) %>%
      slice_head(n = 1) %>%
      arrange(sampleID)
    
    # A tibble: 4 x 8
    # Groups:   latitude, longitud, material [4]
      sampleID latitude longitud material biome    sp1   sp2   sp3
      <chr>       <dbl>    <dbl> <chr>    <chr>  <dbl> <dbl> <dbl>
    1 a            46.4     66.4 soil     forest     3     2     1
    2 c            73.2     34.6 water    sea        0     3     1
    3 d            36.6     46.4 water    sea        4     1     7
    4 f            55.3     50.7 water    lake       0     1     0
    

    【讨论】:

    • 感谢您的回答。聚合选项删除了我需要的 ID 列。这与 ddply 的结果相同。第一个应该是我需要的,但是即使使用此处提供的示例,我也会遇到一些错误。首先,“意外的'>'”。删除“>”(我不知道它们的用途,我不熟悉这种语法)后,我得到一个“Ops.data.frame 中的错误(DF,split(DF$longitud , DF$latitude, DF$material)) : 长度为 4 的列表没有意义”
    • 嗨,不客气。如果我们想保留sample ID,它必须要么在分组变量中,要么在我们正在应用函数的变量中。第一个听起来很合理,但我不明白您选择a 而不是bd 而不是e 的基础是什么,因为它们是相同的materiallat/lon。所以在第一个解决方案中,我只选择了第一个。
    • |&gt; 是原生 R 管道运算符。我们用它来转发上一个操作的结果,并将其作为下一个操作的第一个参数。您无法将其删除,因为代码会分崩离析,但我可以进行编辑。顺便说一句,意外的令牌错误不会阻止代码执行,只是您的 Rstudio 版本无法识别它并像我一样抛出警告信号。
    • 太好了,谢谢@Anoushivaran R!修改后的版本适用于示例数据。我不介意它保留哪个 ID,a 或 b,只要它保留一个。我的数据仍然存在一些错误,希望能够修复它们。最后的 1:4 是因为最后你有 4 行?你怎么提前知道(我原来的df有1000多)?另外,关于“x[1:5][1,]”,我猜 [1:5] 是我不想求和的列,[1,] 是什么?
    • 完美,谢谢,它也适用于我的数据 =D。我认为这给了我一个错误,因为在 200 列之一中,数字前有一个空格,它读作字符 -.- 非常感谢!
    猜你喜欢
    • 2021-12-28
    • 1970-01-01
    • 2023-01-26
    • 1970-01-01
    • 2021-03-27
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多