dplyr-按组大小筛选

dplyr-按组大小筛选,r,dataframe,filter,dplyr,subset,R,Dataframe,Filter,Dplyr,Subset,过滤data.frame以仅获取大小为5的组的最佳方法是什么 因此,我的数据如下所示: require(dplyr) n <- 1e5 x <- rnorm(n) # Category size ranging each from 1 to 5 cat <- rep(seq_len(n/3), sample(1:5, n/3, replace = TRUE))[1:n] dat <- data.frame(x = x, cat = cat) system.time({

过滤data.frame以仅获取大小为5的组的最佳方法是什么

因此,我的数据如下所示:

require(dplyr)
n <- 1e5
x <- rnorm(n)
# Category size ranging each from 1 to 5
cat <- rep(seq_len(n/3), sample(1:5, n/3, replace = TRUE))[1:n]

dat <- data.frame(x = x, cat = cat)
system.time({
  all_ind <- rep(seq_len(n_groups(dat)), group_size(dat))
  take_only <- which(group_size(dat) == 5L)
  out2 <- dat[all_ind %in% take_only, ]
})
#    user  system elapsed 
#   0.026   0.008   0.036
all.equal(out1, out2) # TRUE
require(dplyr)

n这里是另一种dplyr方法,您可以尝试

semi_join(dat, count(dat, cat) %>% filter(n == 5), by = "cat")
--

以下是另一种基于OP原始方法的方法,稍作修改:

n <- 1e5
x <- rnorm(n)
# Category size ranging each from 1 to 5
cat <- rep(seq_len(n/3), sample(1:5, n/3, replace = TRUE))[1:n]

dat <- data.frame(x = x, cat = cat)

# second data set for the dt approch
dat2 <- data.frame(x = x, cat = cat)

sol_floo0 <- function(dat){
  dat <- group_by(dat, cat)
  all_ind <- rep(seq_len(n_groups(dat)), group_size(dat))
  take_only <- which(group_size(dat) == 5L)
  dat[all_ind %in% take_only, ]
}

sol_floo0_v2 <- function(dat){
  g <- group_by(dat, cat) %>% group_size()
  ind <- rep(g == 5, g)
  dat[ind, ]
}



microbenchmark::microbenchmark(times = 10,
                               sol_floo0(dat),
                               sol_floo0_v2(dat2))
#Unit: milliseconds
#               expr      min       lq     mean   median       uq      max neval cld
#     sol_floo0(dat) 43.72903 44.89957 45.71121 45.10773 46.59019 48.64595    10   b
# sol_floo0_v2(dat2) 29.83724 30.56719 32.92777 31.97169 34.10451 38.31037    10  a 
all.equal(sol_floo0(dat), sol_floo0_v2(dat2))
#[1] TRUE

n按时间比较答案:

require(dplyr)
require(data.table)
n <- 1e5
x <- rnorm(n)
# Category size ranging each from 1 to 5
cat <- rep(seq_len(n/3), sample(1:5, n/3, replace = TRUE))[1:n]

dat <- data.frame(x = x, cat = cat)

# second data set for the dt approch
dat2 <- data.frame(x = x, cat = cat)

sol_floo0 <- function(dat){
  dat <- group_by(dat, cat)
  all_ind <- rep(seq_len(n_groups(dat)), group_size(dat))
  take_only <- which(group_size(dat) == 5L)
  dat[all_ind %in% take_only, ]
}

sol_floo0_v2 <- function(dat){
  g <- group_by(dat, cat) %>% group_size()
  ind <- rep(g == 5, g)
  dat[ind, ]
}

sol_docendo_discimus <- function(dat){ 
  dat <- group_by(dat, cat)
  semi_join(dat, count(dat, cat) %>% filter(n == 5), by = "cat")
}

sol_akrun <- function(dat2){
  setDT(dat2)[dat2[, .I[.N==5], by = cat]$V1]
}

sol_sotos <- function(dat2){
  setDT(dat2)[, if(.N == 5) .SD, by = cat]
}

sol_chirayu_chamoli <- function(dat){
  rle_ <- rle(dat$cat)
  dat[dat$cat %in% rle_$values[rle_$lengths==5], ]
}

microbenchmark::microbenchmark(times = 20,
                               sol_floo0(dat),
                               sol_floo0_v2(dat),
                               sol_docendo_discimus(dat), 
                               sol_akrun(dat2),
                               sol_sotos(dat2),
                               sol_chirayu_chamoli(dat))

我概括了作者编写的函数,将其与现有的dplyr函数一起使用:

#' inherit dplyr::filter
#' @param min minimal group size, use \code{min = NULL} to filter on maximal group size only
#' @param max maximal group size, use \code{max = NULL} to filter on minimal group size only
#' @export
#' @source Stack Overflow answer by docendo discimus, \url{https://stackoverflow.com/a/43110620/4575331}
filter_group_size <- function(.data, min = NULL, max = min) {
  g <- dplyr::group_size(.data)
  if (is.null(min) & is.null(max)) {
    stop('`min` and `max` cannot both be NULL.')
  }
  if (is.null(max)) {
    max <- base::max(g, na.rm = TRUE)
  }
  ind <- base::rep(g >= min & g <= max, g)
  .data[ind, ]
}
很好,现在检查OP的问题;组大小正好为
5

dat2 %>%
  group_by(cat) %>%
  filter_group_size(5, NULL) %>%
  summarise(n = n()) %>%
  arrange(desc(n))

# # A tibble: 6,634 x 2
#      cat     n
#    <int> <int>
#  1    NA    19
#  2     1     5
#  3     2     5
#  4     6     5
#  5    15     5
#  6    17     5
#  7    21     5
#  8    27     5
#  9    33     5
# 10    37     5
# # ... with 6,624 more rows
dat2 %>%
  group_by(cat) %>%
  filter_group_size(5) %>%
  summarise(n = n()) %>%
  pull(n) %>%
  unique()
# [1] 5

万岁。

我知道你要求一个
dplyr
解决方案,但是如果你把它和一些
purrr
结合起来,你可以在一行中得到它,而不需要指定任何新的函数。(不过要慢一点。)


使用
n()
,您可以更简洁地执行此操作:


加速dplyr way
n()
过滤器的一个非常简单的方法是将结果存储在一个新列中。如果以后有多个
过滤器,则计算组大小的初始时间将摊销

库(dplyr)
预备组%
分组依据(类别)%>%
变异(
出现次数=n()
) %>%
解组()
}
#使用“事件”列创建新的数据框:
#数据预处理%prep\u组
过滤
事件
字段比解决方案快得多:


solu\0等待,直到看到data.table解决方案。一点也不夸张,也很简单。类似于
setDT(dat)[,if(.N==5).SD,by=cat]
data.table中更快的方法应该是
setDT(dat)[dat[,.I[.N==5],by=cat]$V1]
@ChirayuChamoli:猜你的意思是
m
:是的,它是有序的。@ChirayuChamoli:答案很好。刚把它加在汉克斯下面。与我的
过滤器
解决方案相比,这是一个巨大的加速。仍然在寻找一个比我的第二个混合解决方案更好的解决方案……第二个版本几乎和包含分组的DT解决方案一样快。很好的一个只是为了更好的衡量,您应该在函数计时中包含
groupby
步骤too@docendodiscimus当前位置你问得对,所以这样更好。在我的分析中,我已经对它们进行了分组。这就是为什么我在测量中排除了它…
sol\u docendo\u discimus
不需要
group\u by
呼叫!这就是运行时间如此之高的原因。我有一些数据表明这个版本失败了。似乎分组按数据排序,这可能会导致错误的索引
data.frame(x=c(2,2,1))%%>%group\u by(x)%%>%group\u size
生成
c(1,2)
而不是
c(2,1)
首先,您使用的
group\u size()
dplyr
包中的函数,与我的答案无关。其次,来自
dplyr
包的
groupby()
函数按字母顺序对组进行排序。欢迎使用堆栈溢出。没有任何解释的代码转储很少有帮助。堆栈溢出是关于学习,而不是提供盲目复制和粘贴的代码片段。请回答您的问题,并解释它如何回答所问的具体问题。看见这一点在用现有答案回答老问题(这一个已经超过4岁)时尤为重要。这个答案如何改进已有的答案(特别是乔的答案)?如果你读过问题的“我能想出的dplyr方法是”部分,你就会知道他/她已经尝试过了。OP认为它太慢了。
dat2 %>%
  group_by(cat) %>%
  filter_group_size(5, NULL) %>%
  summarise(n = n()) %>%
  arrange(desc(n))

# # A tibble: 6,634 x 2
#      cat     n
#    <int> <int>
#  1    NA    19
#  2     1     5
#  3     2     5
#  4     6     5
#  5    15     5
#  6    17     5
#  7    21     5
#  8    27     5
#  9    33     5
# 10    37     5
# # ... with 6,624 more rows
dat2 %>%
  group_by(cat) %>%
  filter_group_size(5) %>%
  summarise(n = n()) %>%
  pull(n) %>%
  unique()
# [1] 5
library(dplyr)
library(purrr)
library(tidyr)

dat %>% 
  group_by(cat) %>% 
  nest() %>% 
  mutate(n = map(data, n_distinct)) %>%
  unnest(n = n) %>% 
  filter(n == 5) %>% 
  select(cat, n)
library(dplyr)
dat %>% group_by(cat) %>% filter(n() == 5)
Unit: microseconds
                    expr       min        lq      mean    median        uq        max neval cld
          sol_floo0(dat) 33345.764 35603.446 42430.441 37994.477 41379.411 144103.471    50   c
       sol_floo0_v2(dat) 26180.539 27842.927 29694.203 29089.672 30997.411  37412.899    50  b 
 sol_cached(dat_prepped)   801.402   930.025  1342.348  1098.843  1328.192   5049.895    50 a  
Unit: milliseconds
            expr      min       lq     mean   median       uq      max neval cld
 prep_group(dat) 45.67805 47.68100 48.98929 49.11258 50.08214 52.44737    10   b
  prep_join(dat) 35.01945 36.20857 37.96460 36.86776 38.71056 45.59041    10  a 
dat %>%
  dplyr::group_by(cat) %>%
  dplyr::add_tally() %>%
  dplyr::filter(n == 5)