【问题标题】:How to apply a function to each element of a data.frame?如何将函数应用于 data.frame 的每个元素?
【发布时间】:2016-02-11 21:10:01
【问题描述】:

我想将数值转换为因子,如果值低于 -2,则“down”应该是因子,如果高于 2,则“up”和“no_change”之间:

到目前为止,我一直在考虑创建一个函数:

classifier <- function(x){
    if (x >= 2){
      return(as.factor("up"))
    }else if (x <= -2){
      return(as.factor("down"))
    }else {
      return(as.factor("no_change"))
    }
}

我可以让它对输入进行迭代(使用 for 循环)并返回一个列表,所以我可以将它与 apply 一起使用。

我想将此函数应用到data.frame的所有单元格,我该怎么做?

模拟数据 (runif(15, min=-5, max=5)):

c(1.11004611710086, -1.86842617811635, 1.72159335808828, -2.68788822228089, 
2.72551498375833, 3.67290901951492, -4.00984475389123, -2.39582793787122, 
4.22395745059475, -0.360892189200968, 1.35027756914496, 2.89919016882777, 
-0.158692332915962, -0.950306688901037, 3.39141107397154)

【问题讨论】:

    标签: r


    【解决方案1】:

    使用DF &lt;- iris[-5] 作为样本数据,您可以使用cut,正如我在 cmets 中建议的那样。

    试试:

    DF[] <- lapply(DF, cut, c(-Inf, -2, 2, Inf), c("down", "no_change", "up"))
    
    head(DF)
    ##   Sepal.Length Sepal.Width Petal.Length Petal.Width
    ## 1           up          up    no_change   no_change
    ## 2           up          up    no_change   no_change
    ## 3           up          up    no_change   no_change
    ## 4           up          up    no_change   no_change
    ## 5           up          up    no_change   no_change
    ## 6           up          up    no_change   no_change
    
    tail(DF)
    ##     Sepal.Length Sepal.Width Petal.Length Petal.Width
    ## 145           up          up           up          up
    ## 146           up          up           up          up
    ## 147           up          up           up   no_change
    ## 148           up          up           up   no_change
    ## 149           up          up           up          up
    ## 150           up          up           up   no_change
    

    或者,使用 RHertel 的“mock_data”:

    cut(mock_data, c(-Inf, -2, 2, Inf), c("down", "no_change", "up"))
    ##  [1] no_change no_change no_change down      up        up        down     
    ##  [8] down      up        no_change no_change up        no_change no_change
    ## [15] up       
    ## Levels: down no_change up
    

    基准测试

    正如我在 cmets 中所建议的,RHertel 的方法可能是最有效的。该方法使用非常简单的子集(速度很快)和factor(通常也很快)。

    在您描述的数据大小上,您肯定会注意到差异:

    set.seed(1)
    nrow = 20000
    ncol = 1000
    x <- as.data.frame(matrix(runif(nrow * ncol, min=-5, max=5), ncol = ncol))
    
    factorize <- function(invec) {
      factorized <- rep("no_change", length(invec))
      factorized[invec > 2]  <- "up"
      factorized[invec < -2]  <- "down"
      factor(factorized, c("down", "no_change", "up"))
    }
    
    RHfun <- function(indf = x) {
      indf[] <- lapply(indf, factorize)
      indf
    }
    
    AMfun <- function(DF = x) {
      DF[] <- lapply(DF, cut, c(-Inf, -2, 2, Inf), c("down", "no_change", "up"))
      DF
    }
    
    library(microbenchmark)
    microbenchmark(AMfun(), RHfun(), times = 10)
    # Unit: seconds
    #     expr      min       lq     mean   median       uq       max neval
    #  AMfun() 7.501814 8.015532 8.852863 8.731638 9.660191 10.198983    10
    #  RHfun() 1.437696 1.485791 1.723402 1.574507 1.637139  2.528574    10
    

    【讨论】:

    • 我希望它是最快的解决方案之一,但该功能的目的是针对这种情况。感谢您指出。
    • @Llopis,不。我没有测试过,但我确信 RHertel 的答案会更快。这只是基本的子集,这将非常有效。
    • 我很想看看这些基准,我的 data.frames 可以有 20532 行和 1000 列......而且越快越好 :)
    • @Llopis,完成。正如您在我的回答中看到的那样,基准测试通常很容易做到。
    【解决方案2】:

    我一般不喜欢ifelse(),所以我可能会引入一个新向量并以不同的方式处理问题。

    factorized <- rep("no_change", length(mock_data))
    factorized[mock_data > 2]  <- "up"
    factorized[mock_data < -2]  <- "down"
    factorized <- as.factor(factorized)
    #> factorized
    #[1] no_change no_change no_change down      up        up        down      down      up        no_change no_change up        no_change no_change up       
    #Levels: down no_change up
    

    本例中的数据取自OP:

    mock_data <- c(1.11004611710086, -1.86842617811635, 1.72159335808828, -2.68788822228089, 
               2.72551498375833, 3.67290901951492, -4.00984475389123, -2.39582793787122, 
               4.22395745059475, -0.360892189200968, 1.35027756914496, 2.89919016882777, 
               -0.158692332915962, -0.950306688901037, 3.39141107397154)
    

    感谢@docendo discimus 改进了这个答案并提供了有用的评论。

    【讨论】:

    • 您可能对我的回答中的基准感兴趣。
    • 确实很有趣。非常感谢,@AnandaMahto
    【解决方案3】:

    apply 与行 列的标识符一起使用。

    apply(yourDF, c(1, 2), classifier)
    

    这是为了将函数应用于data.frame 的每个单元格。它可能不适用于向量。

    【讨论】:

    • 我不知道它可以用于所有的data.frame,谢谢!以后可能会派上用场
    • @Llopis 实际上apply 在数组上工作,返回取决于应用的乐趣所产生的内容。它在 data.frame 输入上调用as.matrix,因此会将不同类型的列强制为同一类型。
    猜你喜欢
    • 2018-07-17
    • 1970-01-01
    • 2015-01-18
    • 2021-05-25
    • 2014-09-24
    • 1970-01-01
    • 2012-12-21
    • 2021-10-08
    相关资源
    最近更新 更多