r将多个条件应用于多个列(函数参数值的向量)

时间:2016-06-30 13:17:23

标签: r dplyr

我正在尝试将多个条件应用于data.frame的多个列,其中条件i应该应用于列i,即应用条件取决于我所在的列。我有一个工作解决方案,但它有两个主要缺点是,它在大数据上可能会变慢,因为它使用for循环并且它需要两个输入向量"列将条件应用于"和"要应用的条件"以相同的顺序。我设想了一种利用快速数据纠缠包功能的解决方案,例如dplyr,data.table,并且在参数向量元素的顺序方面更灵活。一个例子应该说清楚(这里条件只是一个阈值测试,但在更大的问题中,它可能是一个涉及数据集变量的更复杂的布尔表达式。)

t <- structure(list(a = c(2L, 10L, 10L, 10L, 3L), 
                    b = c(5L, 10L, 20L, 20L, 20L), 
                    c = c(100L, 100L, 100L, 100L, 100L)), 
               .Names = c("a", "b", "c"), 
               class = "data.frame", 
               row.names = c(NA, -5L))

foo_threshold <- 
  function(data, cols, thresholds, condition_name){
    df <- data.frame(matrix(ncol = length(cols), nrow = nrow(data)))
    colnames(df) <- paste0(cols, "_", condition_name)

    for (i in 1:length(cols)){
      df[,i] <- ifelse(data[,i] > thresholds[i],T,F)
      }
    return(df)
    }

foo_threshold(data = t, cols = c("a", "b"), thresholds = c(5, 18), 
              condition_name = "bigger_threshold")

我试图在dplyr链中解决它但是我无法正确传递参数向量,即如何明确表示他应该将条件i应用于列i。下面是我要去的插图。它没有工作,它错过了一些观点,但我认为它说明了我想要实现的目标。请注意,这里假设条件位于data.frame中,其中列变量保存col名称,并且通过查找(dplyr filer + select chain)提取阈值。

foo_threshold <- function(data, cols, thresholds, cond_name) {
  require(dplyr)
  # fun to evaluate boolean condition
  foo <- function(x) {
    threshold <- thresholds %>% filter(variable==x) %>% select(threshold)
    temp <- ifelse(x > threshold, T, F)
    return(temp)
    }

  vars <- setNames(cols, paste0(cols,"_",cond_name))

  df_out <-
    data %>%
    select_(.dots = cols) %>%
    mutate_(funs(foo(.)), vars) %>%
    select_(.dots = names(vars))

  return(df_out)
  }

# create threshold table
temp <- 
  data.frame(variable = c("a", "b"),
             threshold = c(5, 18),
             stringsAsFactors = F)

# call function (doesn't work)
foo_threshold(data = t, thresholds = temp, cond_name = "bigger_threshold")

编辑:条件的@thepule data.frame可能如下所示,其中x是列。因此,对每个条件的相应列的每一行进行评估。

conditions <- 
  data.frame(variable = c("a", "b"),
             condition = c("x > 5 and x < 10", "!x %in% c("o", "p")"),
             stringsAsFactors = F)

4 个答案:

答案 0 :(得分:2)

使用扫描代替mapply进行了另一次尝试。离开之前的答案,因为我觉得它增加了显示效率低下的价值。 这个新的答案似乎比OP快两倍。我认为它比当前最好的答案要慢一点,但代码稍微简洁一些。

如果您愿意接受结果作为矩阵而不是data.frame,它运行得更快。

override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath){

 switch (indexPath.row)   {
     case 0:
        self.performSegueWithIdentifier("box", sender: self)
     case 1:  
        self.performSegueWithIdentifier("box1", sender: self)
   default:
      break
    }

}

答案 1 :(得分:1)

试试这个:

library(dplyr)

 foo_threshold <-
     function(data, cols, thresholds, condition_name){
         temp <- rbind(data[,cols], thresholds) %>%
         lapply(function(x) x[1:length(x)-1] > last(x)) %>% data.frame()
         colnames(temp) <- paste0(cols, "_", condition_name)
         return(temp)

     }

 foo_threshold(data = t, cols = c("a", "b"), thresholds = c(5, 18), 
               condition_name = "bigger_threshold")

为了测试哪个更快:

test <- data.frame(a = runif(10000000), b = runif(10000000), stringsAsFactors = F)

 lapply(list(foo_threshold_original, foo_threshold),
        function(x) system.time(x(data = test, cols = c("a", "b"), thresholds = c(0.5, 0.8), 
                      condition_name = "bigger_threshold")))

其中foo_threshold_original是您的初始版本。 结果是:

[[1]]
   user  system elapsed 
   3.95    0.64    4.58 

[[2]]
   user  system elapsed 
   1.73    0.24    1.96

因此新版本在更大的数据帧上实际上更快。

答案 2 :(得分:1)

最后尝试回答。试图使代码更通用,以便它可以接受任意函数。很好,它似乎比我之前的任何答案都要快得多。如果我犯了一个愚蠢的错误,我也很累。

temp <- structure(list(a = c(2L, 10L, 10L, 10L, 3L), 
                       b = c(5L, 10L, 20L, 20L, 20L), 
                       c = c(100L, 100L, 100L, 100L, 100L)), 
                  .Names = c("a", "b", "c"), 
                  class = "data.frame", 
                  row.names = c(NA, -5L))



condition <- c(function(x)  x> 5  , 
               function(x) x > 18 )


foo_threshold <- function ( data , cols , threshold , condition_name ) {
    dat <- data[0]
    for ( i in 1:length(condition))     dat[cols[i]] <- condition[[i]]( data[[cols[i]]] )
    names(dat) <- paste0( cols , "_" , condition_name)
    return(dat)
}


foo_threshold(data = temp, cols = c("a", "b"), threshold  = condition , 
           condition_name = "bigger_threshold")

答案 3 :(得分:0)

这个怎么样?不使用dplyr(我加载它无论如何使用管道)

library(dplyr)

foo_threshold <- function( data , cols , thresholds , condition_name){
    dat <- mapply( function(x , val)  x > val  , data[cols] , thresholds ) %>%  as.data.frame
    names(dat) <- paste0(names(dat) , "_" , condition_name)
    return(dat)
}

编辑:简化