【问题标题】:Select rows based on whether value of a columns is in top X of columns根据列的值是否在列的前 X 中选择行
【发布时间】:2019-03-12 23:23:19
【问题描述】:

我有一个数据框,我想根据某列中的值是否在该行中所有列的前 5 个值中来对其进行子集化。 这是我的数据框的简化版本:

> my.df <- data.frame(a = rnorm(10,5), b= rnorm(10,5), c=rnorm(10,5), d=rnorm(10,5), e=rnorm(10,5))
> my.df
          a        b        c        d        e
1  6.401462 5.318849 5.373496 5.101140 3.710973
2  6.715845 4.786936 3.521965 4.264029 4.525138
3  6.076211 5.356114 5.605134 5.443002 5.296778
4  7.009623 5.275595 4.801874 4.355892 6.752737
5  5.002059 6.163398 6.063694 2.409702 6.172111
6  6.298305 3.291884 5.737053 4.701320 4.752406
7  4.856246 4.674743 5.550828 7.501786 5.466611
8  5.037990 4.129333 4.797334 5.143915 5.558161
9  4.903592 3.135622 5.879798 5.639893 4.368915
10 5.500374 4.400130 3.980433 6.203259 4.498614

现在我只想要 a 列或 b 列的值在其行中的前 2 个值中的行。所以在这个例子中删除第 7-9 行,给出:

          a        b        c        d        e
1  6.401462 5.318849 5.373496 5.101140 3.710973
2  6.715845 4.786936 3.521965 4.264029 4.525138
3  6.076211 5.356114 5.605134 5.443002 5.296778
4  7.009623 5.275595 4.801874 4.355892 6.752737
5  5.002059 6.163398 6.063694 2.409702 6.172111
6  6.298305 3.291884 5.737053 4.701320 4.752406
10 5.500374 4.400130 3.980433 6.203259 4.498614

有什么想法吗?

【问题讨论】:

  • 是的,最初我在没有set.seed 的情况下测试数据。必须将其更改为decreasing = TRUE

标签: r subset


【解决方案1】:

我们可以循环使用apply(来自base R)的行检查'a'或'b'中元素的any是否是%in%sorted组以创建逻辑索引并以此为基础对行进行子集化

i1 <- apply(my.df, 1, function(x) any(x[1:2] %in% sort(x, decreasing = TRUE)[1:2]))
my.df[i1,]
#         a        b        c        d        e
#1  6.401462 5.318849 5.373496 5.101140 3.710973
#2  6.715845 4.786936 3.521965 4.264029 4.525138
#3  6.076211 5.356114 5.605134 5.443002 5.296778
#4  7.009623 5.275595 4.801874 4.355892 6.752737
#5  5.002059 6.163398 6.063694 2.409702 6.172111
#6  6.298305 3.291884 5.737053 4.701320 4.752406
#10 5.500374 4.400130 3.980433 6.203259 4.498614

或者使用base R中的max.col来创建逻辑索引,这样会更快并且避免任何转换

i1 <- max.col(my.df, "first")
i2 <- max.col(replace(my.df, cbind(seq_len(nrow(my.df)), i1), -Inf), "first")
my.df[(i1 %in% 1:2) | (i2 %in% 1:2), ]

数据

my.df <- structure(list(a = c(6.401462, 6.715845, 6.076211, 7.009623, 
5.002059, 6.298305, 4.856246, 5.03799, 4.903592, 5.500374), b = c(5.318849, 
4.786936, 5.356114, 5.275595, 6.163398, 3.291884, 4.674743, 4.129333, 
3.135622, 4.40013), c = c(5.373496, 3.521965, 5.605134, 4.801874, 
6.063694, 5.737053, 5.550828, 4.797334, 5.879798, 3.980433), 
    d = c(5.10114, 4.264029, 5.443002, 4.355892, 2.409702, 4.70132, 
    7.501786, 5.143915, 5.639893, 6.203259), e = c(3.710973, 
    4.525138, 5.296778, 6.752737, 6.172111, 4.752406, 5.466611, 
    5.558161, 4.368915, 4.498614)), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6", "7", "8", "9", "10"))

【讨论】:

  • 谢谢!这似乎可行,但是是否可以根据列名而不是第 1 列和第 2 列来指定列?
  • @joffie 是的,您可以将 x[1:2] 替换为 x[c("a", "b")]
  • @akrun 你的max.col 建议看起来有点错误,逻辑上也是
  • @akrun 我猜你的意思是+1 在第二行的末尾而不是-1,但无论哪种方式,删除第一列都不会删除最大值,所以你不一定会找到像这样的第二大值
【解决方案2】:

tidyverse 的可能性。我们使用row_number 创建row_index 并将数据帧转换为长格式(gather),按row 我们filter 具有前两个值“a”或“b”的组并将数据转换回宽格式(spread)。

library(tidyverse)

my.df %>%
   mutate(row = row_number()) %>%
   gather(key, value, -row) %>%
   group_by(row) %>%
   filter(any(key[order(value, decreasing = TRUE)[1:2]] %in% c("a", "b"))) %>%
   spread(key, value)

#    row     a     b     c     d     e
#  <int> <dbl> <dbl> <dbl> <dbl> <dbl>
#1     1  6.40  5.32  5.37  5.10  3.71
#2     2  6.72  4.79  3.52  4.26  4.53
#3     3  6.08  5.36  5.61  5.44  5.30
#4     4  7.01  5.28  4.80  4.36  6.75
#5     5  5.00  6.16  6.06  2.41  6.17
#6     6  6.30  3.29  5.74  4.70  4.75
#7    10  5.50  4.40  3.98  6.20  4.50

我特意保留了row 列,以便我们知道第 7-9 行已被删除,如果不需要,我们可以使用 ungroup() %&gt;% select(-row) 将其删除。

【讨论】:

    【解决方案3】:

    我附上一个基于data.table 的解决方案:

    library(data.table)
    my.df <- data.frame(a = rnorm(10,5), b= rnorm(10,5), c=rnorm(10,5), d=rnorm(10,5), e=rnorm(10,5))
    my.df <- as.data.table(my.df)
    my.df[my.df[,(sum(a<=.SD)<=2) | (sum(b<=.SD)<=2), by=seq_len(nrow(my.df))]$V1]
    

    【讨论】:

      【解决方案4】:

      遍历所有行并检查每一行是否12ordered 值的前两个索引中:

      my.df <- data.frame(a = rnorm(10,5), b= rnorm(10,5), c=rnorm(10,5), d=rnorm(10,5), e=rnorm(10,5))
      my.df <- my.df[sapply(1:nrow(my.df), function(x){any(c(1, 2) %in% order(my.df[x, ], decreasing = TRUE)[1:2])}),]
      

      示例数据:

      set.seed(1)
      my.df <- data.frame(a = rnorm(10,5), b= rnorm(10,5), c=rnorm(10,5), d=rnorm(10,5), e=rnorm(10,5))
      my.df
      
                a        b        c        d        e
      1  4.373546 6.511781 5.918977 6.358680 4.835476
      2  5.183643 5.389843 5.782136 4.897212 4.746638
      3  4.164371 4.378759 5.074565 5.387672 5.696963
      4  6.595281 2.785300 3.010648 4.946195 5.556663
      5  5.329508 6.124931 5.619826 3.622940 4.311244
      6  4.179532 4.955066 4.943871 4.585005 4.292505
      7  5.487429 4.983810 4.844204 4.605710 5.364582
      8  5.738325 5.943836 3.529248 4.940687 5.768533
      9  5.575781 5.821221 4.521850 6.100025 4.887654
      10 4.694612 5.593901 5.417942 5.763176 5.881108
      

      应删除行 310

      my.df <- my.df[sapply(1:nrow(my.df), function(x){any(c(1, 2) %in% order(my.df[x, ], decreasing = TRUE)[1:2])}),]
      my.df
      
               a        b        c        d        e
      1 4.373546 6.511781 5.918977 6.358680 4.835476
      2 5.183643 5.389843 5.782136 4.897212 4.746638
      4 6.595281 2.785300 3.010648 4.946195 5.556663
      5 5.329508 6.124931 5.619826 3.622940 4.311244
      6 4.179532 4.955066 4.943871 4.585005 4.292505
      7 5.487429 4.983810 4.844204 4.605710 5.364582
      8 5.738325 5.943836 3.529248 4.940687 5.768533
      9 5.575781 5.821221 4.521850 6.100025 4.887654
      

      【讨论】:

        【解决方案5】:

        最终我选择了以下解决方案。您添加 2 列,告诉列 a 和 b 的值在哪个位置。然后根据这些值进行子集化。

        my.df$a.num <- as.numeric("") 
        my.df$b.num <- as.numeric("") 
        
        
        for (i in 1:nrow(my.df)){
          index <- sort(my.df[i,], decreasing = TRUE)
          a.num <- which(colnames(index) == "a")
          b.num <- which(colnames(index) == "b")
          my.df$a.num[i] <- LA
          my.df$b.num[i] <- LV}
        
        my.df <- my.df[my.df$a.num < 3 | my.df$b.num < 3, ]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-25
          • 1970-01-01
          • 2018-01-20
          • 1970-01-01
          • 2022-06-21
          相关资源
          最近更新 更多