【问题标题】:How to efficiently mutate multiple columns of a large dataframe如何有效地改变大型数据框的多列
【发布时间】:2019-07-09 09:57:07
【问题描述】:

如果能有效地将my function 应用于我的大型数据框DT_large 的多个列,我将不胜感激。

当我将它与dplyr::mutate_at() 一起应用到一个小数据框DT_small 时,我的函数运行良好且高效。但是,当应用于相对较大的数据集DT_large、可用here 时,dplyr::mutate_at() 需要几个小时才能提供所需的输出。

可能是我的代码中存在一些错误,导致 dplyr::mutate_at() 在相对较大的数据集上效率较低。或者,dplyr::mutate_at() 对于像我这样相对较大的数据集可能效率不高。

在任何一种情况下,我都希望能有任何帮助来解决我的问题,也就是说,一种更快的方法可以将我的函数正确应用到 DT_large 并提供所需的输出,就像我将它应用到 DT_small 时一样。

#small 数据集

DT_small<-structure(list(.id = 1:10, `_E1.1` = c(0.475036902, 0.680123015, 
0.896920608, 0.329908621, 0.652288128, 0.408813318, 0.486444822, 
0.429333778, 2.643293032, 0.782194143), `_E1.2` = c(79.22653114, 
0.680123015, 4.088529776, 0.232076989, 0.652288128, 0.329908621, 
0.486444822, 0.429333778, 2.643293032, 0.963554482), `_E1.3` = c(0.466755502, 
0.680123015, 0.461887024, 1.236938197, 0.652288128, 0.408813318, 
0.486444822, 0.429333778, 2.643293032, 0.95778584), `_E1.4` = c(1.608298119, 
0.680123015, 0.578464999, 0.317125521, 0.652288128, 0.408813318, 
0.486444822, 0.429333778, 2.643293032, 2.125841957), `_E1.5` = c(0.438424932, 
0.680123015, 0.896920608, 0.366118007, 0.652288128, 1.007079029, 
0.486444822, 0.429333778, 2.643293032, 0.634134022), `_E10.1` = c(0.45697607, 
0.647681721, 1.143509029, 0.435735621, 0.49400961, 0.501421816, 
0.461123723, 0.568477247, 1.756598213, 0.67895017), `_E10.2` = c(35.30312978, 
0.647681721, 2.58357783, 0.25514789, 0.49400961, 0.435735621, 
0.461123723, 0.568477247, 1.756598213, 0.776970116), `_E10.3` = c(0.79477661, 
0.647681721, 0.672430959, 0.886991224, 0.49400961, 0.501421816, 
0.461123723, 0.568477247, 1.756598213, 1.019701072), `_E10.4` = c(1.912254794, 
0.647681721, 0.840757508, 0.414669983, 0.49400961, 0.501421816, 
0.461123723, 0.568477247, 1.756598213, 1.576577576), `_E10.5` = c(0.429335115, 
0.647681721, 1.143509029, 0.336512868, 0.49400961, 0.82434125, 
0.461123723, 0.568477247, 1.756598213, 0.639407175), `_E100.1` = c(0.567579678, 
0.780423094, 1.739967261, 0.282217304, 0.784904687, 0.319146371, 
0.585056235, 0.596494912, 3.545358563, 0.899595619)), row.names = c(NA, 
-10L), class = c("data.table", "data.frame"))

#大数据集

1) download to your directory from https://jmp.sh/iC6WOzw
2) DT_large <- read_csv("DT_large.csv")

#我的功能

my_dataset$new_variable <- ifelse(my_dataset$old_variable >quantile(
my_dataset$old_variable,probs=0.80),quantile(
my_dataset$old_variable,probs=0.80),my_dataset$old_variable)

#my 函数应用于我的小数据集

//this perfectly delivers the desired output in seconds
DT_small %>% mutate_at(vars(matches("_E")),
                 funs(ifelse(
         DT_small$.>quantile(
                 DT_small$.,probs=0.80),quantile(
                 DT_small$.,probs=0.80),DT_small$.)))

#my 函数应用于我的大型数据集

//this takes several hours to deliver the desired output
DT_large %>% mutate_at(vars(matches("_E")),
                 funs(ifelse(
         DT_large$.>quantile(
                 DT_large$.,probs=0.80),quantile(
                 DT_large$.,probs=0.80),DT_large$.)))

提前感谢您的帮助。

【问题讨论】:

    标签: r performance function datatable tidyr


    【解决方案1】:

    您可以通过以下方式获得相当大的加速:1.) 计算一次分位数,2.) 将新的更简洁的函数应用于列。

    在我的机器上,这种方法大约快 15 倍。

    library(dplyr)
    library(microbenchmark)
    
    dplyr_res <- DT_small %>% mutate_at(vars(matches("_E")),
                           funs(ifelse(
                             DT_small$.>quantile(
                               DT_small$.,probs=0.80),quantile(
                                 DT_small$.,probs=0.80),DT_small$.)))
    
    fun_col <- function(col) { 
      m <- quantile(col, .8) # compute once
      ifelse(col > m, m, col)
    }
    
    sapply_res <- sapply(DT_small[,2:ncol(DT_small)], fun_col)
    
    
    dplyr_res %>% dplyr::select(-.id) == sapply_res
    #>       _E1.1 _E1.2 _E1.3 _E1.4 _E1.5 _E10.1 _E10.2 _E10.3 _E10.4 _E10.5
    #>  [1,]  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE
    #>  [2,]  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE
    #>  [3,]  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE
    #>  [4,]  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE
    #>  [5,]  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE
    #>  [6,]  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE
    #>  [7,]  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE
    #>  [8,]  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE
    #>  [9,]  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE
    #> [10,]  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE
    #>       _E100.1
    #>  [1,]    TRUE
    #>  [2,]    TRUE
    #>  [3,]    TRUE
    #>  [4,]    TRUE
    #>  [5,]    TRUE
    #>  [6,]    TRUE
    #>  [7,]    TRUE
    #>  [8,]    TRUE
    #>  [9,]    TRUE
    #> [10,]    TRUE
    
    
    
    microbenchmark(dplyr_res = DT_small %>% mutate_at(vars(matches("_E")),
                                                      funs(ifelse(
                                                        DT_small$.>quantile(
                                                          DT_small$.,probs=0.50),quantile(
                                                            DT_small$.,probs=0.50),DT_small$.))),
                   sapply_res = sapply(DT_small[,2:ncol(DT_small)], fun_col))
    #> Unit: milliseconds
    #>        expr       min        lq      mean    median        uq       max
    #>   dplyr_res 12.372519 12.668833 13.577804 12.856150 13.553805 60.220232
    #>  sapply_res  1.808413  1.850595  1.966174  1.874696  1.911037  3.441024
    #>  neval cld
    #>    100   b
    #>    100  a
    

    节省重新计算可能在这里做了很多工作。我没有明确测试sapply() 是否比mutate_at 快。

    并行运行的快速示例(只有在多列时才值得)

    parallel::mcmapply(fun_col, DT_small %>% select(-.id))
    

    取决于是否安装了并行包。

    【讨论】:

    • 谢谢,@gfgm。我的目标不是计算中位数。刚刚将问题编辑为我实际在做什么。
    • 好的,如果你切换到我的方法并用quantile(x, .8) 交换中值,它仍然快 7-8 倍。将编辑我的答案
    • @Krantz 完成。我没看你的大数据有多大。如果它有很多列,您可能需要考虑将fun_col() 并行应用于列。
    • 只做sapply(DT_small %&gt;% select(matches("_E")), fun_col)
    • 您需要将输出分配给要编辑的变量,例如DT_small[, grepl("_E", names(DT_small))] &lt;- sapply(...)。这将改变现有 data.frame 列的值。
    猜你喜欢
    • 1970-01-01
    • 2015-02-18
    • 2019-04-21
    • 2018-11-22
    • 2022-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-05
    相关资源
    最近更新 更多