【问题标题】:Remove rows based on factor-levels根据因子级别删除行
【发布时间】:2012-06-23 14:54:51
【问题描述】:

我有一个格式为“long”的 data.frame df。

df <- data.frame(site = rep(c("A","B","C"), 1, 7),
                 time = c(11,11,11,22,22,22,33),
                 value = ceiling(rnorm(7)*10))
df <- df[order(df$site), ]

df
  site time value
1    A   11    12
2    A   22   -24
3    A   33   -30
4    B   11     3
5    B   22    16
6    C   11     3
7    C   22     9

问题

对于df$site 的每个级别,如何删除不存在df$time 唯一元素的行?

在这种情况下,我想删除 df[3,],因为对于 df$time,时间戳 33 仅存在于站点 A 而不存在于站点 B 和站点 C。

期望的输出:

df.trimmed
  site time value
1    A   11    12
2    A   22   -24
4    B   11     3
5    B   22    16
6    C   11     3
7    C   22     9

data.frame 轻松拥有 800k 行和 200k 唯一时间戳。我不想使用循环,但我不知道在这种情况下如何使用 apply() 或 lapply() 等矢量化函数。

【问题讨论】:

    标签: r data.table subset r-factor


    【解决方案1】:

    这是使用data.table 包的另一种可能的解决方案:

    unTime <- unique(df$time)
    
    library(data.table)
    
    DT <- data.table(df, key = "site")
    
    (notInAll <- unique(DT[, list(ans = which(!unTime %in% time)), by = key(DT)]$ans))
    # [1] 3
    
    DT[time %in% unTime[-notInAll]]
    
    #      site time value
    # [1,]    A   11     3
    # [2,]    A   22    11
    # [3,]    B   11    -6
    # [4,]    B   22    -2
    # [5,]    C   11   -19
    # [6,]    C   22   -14
    

    马修编辑
    好的。或者更直接的方式:

    DT = as.data.table(df)
    tt = DT[,length(unique(site)),by=time]
    tt
       time V1
    1:   11  3
    2:   22  3
    3:   33  1
    
    tt = tt[V1==max(V1)]      # See * below
    tt
       time V1
    1:   11  3
    2:   22  3
    
    DT[time %in% tt$time]
       site time value
    1:    A   11     7
    2:    A   22    -2
    3:    B   11     8
    4:    B   22   -10
    5:    C   11     3
    6:    C   22     1
    

    如果所有站点都没有时间,当最终结果应该为空时(正如 Ben 在 cmets 中指出的那样),上面标记为 * 的步骤可能是:

    tt = tt[V1==length(unique(DT$site))]
    

    【讨论】:

    • 感谢您的建议。该软件包做得非常好,而且速度非常快。 2.711.520 行只需不到 2 秒!
    • @maxmagmilch,最要感谢的是 Matthew Dowle(包的作者和维护者,并且积极参与 SO)!
    • @BenBarnes 嗨,非常感谢。顺便说一句,来自 LondonR 的 presentation 是否找到了你的路。对它如何遇到“冷”感兴趣。
    • @MatthewDowle,感谢您使用更直接的逻辑进行编辑。当所有站点都没有时间时,我刚刚想到的事情将是一个潜在的问题。但这是一个简单的检查。非常感谢您提供的演示文稿链接。我认为这很清楚,我忘记了列表列功能 - 非常方便!也对 1.8.1 中的 := 感到非常兴奋。
    【解决方案2】:

    rle 适合你吗?

    df <- df[order(df$time), ]
    df <- subset(df, time != rle(df$time)$value[rle(df$time)$lengths == 1])
    df <- df[order(df$site), ]
    df
    ##   site time value
    ## 1    A   11    17
    ## 4    A   22    -3
    ## 2    B   11     8
    ## 5    B   22     5
    ## 3    C   11     0
    ## 6    C   22    13
    

    重新查看您的数据,似乎这个解决方案可能对您的需求来说太简单了....

    更新

    这是一种应该比我上面提到的rle 解决方案更好的方法。与其查找运行长度“1”,不如删除与table(df$site, df$time) 结果的某些条件不匹配的行。为了说明,我还添加了一些假数据。

    df <- data.frame(site = rep(c("A","B","C"), 1, 7),
                     time = c(11,11,11,22,22,22,33),
                     value = ceiling(rnorm(7)*10))
    df2 <- data.frame(site = rep(c("A","B","C"), 1, 7),
                     time = c(14,14,15,15,16,16,16),
                     value = ceiling(rnorm(7)*10))
    df <- rbind(df, df2)
    df <- df[order(df$site), ]
    
    temp <- as.numeric(names(which(colSums(with(df, table(site, time)))
                                   >= length(levels(df$site)))))
    df2 <- merge(df, data.frame(temp), by.x = "time", by.y = "temp")
    df2 <- df2[order(df2$site), ]
    df2
    ##   time site value
    ## 3   11    A    -2
    ## 4   16    A    -2
    ## 7   22    A     2
    ## 1   11    B   -16
    ## 5   16    B     3
    ## 8   22    B    -6
    ## 2   11    C     8
    ## 6   16    C    11
    ## 9   22    C   -10
    

    这是对地点/时间组合进行制表和总结的结果:

    colSums(with(df, table(site, time)))
    ## 11 14 15 16 22 33 
    ##  3  2  2  3  3  1 
    

    因此,如果我们有兴趣包含至少有两个站点具有时间戳的站点,我们可以将行 &gt;= length(levels(df$site))(在本例中为 3)更改为 &gt;= length(levels(df$site))-1(显然为 2)。

    不确定此解决方案是否对您有用,但我想我会分享它以展示我们使用 R 提供的解决方案的灵活性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-31
      • 1970-01-01
      • 2017-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多