【问题标题】:Select row by level of a factor按因子的级别选择行
【发布时间】:2015-08-25 17:37:03
【问题描述】:

我有一个数据框 df2,其中包含按 ID factor 分组的观察结果,我想对其进行子集化。我使用了另一个函数来确定我想要选择的每个因子组中的哪些行。这在下面的df 中显示:

df <- data.frame(ID = c("A","B","C"),
                 pos = c(1,3,2))
df2 <- data.frame(ID = c(rep("A",5), rep("B",5), rep("C",5)),
                  obs = c(1:15))

df 中,pos 对应于我要在ID 中提到的因子级别内选择的行的索引,而不是在整个数据框df2 中。我正在寻找一种方法根据正确的索引为每个ID选择行(因此它们的行号在df2的每个因素的水平内)。

所以,在这个例子中,我想用ID == 'A'选择df2中的第一个值,用ID == 'B'选择df2中的第三个值,用ID == 'C'选择df2中的第二个值。

这会给我:

df3 <- data.frame(ID = c("A", "B", "C"),
                  obs = c(1, 8, 12))

【问题讨论】:

    标签: r subset r-factor


    【解决方案1】:

    dplyr

    library(dplyr)
    
    merge(df,df2) %>% 
      group_by(ID) %>% 
      filter(row_number() == pos) %>%
      select(-pos)
    
    #   ID obs
    # 1  A   1
    # 2  B   8
    # 3  C  12
    

    基础 R

    df2m <- merge(df,df2)
    do.call(rbind, 
      by(df2m, df2m$ID, function(SD) SD[SD$pos[1], setdiff(names(SD),"pos")]) 
    )
    

    by将合并后的数据框df2m除以df2m$ID,并对各个部分进行操作;它以列表的形式返回结果,因此它们最后必须是rbinded。每个数据子集(与ID 的每个值相关联)都由pos 过滤,并使用普通的data.frame 语法取消选择"pos" 列。

    data.table@DavidArenburg 在评论中建议

    library(data.table)
    
    setkey(setDT(df2),"ID")[df][, 
      .SD[pos[1L], !"pos", with=FALSE]
    , by = ID]
    

    第一部分 -- setkey(setDT(df2),"ID")[df] -- 是合并。之后,结果表被拆分by = ID,并对每个数据子集.SD进行操作。 pos[1L] 以正常方式进行子集化,而!"pos", with=FALSE 对应于删除pos 列。

    查看@eddi 的答案以获得更好的 data.table 方法。

    【讨论】:

    • 也可能是library(data.table) ; setkey(setDT(df2), "ID")[df][, .SD[pos[1L]], by = ID] 或类似的东西。
    • 您的解决方案似乎是最优雅的......但我无法安装 dplyr。我知道这不关你的事,但我还是想在这里说出来,所以你不会认为我一直在忽略上面的帖子。
    • 在我的整个数据库上运行 dplyr 解决方案,我收到以下错误消息:错误:'names' 属性 [26127] 必须与向量 [49] 的长度相同。基础 R 也不起作用:[.default(xj, i) 中的错误:无效的下标类型“列表”。或 data.table:setDT 中的错误(datatestE,“IDPAT”):参数 'giveNames' 到 'setDT' 必须是逻辑 TRUE/FALSE。我知道这与我的数据结构有关。
    • @user2092517 好的,这很有趣,但我真的不知道如何解决它。如果其他答案中的基本 R 解决方案有效,我会说继续。
    • 本着 dplyr 的精神,我们可以使用 dplyr inner_join,并推迟合并 -- df2 %&gt;% group_by(ID) %&gt;% mutate(pos=row_number()) %&gt;% inner_join(df) %&gt;% select(-pos)
    【解决方案2】:

    这是基本的 R 解决方案:

    df2$pos <- ave(df2$obs, df2$ID, FUN=seq_along)
    merge(df, df2)
      ID pos obs
    1  A   1   1
    2  B   3   8
    3  C   2  12
    

    如果df2 是按ID 排序的,你可以在第一行使用df2$pos &lt;- sequence(table(df2$ID))

    【讨论】:

    • 好主意。不过,您可以使用df2$pos &lt;- sequence(lengths(split(df2$ID,df2$ID))) 然后只使用merge(df,df2) 而不是制作中间数据集,我认为这种方式并不比 dplyr 丑多少。
    • 这个currently edited 的答案假定df2 中的条目按ID 预先排序。
    • @A.Webb 似乎是一个合理的假设,但我会放回原始版本(在我编辑之前),涵盖您关心的情况。
    • @Frank 如果您愿意,可以发布您自己的版本,并附上假设,但我不会编辑另一个答案的代码,尤其是。给定额外的假设。
    • @A.Webb 呃,现在有点晚了,但这是个好建议;谢谢。我在这里对其进行了编辑,因为它似乎遵循相同的想法(对我来说,因为它是关于在合并之前创建一个 pos 列)。
    【解决方案3】:

    使用data.table 1.9.5+ 版本:

    setDT(df2)[df, .SD[pos], by = .EACHI, on = 'ID']
    

    ID 列上合并,然后为df 的每一行选择pos 行。

    【讨论】:

    • 或者只是 setkey(setDT(df2), "ID")[df, .SD[pos], by = .EACHI] 没有开发版
    猜你喜欢
    • 2018-02-25
    • 2015-10-13
    • 2014-02-11
    • 2022-06-15
    • 1970-01-01
    • 2016-09-30
    • 1970-01-01
    • 2012-06-23
    • 2012-09-12
    相关资源
    最近更新 更多