通过匹配连续列中的值过滤 R 数据帧行
Filter R dataframe rows by matching value in consecutive columns
我有一个包含真 (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 行也是如此。想法?
只需删除第一列和最后一列,使用 &
创建逻辑矩阵,然后使用 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
不是基于单一数据。我们通过删除第一列和最后一列然后使用 &
从两个大小相等的数据集创建一个逻辑矩阵,因此,如果两者在同一位置都有 1,则只有它会 return TRUE
否则为 FALSE。 rowSums
在这个矩阵上 returns 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
或者如果我们正在寻找一般情况,我们可以通过使用 apply
和 MARGIN = 1
。对于每个相邻的相似值,rle
return 是 values
和 lengths
的 list
。然后,我们根据 lengths
和 values
创建一个逻辑向量,即如果 '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
这是使用数据透视表的另一种方法:
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
基于创建辅助列将所有原始列连接为字符串(使用 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
我有一个包含真 (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 行也是如此。想法?
只需删除第一列和最后一列,使用 &
创建逻辑矩阵,然后使用 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
不是基于单一数据。我们通过删除第一列和最后一列然后使用 &
从两个大小相等的数据集创建一个逻辑矩阵,因此,如果两者在同一位置都有 1,则只有它会 return TRUE
否则为 FALSE。 rowSums
在这个矩阵上 returns 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
或者如果我们正在寻找一般情况,我们可以通过使用 apply
和 MARGIN = 1
。对于每个相邻的相似值,rle
return 是 values
和 lengths
的 list
。然后,我们根据 lengths
和 values
创建一个逻辑向量,即如果 '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
这是使用数据透视表的另一种方法:
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
基于创建辅助列将所有原始列连接为字符串(使用 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