【问题标题】:R combining duplicate rows by ID with different column types in a dataframeR按ID组合重复行与数据框中的不同列类型
【发布时间】:2020-08-31 00:51:10
【问题描述】:

我有一个以列 ID 作为标识符的数据框和其他一些不同类型的列(因子和数字)。看起来是这样的

df <- data.frame(id    = c(1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4),
                 abst  = c(0, NA, 2, NA, NA, NA, 0, 0, NA, 2, NA, 3, 4),
                 farbe = as.factor(c("keine", NA, "keine", NA, NA, NA, "keine", "keine", NA, NA, NA, "rot", "rot")),
                 gier  = c(0, NA, 5, NA, NA, NA, 0, 0, NA, 1, NA, 6, 2))

现在我想合并重复的 ID。数字列定义为所有相同 ID 的平均值(没有 NA!)。因子列合并为一个。 NA 可以省略。

最终结果应该是这样的

dfRes <- data.frame(id    = c(1, 2, 3, 4),
                    abst  = c(1, 0, 0, 3),
                    farbe = as.factor(c("keine", "keine", "keine", "rot")),
                    gier  = c(2.5, 0, 0, 3))

我希望有一种方法可以快速进行计算,因为我有大约一百万个观察值。 提前致谢!

编辑(添加):“farbe”可能不是唯一的。在这种情况下,我认为对我的数据最好的想法是有一个重复的行,但只有一个不同的“farbe”,所以有 2 个相同的 ID 和“farbe”的所有相同但不同的值。这应该只是非常罕见的情况,但却是一个很好的补充。

我的真实数据中有更多的数值列和因子列。是否也可以创建一个解决方案,所以我不必定义每一列?

【问题讨论】:

  • 对于相同的id,farbe 是否总是相同的?
  • 好问题,我希望如此。否则问题不大。但是必须做出决定,应该使用哪个值。
  • 我认为到目前为止发布的所有解决方案都假定是唯一的farbe(至少我的是这样)。如果不是,你是唯一一个知道如何处理不同价值观的人。之后我们可以修改答案。
  • 你是对的。在这种情况下,我认为对我的数据最好的想法是有一个重复的行,但只有一个不同的“farbe”。这应该只是非常罕见的情况,没关系。

标签: r dataframe data.table aggregate tidyverse


【解决方案1】:

一个 dplyr 解决方案。

library(dplyr)

df %>% 
  group_by(id) %>% 
  summarise(abst = mean(na.omit(abst)), 
            farbe = na.omit(farbe)[1],
            gier = mean(na.omit(gier)))
#> # A tibble: 4 x 4
#>      id  abst farbe  gier
#>   <dbl> <dbl> <fct> <dbl>
#> 1     1     1 keine   2.5
#> 2     2     0 keine   0  
#> 3     3     0 keine   0  
#> 4     4     3 rot     3

由reprex package (v0.3.0) 于 2020-05-14 创建

【讨论】:

  • 不错的解决方案。如果我有很多数字和因子列,我该如何实现?我必须定义每一列吗?
  • 我在我的数据上尝试了这个解决方案,大约 1 mio obervation。需要10多分钟!这是为什么?我尝试了一个 data.table 解决方案,它花了大约 1 秒。
【解决方案2】:

也是data.table 解决方案:

    library(data.table)
    df <- data.table( # instead of data.frame
                     id    = c(1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4), 
                     abst  = c(0, NA, 2, NA, NA, NA, 0, 0, NA, 2, NA, 3, 4),
                     farbe = as.factor(c("keine", NA, "keine", NA, NA, NA, "keine", "keine", NA, NA, NA, "rot", "rot")),
                     gier  = c(0, NA, 5, NA, NA, NA, 0, 0, NA, 1, NA, 6, 2))


    newdf <- df[,
                .(abst=mean(abst,na.rm=T), # perform mean of abst removing NAs
                  farbe=na.omit(unique(farbe)), # assuming farbe is unique for each ID, extract the unique value after removing the NAs
                  gier=mean(gier,na.rm=T)), # perform mean of gier removing NAs
                by=id] # for each ID

    newdf

       id abst farbe gier
    1:  1    1 keine  2.5
    2:  2    0 keine  0.0
    3:  3    0 keine  0.0
    4:  4    3   rot  3.0

【讨论】:

    【解决方案3】:

    编辑:

    刚刚看到您对非唯一因子列和按类型选择列的编辑。这将起作用,但我会考虑一种更清洁的方法来执行此操作并报告(我相信有一种简单的方法)。如果您想像原始示例一样手动指定列并且您有非唯一因素,只需使用unlist() 和unique(),方式与以下相同。或者,您可以考虑使用paste() 和collapse = "; " 或类似的东西将所有因子水平组合在一行上。如果要更改最终 data.table 的列顺序,请在 data.table 上使用setcolorder()

    setDT(df)
    
    # For selecting columns later
    num_cols <- sapply(df, is.numeric)
    num_cols[names(num_cols) == "id"] <- FALSE
    fac_cols <- sapply(df, is.factor)
    
    df[, lapply(.SD, mean, na.rm = T), by = id, .SDcols = num_cols][
      df[, lapply(.SD, function(i) unlist(unique(i[!is.na(i)]))), by = id, .SDcols = fac_cols], on = "id"]
    
       id abst gier farbe
    1:  1    1  2.5 keine
    2:  2    0  0.0 keine
    3:  3    0  0.0 keine
    4:  4    3  3.0  rot2
    5:  4    3  3.0   rot
    

    工作原理: 它加入数字列摘要

    df[, lapply(.SD, mean, na.rm = TRUE), by = id, .SDcols = num_cols]
    

    带有因子列摘要

    df[, lapply(.SD, function(i) unlist(unique(i[!is.na(i)]))), by = id, .SDcols = fac_cols]
    

    需要修改的数据:

    df <- data.frame(id    = c(1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4),
                     abst  = c(0, NA, 2, NA, NA, NA, 0, 0, NA, 2, NA, 3, 4),
                     farbe = as.factor(c("keine", NA, "keine", NA, NA, NA, "keine", "keine", NA, NA, "rot2", "rot", "rot")),
                     gier  = c(0, NA, 5, NA, NA, NA, 0, 0, NA, 1, NA, 6, 2))
    

    原答案:

    这是众多data.table 解决方案之一。这会按因子列对 data.table 进行排序,以便在汇总时获取最高值。我还将它转换回纯 data.frame 但如果您不想这样做,则不必这样做。希望这会有所帮助!

    此外,这假定farbe 对于每个id 都是相同的

    library(data.table)
    
    setDT(df)
    
    df <- df[order(farbe), .(abst = mean(abst, na.rm = TRUE),
                             farbe = farbe[1],
                             gier = mean(gier, na.rm = TRUE)), by = id]
    
    setDF(df)
    df
      id abst farbe gier
    1  1    1 keine  2.5
    2  2    0 keine  0.0
    3  3    0 keine  0.0
    4  4    3   rot  3.0
    

    【讨论】:

    • 为什么这个 data.table 解决方案比 dplyr 解决方案(超过 10 分钟!)快得多(比如 1 秒进行 1 次 mio 观察)?所有数字和因子列都可以这样做吗?我有更多的列,所以不定义每一列会更容易。有没有办法删除NaN? (不是 NA)
    • 嘿@Bolle,希望编辑有所帮助。不是 100% 确定你对 NaN 的意思是什么——你必须发布更多数据或其他问题。我猜这是因为你取的是只有NA 值的组的平均值(即mean(c(NA, NA), na.rm = T))。此外,dplyr 解决方案可能较慢的主要原因可能是因为它在分组后为每一列调用了na.omit()。不过只是猜测。
    • @Andrew 你的新解决方案看起来不错。我测试了一个包含更多列的数据表。它有效,但我收到警告:“第 1 项 j 的组 32133 结果的长度为零。这将填充 2 个 NA 以匹配此结果中最长的列。”这是个大问题吗?这到底是什么意思,我该如何解决?
    • 嘿@Bolle,没有看到数据,我真的不知道。我会通过使用.GRP 添加索引来手动签出该组,然后看看发生了什么。前/后总结。希望这会有所帮助!
    • 我喜欢您的解决方案,很好阅读和理解。如果我告诉你这是一个时间序列,所以 ID 都是不同的,有区别吗?因为您的解决方案效果很好,但速度有点慢。还有一个问题。如果您的数据表中没有因子列,则所有观察结果都将为空。
    【解决方案4】:

    其他解决方案

    df %>% 
      group_by(id) %>% 
      fill(farbe, .direction = "updown") %>% 
      group_by(id, farbe) %>% 
      summarise_all(~ mean(., na.rm = T))
    

    【讨论】:

      猜你喜欢
      • 2020-09-04
      • 2020-01-18
      • 1970-01-01
      • 2016-10-30
      • 1970-01-01
      • 1970-01-01
      • 2020-12-24
      • 1970-01-01
      • 2020-02-15
      相关资源
      最近更新 更多