【问题标题】:R dplyr to data table - Group and FilterR dplyr 到数据表 - 分组和过滤
【发布时间】:2016-01-01 16:38:43
【问题描述】:

出于速度原因,我正在尝试将我的数据操作代码从 dplyr 转换为 data.table。我快到了,但错过了最后一步。

我有一些示例数据来复制我的问题。

c_dt = data.table(u_id=rep(c("u1", "u2"),each=5),
                  p_id=c("p1", "p1", "p1", "p2","p2", "p1", "p2", "p2", "p2", "p2" ), 
                  c_dt=c("2015-12-01", "2015-12-02", "2015-12-03", "2015-12-02",
                         "2015-12-05", "2015-12-02", "2015-12-03", "2015-12-04", 
                         "2015-12-05", "2015-12-06"))

我希望确定u_idp_id 重复的行;并只保留最小c_dt 的行(基本上保留第一个实例)。我为此使用以下dplyr 代码:

c_df <- as.data.frame(c_dt)
cdedup_df <- c_df %>% group_by(p_id, u_id) %>% filter(c_dt == min(c_dt))

给出以下输出

> cdedup_df
Source: local data frame [4 x 3]
Groups: p_id, u_id

  u_id p_id       c_dt
1   u1   p1 2015-12-01
2   u1   p2 2015-12-02
3   u2   p1 2015-12-02
4   u2   p2 2015-12-03

我有以下data.table 代码可以正确识别所需的行,但我无法弄清楚如何过滤和行。

cdedup_dt <- c_dt[,c_dt == min(c_dt),by = list(u_id, p_id)]
cdedup_dt
    u_id p_id    V1
 1:   u1   p1  TRUE
 2:   u1   p1 FALSE
 3:   u1   p1 FALSE
 4:   u1   p2  TRUE
 5:   u1   p2 FALSE
 6:   u2   p1  TRUE
 7:   u2   p2  TRUE
 8:   u2   p2 FALSE
 9:   u2   p2 FALSE
10:   u2   p2 FALSE

【问题讨论】:

    标签: r data.table dplyr


    【解决方案1】:

    低于我的方法。我希望它对于大数据集可以更好地扩展,因为group 没有min,只有单一排序,data.table 非常有效,然后首先按组进行子集。

    setorderv(c_dt, "c_dt")[, .SD[1L], .(u_id, p_id)]
    # in data.table 1.9.7+ you can also use `head`
    setorderv(c_dt, "c_dt")[, head(.SD, 1L), .(u_id, p_id)]
    

    以下代码包括对当前其他答案的验证。
    如果 OP 将提供大数据集,我可以添加基准。

    library(data.table)
    c_dt = data.table(u_id=rep(c("u1", "u2"),each=5), p_id=c("p1", "p1", "p1", "p2","p2", "p1", "p2", "p2", "p2", "p2" ), c_dt=c("2015-12-01", "2015-12-02", "2015-12-03", "2015-12-02", "2015-12-05", "2015-12-02", "2015-12-03", "2015-12-04", "2015-12-05", "2015-12-06"))
    
    zero = c_dt[, list(c_dt=min(c_dt)), by=list(u_id, p_id)]
    ananda = c_dt[, list(c_dt = c_dt[c_dt == min(c_dt)]), by = .(u_id, p_id)]
    tal = c_dt[, .SD[rank(c_dt, ties.method = c("first")) == 1],by = .(u_id, p_id)]
    all.equal(zero, ananda)
    #[1] TRUE
    all.equal(ananda, tal)
    #[1] TRUE
    
    jan = setorderv(c_dt, "c_dt")[, .SD[1L], .(u_id, p_id)]
    all.equal(tal, jan)
    #[1] TRUE
    

    【讨论】:

    • 我在我的机器上执行的一个快速而肮脏的基准测试表明@zero323 方法是最有效的。您能否简要解释一下您的命令中实际发生的情况?是某种连接操作吗?
    • @TalJ.Levy 是的,setorderv 对数据集进行排序。您正在测试的音量是多少?
    • 我使用了 9000 个不同的 u_idp_idc_dt 值。数据集大小为 20,000,000 行(这当然意味着我添加了重复项)。
    【解决方案2】:

    这样的事情应该可以解决问题:

    c_dt[, list(c_dt=min(c_dt)), by=list(u_id, p_id)]
    ##    u_id p_id       c_dt
    ## 1:   u1   p1 2015-12-01
    ## 2:   u1   p2 2015-12-02
    ## 3:   u2   p1 2015-12-02
    ## 4:   u2   p2 2015-12-03
    

    【讨论】:

    • 或者c_dt[, list(c_dt = c_dt[c_dt == min(c_dt)]), by = .(u_id, p_id)]
    • @AnandaMahto 我不确定。问题描述(基本上保留第一个实例)与示例代码不匹配。
    • @zero323,你能解释一下在 j 列中放置一个列表是什么意思(它为每对唯一的 u_id 和 p_id 保存 c_dt 的最小值) ?
    • 一般dt[, list(v)](或dt[, .(v)])表示返回类型是data.table。 dt[,v] 返回一个向量。当您使用它时,它不再明显。 – zero323 2 小时前
    【解决方案3】:

    所以你确实非常接近。您所缺少的只是在 j 列中传递.SD。让我们看看它是如何工作的:

    library(data.table)
    c_dt = data.table(u_id=rep(c("u1", "u2"),each=5),  
                      p_id=c("p1", "p1", "p1", "p2","p2", "p1", "p2", "p2", "p2", "p2" ), 
                      c_dt=c("2015-12-01", "2015-12-02",  
                       "2015-12-03", "2015-12-02", "2015-12-05", 
                       "2015-12-02", "2015-12-03", "2015-12-04", 
                       "2015-12-05", "2015-12-06"))
    c_dt
        u_id p_id       c_dt
     1:   u1   p1 2015-12-01
     2:   u1   p1 2015-12-02
     3:   u1   p1 2015-12-03
     4:   u1   p2 2015-12-02
     5:   u1   p2 2015-12-05
     6:   u2   p1 2015-12-02
     7:   u2   p2 2015-12-03
     8:   u2   p2 2015-12-04
     9:   u2   p2 2015-12-05
    10:   u2   p2 2015-12-06
    

    现在我们将 by u_idp_id 分组,并按 c_df 的最小值进行过滤:

    cdedup_dt <- c_dt[ , .SD[c_dt == min(c_dt)], by = .(u_id, p_id)]
    cdedup_dt
       u_id p_id       c_dt
    1:   u1   p1 2015-12-01
    2:   u1   p2 2015-12-02
    3:   u2   p1 2015-12-02
    4:   u2   p2 2015-12-03
    

    请注意,.(u_id, p_id) 等于 list(u_id, p_id).SD 指的是每个组的 Data.table 的子集。你所缺少的只是.SD
    正如@zero323 所提到的, min 将保留重复项(这基本上意味着我们的示例中有一些重复的行)。如果您只想为每个组保留一个记录,则更安全的选择是使用排名功能:

    cdedup_dt <- c_dt[, .SD[rank(c_dt, ties.method = c("first")) == 1],by = .(u_id, p_id)]
    
    cdedup_dt
       u_id p_id       c_dt
    1:   u1   p1 2015-12-01
    2:   u1   p2 2015-12-02
    3:   u2   p1 2015-12-02
    4:   u2   p2 2015-12-03
    

    【讨论】:

      猜你喜欢
      • 2018-06-02
      • 1970-01-01
      • 1970-01-01
      • 2021-12-28
      • 1970-01-01
      • 2021-10-25
      • 2021-02-27
      • 2016-06-28
      • 1970-01-01
      相关资源
      最近更新 更多