在 R 中使用 dplyr 在 group_by 之后应用自定义函数
Posted
技术标签:
【中文标题】在 R 中使用 dplyr 在 group_by 之后应用自定义函数【英文标题】:Apply a custom function after group_by using dplyr in R 【发布时间】:2019-12-06 09:39:13 【问题描述】:如何在 group_by 之后使用 dplyr 应用函数来删除具有 2 个或更多连续 NA 的组?我编写了一个函数,无论数据框中的列是否有 2 个或更多 NA,它都会输出 True 或 False:
# function for determining if ts contains consecutive NAs
is.na.contiguous <- function(df, consecutive)
na.rle <- rle(is.na(df$b))
na.rle$values <- na.rle$values & na.rle$lengths >= consecutive
any(na.rle$values)
# example df
d = structure(list(a = c(1, 2, 3, 4, 5, 6, 7, 8), b = c(1, 2, 2,
+ NA, NA, 2, NA, 2), c = c(1, 1, 1, 2, 2, 2, 3, 3)), class = "data.frame", row.names = c(NA,
+ -8L))
head(d)
a b c
1 1 1 1
2 2 2 1
3 3 2 1
4 4 NA 2
5 5 NA 2
6 6 2 2
7 7 NA 3
8 8 2 3
# test function
is.na.contiguous(d,2)
TRUE # column b has 2 consecutive NAs
is.na.contiguous(d,3)
FALSE # column b does not have 3 consecutive NAs
现在如何将此函数应用于数据框中的每个组?以下是我尝试过的:
d %>% group_by(c) %>% mutate(consecNA = is.na.contiguous(.,2)) %>% as.data.frame()
a b c consecNA
1 1 1 1 TRUE
2 2 2 1 TRUE
3 3 2 1 TRUE
4 4 NA 2 TRUE
5 5 NA 2 TRUE
6 6 2 2 TRUE
7 7 NA 3 TRUE
8 8 2 3 TRUE
我做错了什么?
【问题讨论】:
添加一列,d %>% group_by(c) %>% mutate(consecNA = any(is.na(b) & lag(is.na(b), default = FALSE)))
;删除组,d %>% group_by(c) %>% filter(!any(is.na(b) & lag(is.na(b), default = FALSE)))
@TYL 你能显示预期的输出吗?是一列逻辑向量还是你要过滤
我的最终目标是过滤,但我似乎找不到方法。所以我的想法是改变一列逻辑向量,然后根据该列进行过滤。
【参考方案1】:
一个选项是在逻辑向量 (is.na(b)
) 上使用来自 data.table
的 rleid
,并使用它来对行数大于或等于 2 的组进行子集化,如果 all
元素是NA
library(data.table)
i1 <- setDT(d)[, .I[!(.N >=2 & all(is.na(b)))], rleid(is.na(b))]$V1
d[i1]
#. a b c
#1: 1 1 1
#2: 2 2 1
#3: 3 2 1
#4: 6 2 2
#5: 7 NA 3
#6: 8 2 3
或者如果我们还需要按 'c' 分组
setDT(d)[d[, .I[sum(is.na(b)) <2], .(grp = rleid(is.na(b)), c)]$V1]
或tidyverse
library(dplyr)
d %>%
group_by(grp = rleid(is.na(b))) %>%
filter(!(n() >=2 & all(is.na(b))))
# A tibble: 6 x 4
# Groups: grp [4]
# a b c grp
# <dbl> <dbl> <dbl> <int>
#1 1 1 1 1
#2 2 2 1 1
#3 3 2 1 1
#4 6 2 2 3
#5 7 NA 3 4
#6 8 2 3 5
或者另一种选择是获取逻辑向量的sum
并检查它是否小于2
d %>%
group_by(c, grp = rleid(is.na(b))) %>%
filter(sum(is.na(b))<2)
如果我们使用 OP 中的函数
is.na.contiguous <- function(x, consecutive)
na.rle <- rle(is.na(x))
with(na.rle, any(values & na.rle$lengths >= consecutive))
d %>%
group_by(c) %>%
mutate(consecNA = is.na.contiguous(b, 2))
# A tibble: 8 x 4
# Groups: c [3]
# a b c consecNA
# <dbl> <dbl> <dbl> <lgl>
#1 1 1 1 FALSE
#2 2 2 1 FALSE
#3 3 2 1 FALSE
#4 4 NA 2 TRUE
#5 5 NA 2 TRUE
#6 6 2 2 TRUE
#7 7 NA 3 FALSE
#8 8 2 3 FALSE
【讨论】:
【参考方案2】:与其将整个数据框传递给is.na.contiguous
,不如只传递列值,然后通过组应用它会很简单,如果您想对某些不同的列执行相同的操作,它也会变得灵活。
is.na.contiguous <- function(x, consecutive)
na.rle <- rle(is.na(x))
na.rle$values <- na.rle$values & na.rle$lengths >= consecutive
any(na.rle$values)
library(dplyr)
d %>%
group_by(c) %>%
filter(!is.na.contiguous(b, 2))
# a b c
# <dbl> <dbl> <dbl>
#1 1 1 1
#2 2 2 1
#3 3 2 1
#4 7 NA 3
#5 8 2 3
【讨论】:
以上是关于在 R 中使用 dplyr 在 group_by 之后应用自定义函数的主要内容,如果未能解决你的问题,请参考以下文章
R语言dplyr包使用dplyr函数使用group_by函数summarise函数和mutate函数计算分组占比实战
如何使用 R 和 dplyr 中连续的元素执行 group_by
R语言dplyr包使用group_by函数和summarise函数构建频率表实战
R使用dplyr group_by / sum for循环,作为连接列表输出
R语言dplyr包使用group_by函数arrange函数和filter函数获取每个分组的第一个第N个最后一个记录实战