通过匹配连续列中的值过滤 R 数据帧行

Posted

技术标签:

【中文标题】通过匹配连续列中的值过滤 R 数据帧行【英文标题】:Filter R dataframe rows by matching value in consecutive columns 【发布时间】: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 行也是如此。想法?

【问题讨论】:

【参考方案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

【讨论】:

以上是关于通过匹配连续列中的值过滤 R 数据帧行的主要内容,如果未能解决你的问题,请参考以下文章

用R中不同数据帧中具有相同ID的行中的值替换列中的NA

根据 R 中列中的值过滤数据框

R:仅当同一列中的两行中的值为真时才将值添加到 [row,column]

使用 Filter 或 If Else 根据 R 中另一列中的值过滤指标

通过多列中的值有效地在R中闪烁过滤数据帧

然后匹配两列中的值,然后基于R中返回的新值