【问题标题】:r apply multiple conditions to multiple columns (vectors of function argument values)r 将多个条件应用于多个列(函数参数值的向量)
【发布时间】:2016-06-30 13:17:22
【问题描述】:

我正在尝试将多个条件应用于 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 文件管理器 + 选择链)提取的。

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 of conditions 可能如下所示,其中 x 是列。所以每个条件都会针对其对应列的每一行进行评估。

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

【问题讨论】:

  • 我会提醒不要将对象命名为t,因为那是转置函数
  • 感谢您的提示。它应该代表 temp,我很懒。
  • 好的,这种格式的条件肯定更棘手。不过有趣的问题。
  • 好的,这种格式的条件肯定更棘手。不过有趣的问题。那里的“o”和“p”代表什么?
  • 示例并不完美。想要明确布尔值不仅适用于数字变量,也适用于字符变量。当然,同时比较数字和字符值是没有意义的。

标签: r dplyr


【解决方案1】:

再次尝试使用扫描而不是映射。保留之前的答案,因为我觉得它增加了价值,显示了 mapply 的低效。 这个新答案的运行速度似乎是 OP 的两倍多。我认为它比当前评分最高的答案慢了一点,但代码更简洁。

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

library(dplyr) 


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))

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

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

【讨论】:

  • 我认为你的函数调用中的 temp 应该像 OP 初始问题一样是 t 还是故意避免使用 t?
  • 确实是为了避免使用t,我更新了答案,让数据源更加清晰。
  • 如果输出是矩阵,答案会如何变化?
  • 它会运行得稍微快一些,因为您不需要转换为 data.frame。虽然它相对较小。
【解决方案2】:

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

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")

【讨论】:

  • 在您的函数定义中,当它应该是数据时,您会引用 temp。否则我真的很喜欢它在条件向量和性能方面给我的灵活性。
  • 我现在会接受这个,尽管我会进一步考虑如何解决参数值向量中的顺序问题,即它不应该在 cols 和 threshold 中具有相同的顺序。谢谢!
  • 你是对的,对愚蠢的错误感到抱歉,会更新!
  • 如果您希望它们不重要,您可以使用命名列表将函数链接到变量,例如 funs 5 , b = function( x) x > 18) foo_threshold
  • 好主意!我会试试那个tmr。
【解决方案3】:

试试这个:

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

所以新版本在更大的数据帧上实际上更快。

【讨论】:

  • 如我所见,您将阈值作为最后一行与输入数据进行了行绑定,然后将除最后一行之外的所有内容与最后一行进行比较,从而产生一个布尔 data.frame。我喜欢这个主意,尽管我认为您应该删除结果 data.frame 的最后一行。我唯一的问题是这个例子中的条件是数字阈值,但更大的问题是需要评估的布尔表达式。
  • 我明白了,是的,这仅适用于数字阈值。
  • 此外,我仍然必须以相同的顺序提供列和阈值,并且无法使用通过查找提取的条件的 data.frame。但这绝对是性能的提升。
  • 你能添加一个更现实的阈值示例吗?您想用作查找表的条件数据框之一?
【解决方案4】:

这个怎么样?不使用 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)
}

编辑:简化

【讨论】:

  • 刚刚进行了一些速度测试,我的代码实际上比 OP 慢 10 倍。因此,如果您对速度感兴趣,最好避免!
  • 我想我可以以某种方式使用mapply。很惊讶它这么慢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-07
  • 2012-03-24
  • 2021-12-07
  • 2015-12-28
  • 2017-01-18
相关资源
最近更新 更多