使用dplyr中的函数合并其他行中的值

时间:2014-11-19 02:44:38

标签: r dplyr

提前为笨重的代码道歉。 我有一个类似于以下的数据框:

df <- data.frame(c(rep_len(1,5), 2, 2), c("A", "A", "B", "B", "C", "C", "C"))
names(df) <- c("id", "consequence")

  id consequence
1  1           A
2  1           A
3  1           B
4  1           B
5  1           C
6  2           C
7  2           C

我想执行以下过滤操作:

  

如果id by group包含结果A或B,则保留这些行,并删除带有结果C的行。如果一个组只包含C或单行,则保留那些/那些行/行。

我试图在dplyr中使用自定义函数执行此操作,但是存在所有行都被过滤的问题,从而消除了所有后果C:

# filtering function:
consequence_select <- function(x) {
  if(n_distinct(x$consequence) > 1) {
  if(any(unique(x$consequence) %in% c("A", "B"))) {
  x %>%
    filter(consequence %in% c("A", "B"))} else {return(x)}
     } else {return(x)}
}


df %>%
group_by(id) %>%
consequence_select

  id consequence
1  1           A
2  1           A
3  1           B
4  1           B

我能用plyr正确地做到这一点:

ddply(df, .(id), consequence_select)

  id consequence
1  1           A
2  1           A
3  1           B
4  1           B
5  2           C
6  2           C

3 个答案:

答案 0 :(得分:4)

使用dplyr,您需要将函数包装在do

df %>%
  group_by(id) %>%
  do(consequence_select(.))

.是指代数据框df的“代词”。

答案 1 :(得分:4)

您可以通过仅在filter参数内部而不是do内部应用来优化代码,因为filter是此类任务的专用dplyr函数。我创建了两个函数,并使用现有答案对它们进行基准测试。您要使用哪个功能取决于您的要求 - 对于样本数据,它们都会产生相同的结果。我还为基准测试创建了稍大的样本数据,如下所示。

# sample data
df <- data.frame(id = sample(100, 1000, replace = T), 
                 consequence = sample(LETTERS[1:3], 1000, replace = TRUE, prob = c(0.2, 0.2, 0.6)))

# the existing custom function
consequence_select <- function(x) {
  if(n_distinct(x$consequence) > 1) {
    if(any(unique(x$consequence) %in% c("A", "B"))) {
      x %>%
        filter(consequence %in% c("A", "B"))} else {return(x)}
  } else {return(x)}
}

# eipi's answer
f1 <- function() {
  df %>%
  group_by(id) %>%
  do(consequence_select(.)) }

# jazzuro's answer
f2 <- function() {
  df %>%
  group_by(id) %>%
  do(if(all(.$consequence == "C")) {.} else{.[-which(.$consequence == "C"), ]}) }

# my answer 1
f3a <- function() {
  df %>% 
    group_by(id) %>% 
    filter((consequence != "C" & n_distinct(consequence) > 1L) | all(consequence == "C") )
}

# my answer 2
f3b <- function() {
  df %>% 
    group_by(id) %>% 
    filter((consequence %in% c("A", "B") & n_distinct(consequence) > 1L) | all(consequence == "C"))
}

library(microbenchmark)

microbenchmark(f1(), f2(), f3a(), f3b(), unit = "relative")

Unit: relative
 expr       min        lq    median        uq       max neval
f1()  11.243524 11.092915 10.956129 10.717519  8.859949   100
f2()   6.603549  6.663674  6.653424  6.566012 10.956784   100
f3a()  1.279952  1.294679  1.291719  1.294606  1.165322   100
f3b()  1.000000  1.000000  1.000000  1.000000  1.000000   100

all.equal(f1(), f3a())
#[1] TRUE
all.equal(f1(), f3b())
#[1] TRUE

正如您所看到的,数据量的略微增加已经显示出功能之间的速度差异> 10倍。

答案 2 :(得分:3)

您可以使用do执行此类功能。 foo是您的数据。

foo %>%
    group_by(id) %>%
    do(if(all(.$consequence == "C")) {.} else{.[-which(.$consequence == "C"), ]})

#  id consequence
#1  1           A
#2  1           A
#3  1           B
#4  1           B
#5  2           C
#6  2           C