如何使用 R 总结多个数字和基于文本的条件子集
How to use R summarise with multiple numeric and text-based conditional subsets
我有一个 table 每个 ID 包含两行。
table <- tibble(
id = c(1,1,2,2,3,3,4,4,5,5),
row1 = c(2,5,2,5,1,3,2,5,3,2),
row2 = c("foo", "other foo", "bar", "bar", "bar", "bar other", "other", "foo", "other", "other")
)
> table
# A tibble: 10 × 3
id row1 row2
<dbl> <dbl> <chr>
1 1 2 foo
2 1 5 other foo
3 2 2 bar
4 2 5 bar
5 3 1 bar
6 3 3 bar other
7 4 2 other
8 4 4 foo
9 5 3 other
10 5 2 other
我想根据连续三个规则将 table 解析为每个 ID 的一行:
- 如果对于每个ID,有一行row1大于等于5,则选择row1小于5的行。
- 否则,如果对于每个 ID,有一行 row2 包含单词 'other',请选择 row2 不包含单词 'other'
的行
- 否则,对于每个 ID,选择第一行。
我觉得一定有更直接的方法来做到这一点。到目前为止,这是我的尝试,但我不知道如何将 NA 解析为 return 'bar'.
table %>%
group_by(id) %>%
summarise(
row1 = ifelse(max(row1) >= 5,
first(row1[row1 < 5]),
ifelse(
grep("other", row2),
ifelse(
!is.na(first(row1[grep("other", row2, invert = T)])),
first(row1[grep("other", row2, invert = T)]),
first(row1)),
first(row1))
),
row2 = ifelse(
max(row1) >= 5,
first(row2[row1 < 5]),
ifelse(
grep("other", row2),
ifelse(
!is.na(first(row2[grep("other", row2, invert = T)])),
first(row2[grep("other", row2, invert = T)]),
first(row2)),
first(row2)
)
)
)
# A tibble: 5 × 3
id row1 row2
<dbl> <dbl> <chr>
1 1 2 foo
2 2 2 NA
3 3 1 bar
4 4 2 foo
5 5 3 other
期望的输出:
id
row1
row2
1
2
foo
2
2
bar
3
1
bar
4
2
other
5
3
other
非常感谢您的帮助。
这是一个解决方案,利用这个小函数,f()
使用 tidyverse
或 data.table
f <- function(r1,r2) {
if(sum(r1>=5)==1) return(list("row1" =r1[r1<5], "row2"=r2[r1<5]))
if(sum(grepl("other",r2))==1) return(list("row1" = r1[!grepl("other",r2)], "row2"=r2[!grepl("other",r2)]))
list("row1"=r1[1],"row2"=r2[1])
}
用法
library(tidyverse)
table %>%
group_by(id) %>%
summarize(n=list(f(row1,row2))) %>%
unnest_wider(n)
或
library(data.table)
setDT(table)[, f(row1,row2), by=id]
输出:
id row1 row2
<num> <num> <char>
1: 1 2 foo
2: 2 2 bar
3: 3 1 bar
4: 4 2 other
5: 5 3 other
table %>%
group_by(id) %>%
subset(
case_when(
any(row1 >= 5) ~ row1 < 5,
any(grepl("other", row2)) ~ !grepl("other", row2),
T ~ T
)
) %>%
filter(row_number() == 1) %>%
ungroup()
这个答案利用了 dplyr 的分组能力来检查每个组中的 any() ,因此很容易知道组中是否发生了某种情况。
它还使用 case_when() 按优先顺序检查一系列条件,实现一系列 if/else。
最后,由于在任何情况下我们只想要符合条件的第一行,它使用函数 row_number() 来检查我们是否在组中的第一行,以便到 select 它。
输出为:
# A tibble: 5 x 3
id row1 row2
<dbl> <dbl> <chr>
1 1 2 foo
2 2 2 bar
3 3 1 bar other
4 4 2 other
5 5 3 other
>
一个dplyr
解决方案:
table %>%
group_by(id) %>%
filter(row1 < 5 | n_distinct(row1 < 5) == 1) %>%
filter(!grepl("other", row2) | n_distinct(grepl("other", row2)) == 1) %>%
slice(1) %>% ungroup()
# # A tibble: 5 × 3
# id row1 row2
# <dbl> <dbl> <chr>
# 1 1 2 foo
# 2 2 2 bar
# 3 3 1 bar
# 4 4 2 other
# 5 5 3 other
n_distinct(...) == 1
用于判断一个条件是全TRUE
还是全FALSE
.
我们可以这样做:
library(dplyr)
library(tidyr)
library(stringr)
table %>%
group_by(id) %>%
separate_rows(row2) %>%
mutate(x = ifelse(row1>=5, min(row1),NA),
y = ifelse(str_detect(row2, 'other'), !str_detect(row2, 'other'), NA)) %>%
slice(1) %>%
select(-c(x, y))
id row1 row2
<dbl> <dbl> <chr>
1 1 2 foo
2 2 2 bar
3 3 1 bar
4 4 2 other
5 5 3 other
我有一个 table 每个 ID 包含两行。
table <- tibble(
id = c(1,1,2,2,3,3,4,4,5,5),
row1 = c(2,5,2,5,1,3,2,5,3,2),
row2 = c("foo", "other foo", "bar", "bar", "bar", "bar other", "other", "foo", "other", "other")
)
> table
# A tibble: 10 × 3
id row1 row2
<dbl> <dbl> <chr>
1 1 2 foo
2 1 5 other foo
3 2 2 bar
4 2 5 bar
5 3 1 bar
6 3 3 bar other
7 4 2 other
8 4 4 foo
9 5 3 other
10 5 2 other
我想根据连续三个规则将 table 解析为每个 ID 的一行:
- 如果对于每个ID,有一行row1大于等于5,则选择row1小于5的行。
- 否则,如果对于每个 ID,有一行 row2 包含单词 'other',请选择 row2 不包含单词 'other' 的行
- 否则,对于每个 ID,选择第一行。
我觉得一定有更直接的方法来做到这一点。到目前为止,这是我的尝试,但我不知道如何将 NA 解析为 return 'bar'.
table %>%
group_by(id) %>%
summarise(
row1 = ifelse(max(row1) >= 5,
first(row1[row1 < 5]),
ifelse(
grep("other", row2),
ifelse(
!is.na(first(row1[grep("other", row2, invert = T)])),
first(row1[grep("other", row2, invert = T)]),
first(row1)),
first(row1))
),
row2 = ifelse(
max(row1) >= 5,
first(row2[row1 < 5]),
ifelse(
grep("other", row2),
ifelse(
!is.na(first(row2[grep("other", row2, invert = T)])),
first(row2[grep("other", row2, invert = T)]),
first(row2)),
first(row2)
)
)
)
# A tibble: 5 × 3
id row1 row2
<dbl> <dbl> <chr>
1 1 2 foo
2 2 2 NA
3 3 1 bar
4 4 2 foo
5 5 3 other
期望的输出:
id | row1 | row2 |
---|---|---|
1 | 2 | foo |
2 | 2 | bar |
3 | 1 | bar |
4 | 2 | other |
5 | 3 | other |
非常感谢您的帮助。
这是一个解决方案,利用这个小函数,f()
使用 tidyverse
或 data.table
f <- function(r1,r2) {
if(sum(r1>=5)==1) return(list("row1" =r1[r1<5], "row2"=r2[r1<5]))
if(sum(grepl("other",r2))==1) return(list("row1" = r1[!grepl("other",r2)], "row2"=r2[!grepl("other",r2)]))
list("row1"=r1[1],"row2"=r2[1])
}
用法
library(tidyverse)
table %>%
group_by(id) %>%
summarize(n=list(f(row1,row2))) %>%
unnest_wider(n)
或
library(data.table)
setDT(table)[, f(row1,row2), by=id]
输出:
id row1 row2
<num> <num> <char>
1: 1 2 foo
2: 2 2 bar
3: 3 1 bar
4: 4 2 other
5: 5 3 other
table %>%
group_by(id) %>%
subset(
case_when(
any(row1 >= 5) ~ row1 < 5,
any(grepl("other", row2)) ~ !grepl("other", row2),
T ~ T
)
) %>%
filter(row_number() == 1) %>%
ungroup()
这个答案利用了 dplyr 的分组能力来检查每个组中的 any() ,因此很容易知道组中是否发生了某种情况。
它还使用 case_when() 按优先顺序检查一系列条件,实现一系列 if/else。
最后,由于在任何情况下我们只想要符合条件的第一行,它使用函数 row_number() 来检查我们是否在组中的第一行,以便到 select 它。
输出为:
# A tibble: 5 x 3
id row1 row2
<dbl> <dbl> <chr>
1 1 2 foo
2 2 2 bar
3 3 1 bar other
4 4 2 other
5 5 3 other
>
一个dplyr
解决方案:
table %>%
group_by(id) %>%
filter(row1 < 5 | n_distinct(row1 < 5) == 1) %>%
filter(!grepl("other", row2) | n_distinct(grepl("other", row2)) == 1) %>%
slice(1) %>% ungroup()
# # A tibble: 5 × 3
# id row1 row2
# <dbl> <dbl> <chr>
# 1 1 2 foo
# 2 2 2 bar
# 3 3 1 bar
# 4 4 2 other
# 5 5 3 other
n_distinct(...) == 1
用于判断一个条件是全TRUE
还是全FALSE
.
我们可以这样做:
library(dplyr)
library(tidyr)
library(stringr)
table %>%
group_by(id) %>%
separate_rows(row2) %>%
mutate(x = ifelse(row1>=5, min(row1),NA),
y = ifelse(str_detect(row2, 'other'), !str_detect(row2, 'other'), NA)) %>%
slice(1) %>%
select(-c(x, y))
id row1 row2
<dbl> <dbl> <chr>
1 1 2 foo
2 2 2 bar
3 3 1 bar
4 4 2 other
5 5 3 other