【问题标题】:Filter R dataframe rows by matching value in consecutive columns通过匹配连续列中的值过滤 R 数据帧行
【发布时间】:2021-12-29 18:54:01
【问题描述】:

我有一个包含真 (1) 和假 (0) 信息的大型数据框。下面是它的简化示例。

df <- read.table(text = "  0m-10m 0m-15m 0m-20m 0m-25m
                 X 1 0 1 1 
                 Y 1 1 0 0 
                 Z 0 1 0 1 ", header = T)

我需要对两个连续对比为真(都 == 1)的行进行子集化,以便结果如下所示

   0m-10m 0m-15m 0m-20m 0m-25m
X  1 0 1 1
Y  1 1 0 0 

我可以按 rowSums >=2 进行过滤,但对于我需要排除的第 Z 行也是如此。想法?

【问题讨论】:

    标签: r subset


    【解决方案1】:

    只需删除第一列和最后一列,使用&amp; 创建一个逻辑矩阵,然后使用rowSums 创建子集的逻辑向量

    df[rowSums(df[-1] & df[-ncol(df)]) > 0,]
    

    -输出

      0m-10m 0m-15m 0m-20m 0m-25m
    X      1      0      1      1
    Y      1      1      0      0
    

    上面的rowSums不是基于单一数据的。我们通过删除第一列和最后一列然后使用&amp;,从两个大小相等的数据集创建一个逻辑矩阵,因此,如果两者在同一位置都有1,则只有它会返回TRUE,否则返回FALSE。此矩阵上的rowSums 返回 TRUE(或 TRUE/FALSE -> 1/0)的总和

    > df[-1] & df[-ncol(df)]
      0m-15m 0m-20m 0m-25m
    X  FALSE  FALSE   TRUE
    Y   TRUE  FALSE  FALSE
    Z  FALSE  FALSE  FALSE
    > rowSums(df[-1] & df[-ncol(df)])
    X Y Z 
    1 1 0 
    > rowSums(df[-1] & df[-ncol(df)]) > 0
        X     Y     Z 
     TRUE  TRUE FALSE 
    

    或者,如果我们正在寻找一般情况,我们可以使用rle - 通过循环使用applyMARGIN = 1 对每一行进行运行长度编码。 rle 为每个相邻的相似值返回 listvalueslengths。然后,我们根据lengthsvalues 创建一个逻辑向量,即如果“values”为 1,“lengths”为 2。

    n <- 2
    df[apply(df, 1, FUN = function(x) with(rle(x), any(lengths == n & values))),]
      0m-10m 0m-15m 0m-20m 0m-25m
    X      1      0      1      1
    Y      1      1      0      0
    

    -代码分解

    > apply(df, 1, FUN = rle)
    $X
    Run Length Encoding
      lengths: Named int [1:3] 1 1 2
     - attr(*, "names")= chr [1:3] "0m-15m" "0m-20m" ""
      values : Named int [1:3] 1 0 1
     - attr(*, "names")= chr [1:3] "0m-10m" "0m-15m" "0m-25m"
    
    $Y
    Run Length Encoding
      lengths: Named int [1:2] 2 2
     - attr(*, "names")= chr [1:2] "0m-20m" ""
      values : Named int [1:2] 1 0
     - attr(*, "names")= chr [1:2] "0m-15m" "0m-25m"
    
    $Z
    Run Length Encoding
      lengths: Named int [1:4] 1 1 1 1
     - attr(*, "names")= chr [1:4] "0m-15m" "0m-20m" "0m-25m" ""
      values : Named int [1:4] 0 1 0 1
     - attr(*, "names")= chr [1:4] "0m-10m" "0m-15m" "0m-20m" "0m-25m"
    > apply(df, 1, FUN = function(x) with(rle(x),lengths == n & values))
    $X
    0m-15m 0m-20m        
     FALSE  FALSE   TRUE 
    
    $Y
    0m-20m        
      TRUE  FALSE 
    
    $Z
    0m-15m 0m-20m 0m-25m        
     FALSE  FALSE  FALSE  FALSE 
    > apply(df, 1, FUN = function(x) with(rle(x), any(lengths == n & values)))
        X     Y     Z 
     TRUE  TRUE FALSE 
    

    【讨论】:

    • 我或许应该澄清一下,这是一个有 12,939 行和 8 列的数据框的小例子。我认为第一个解决方案还不够,因为 rowSums 并没有削减它。在您的第二个解决方案中,“价值观”指的是什么?
    • @KalaDowney 有 10 行还是 12000 行都没有关系。第一个解决方案不是基于行。它每 2 个备用列检查 1
    • 啊!抱歉,我不清楚。
    • @KalaDowney 我添加了步骤以便更好地理解。希望对你有帮助
    • 这对您很有帮助,谢谢!没有任何 NA,我们在早期省略了这些。
    【解决方案2】:

    这里是另一种使用 pivot 的方法:

    library(dplyr)
    library(tidyr)
    
    df %>% 
      rownames_to_column("xyz") %>% 
      pivot_longer(
        -xyz
      ) %>% 
      group_by(xyz) %>% 
      mutate(helper = lag(value),
             flag = ifelse(value==1 & helper==1, 1,0)) %>% 
      filter(any(flag==1)) %>% 
      pivot_wider(
        names_from = name,
        values_from = value,
        values_fill = 0
      ) %>% 
      summarize(across(starts_with("X"), sum)) %>% 
      column_to_rownames("xyz")
    
    
      X0m.10m X0m.15m X0m.20m X0m.25m
    X       1       0       1       1
    Y       1       1       0       0
    

    【讨论】:

      【解决方案3】:

      基于创建辅助列的解决方案,将所有原始列连接为字符串(使用tidyr::unite),然后在字符串上使用stringr::str_detect

      library(tidyverse)
      
      df <- read.table(text = "  0m-10m 0m-15m 0m-20m 0m-25m
                       X 1 0 1 1 
                       Y 1 1 0 0 
                       Z 0 1 0 1 ", header = T)
      
      df %>% 
        unite(aux, sep = "", remove = F) %>% 
        filter(str_detect(aux, "11")) %>%
        select(-aux)
      
      #>   X0m.10m X0m.15m X0m.20m X0m.25m
      #> X       1       0       1       1
      #> Y       1       1       0       0
      

      【讨论】:

        猜你喜欢
        • 2011-08-27
        • 1970-01-01
        • 2021-12-28
        • 1970-01-01
        • 2022-01-18
        • 1970-01-01
        • 2021-07-08
        • 2021-11-19
        • 1970-01-01
        相关资源
        最近更新 更多