【问题标题】:Collapsing data frame by selecting one row per group通过选择每组一行来折叠数据框
【发布时间】:2010-04-13 02:17:18
【问题描述】:

我正在尝试通过从特定列中具有相同值的每组行中删除除一行之外的所有行来折叠数据框。换句话说,每个组的第一行。

例如,我想转换这个

> d = data.frame(x=c(1,1,2,4),y=c(10,11,12,13),z=c(20,19,18,17))
> d
  x  y  z
1 1 10 20
2 1 11 19
3 2 12 18
4 4 13 17

进入这个:

    x  y  z
1   1 11 19
2   2 12 18
3   4 13 17

我目前正在使用聚合来执行此操作,但性能无法接受更多数据:

> d.ordered = d[order(-d$y),]
> aggregate(d.ordered,by=list(key=d.ordered$x),FUN=function(x){x[1]})

我尝试使用与此处相同的函数参数进行拆分/取消拆分,但未拆分抱怨重复的行号。

rle 有可能吗?是否有一个 R 成语将 rle 的长度向量转换为开始每次运行的行的索引,然后我可以使用它从数据框中提取这些行?

【问题讨论】:

    标签: r dataframe


    【解决方案1】:

    也许duplicated() 可以帮忙:

    R> d[ !duplicated(d$x), ]
      x  y  z
    1 1 10 20
    3 2 12 18
    4 4 13 17
    R> 
    

    编辑嘘,没关系。这会在每个重复块中选择第一个,你想要最后一个。所以这里是另一个使用plyr的尝试:

    R> ddply(d, "x", function(z) tail(z,1))
      x  y  z
    1 1 11 19
    2 2 12 18
    3 4 13 17
    R> 
    

    plyr 在这里完成了寻找唯一子集、循环它们并应用提供的函数的艰苦工作——它只是使用 tail(z, 1) 返回块 z 中的最后一组观察结果。

    【讨论】:

    • 那么你需要简单地添加一个“处理步骤”来创建一个因子变量,plyr 可以在该变量上循环。这一切都可以通过索引命令来完成,试一试。顺便说一句,您的文本(说选择了第一行)和示例(显示第二行)之间不一致。
    • 顺便说一句,r-help 和here 之间的交叉发帖也有点糟糕。你在 r-help 上得到了很好的答案,那你为什么不研究它们呢?
    • 我的荣幸。作为 StackOverflow 上常见的最佳实践,您应该接受一个帖子作为解决方案(如果您认为它提供了一个),并通过单击向上箭头为每个有用的帖子投票。这就是评分的运作方式。
    【解决方案2】:

    只是对 Dirk 提供的内容添加一点...duplicated 有一个 fromLast 参数,您可以使用它来选择最后一行:

    d[ !duplicated(d$x,fromLast=TRUE), ]
    

    【讨论】:

    • 嗨,Ian -- 不幸的是,James 从来没有真正明确说明他是想要第一个还是最后一个,并且在帖子中自相矛盾......但是你对 fromLast 的暗示是一个很好的提示!
    • 谢谢,这就像一个魅力。我需要的是第一个还是最后一个,这完全取决于排序,使用 fromLast 我可以任意攻击它
    • 我提出了同样的建议,但你以“更喜欢所有列”为由拒绝了它。为什么这不再重要?
    • 抱歉,Dirk,我当时误解了重复的工作原理
    【解决方案3】:

    这是一个data.table 解决方案,对于大型数据集而言,该解决方案将节省时间和内存

    library(data.table)
    DT <- as.data.table(d)           # convert to data.table
    setkey(DT, x)                    # set key to allow binary search using `J()`
    DT[J(unique(x)), mult ='last']   # subset out the last row for each x
    DT[J(unique(x)), mult ='first']  # if you wanted the first row for each x
    

    【讨论】:

    • 但是如果需要的 all 是每个组中的最后一行,那么DT[!duplicated(x,fromLast=TRUE)] 可能比setkey + join 的总时间快,并且有一些避免DT 的变量名重复的语法糖优势(即只是x 而不是DT$x)。
    • 使用行索引会加快速度,我猜,DT[ DT[, .I[.N] , by = x]$V1]。检查stackoverflow.com/questions/19424762/…。感谢@Simono101
    • unique(DT,by="x",fromLast=TRUE) 现在比 DT[!duplicated(x,fromLast=TRUE)]DT[J(unique(x)), mult ='last'] 更简单、更快捷
    【解决方案4】:

    有几个使用dplyr的选项:

    library(dplyr)
    df %>% distinct(x, .keep_all = TRUE)
    df %>% group_by(x) %>% filter(row_number() == 1)
    df %>% group_by(x) %>% slice(1)
    

    distinct()group_by() 可以使用多个列:

    df %>% distinct(x, y, .keep_all = TRUE)
    

    如果有日期或其他一些顺序字段并且 您想确保保留最新的观察结果,如果您想避免平局,slice() 很有用:

    df %>% group_by(x) %>% filter(date == max(date)) %>% slice(1)
    

    【讨论】:

    猜你喜欢
    • 2017-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 2013-12-13
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多