【问题标题】:Is there an R function for returning sorted indexes of any values of a vector?是否有用于返回向量任何值的排序索引的 R 函数?
【发布时间】:2019-08-18 13:00:58
【问题描述】:

我对 R data.table 不流利,如果能帮助解决以下问题,我们将不胜感激! 我有带有数值列的大 data.table(~1000000 行),我想输出一个相同维度的 data.table,其中包含每行值的排序索引位置。

一个简短的例子:

-输入:

dt = data.frame(ack = 1:7)

dt$A1 = c( 1,    6,  9,  10,  3,   5, NA)
dt$A2 = c( 25,  12, 30,  10, 50,   1, 30)
dt$A3 = c( 100, 63, 91, 110,  1,   4, 10)
dt$A4 = c( 51,  65,  2,   1,  0, 200,  1)

第一行:1 (1) 2) 3) 4), (1, 25, 100, 51) 的行排序索引位置为 (1, 2, 4, 3),输出应为:

dt$PosA1 = c(1, ...
dt$PosA2 = c(2, ...
dt$PosA3 = c(4, ...
dt$PosA4 = c(3, ...

第三行:2 (1) 2) 3) 4) ,必须输出:

dt$PosA1 = c( 1,1,2,...)
dt$PosA2 = c( 2,2,3,...)
dt$PosA3 = c( 4,3,4,...)
dt$PosA4 = c( 3,4,1,...)

输出是输入数据的同一维度。表中按行填充排序索引的值。

dt$PosA1 = c( 1, 1, 2, 2, 3, 1, NA)
dt$PosA2 = c( 2, 2, 3, 3, 4, 2, 3)
dt$PosA3 = c( 4, 3, 4, 4, 2, 2, 2)
dt$PosA4 = c( 3, 4, 1, 1, 1, 4, 1)

我想也许是这样的?

library(data.table)
setDT(dt)

# pseudocode
dt[, PosA1 := rowPosition(.SD, 1, na.rm=T),
     PosA2 := rowPosition(.SD, 2, na.rm=T),
     PosA3 := rowPosition(.SD, 3, na.rm=T),
     PosA4 := rowPosition(.SD, 4, na.rm=T),
     .SDcols=c(A1, A2, A3, A4)]

我不确定语法,我错过了 rowPosition 函数。是否有任何功能可以做到这一点? (我在这里将其命名为 rowPosition)

编写一个高效的代码或解决问题的另一种方法会很有帮助!

问候。

【问题讨论】:

    标签: r function sorting data.table row


    【解决方案1】:

    您可以转换为长格式并使用rank。或者,由于您使用的是 data.table,frank:

    library(data.table)
    setDT(dt)
    melt(dt, id="ack")[, f := frank(value, na.last="keep", ties.method="dense"), by=ack][, 
      dcast(.SD, ack ~ variable, value.var="f")]
    
       ack A1 A2 A3 A4
    1:   1  1  2  4  3
    2:   2  1  2  3  4
    3:   3  2  3  4  1
    4:   4  2  2  3  1
    5:   5  3  4  2  1
    6:   6  3  1  2  4
    7:   7 NA  3  2  1
    

    melt 切换到长格式;而dcast 转换回宽格式。

    【讨论】:

    • 谢谢@Frank,但我遇到错误:Error in melt.data.table(dt, id = "ack") : One or more values in 'id.vars' is invalid.
    • @Pascal 您需要创建一个行 ID 列,例如 dt[, ack := .I]dt$ack <- seq_len(nrow(dt))。我在编辑后使用您帖子中的代码,以便它可以复制粘贴。你可以看看上面,看看我的意思。当然,你不需要把它命名为 ack :)
    • 但是如果我在我的 data.table (811000 x 16) 上使用 Sys.time() 并在 4 Core I5 vPro 8th Gen 16Go RAM 上花费大约 800 万。有没有办法优化这个持续时间或者我应该认为这是一个很好的计数?
    • @Pascal 我同意——这种事情的 8 分钟似乎太长了。您可以等待进一步的答案。希望其他人有更好的想法并在这里发布。
    • 非常感谢这个解决方案!我会拿很多咖啡杯等待更好的:)!
    【解决方案2】:

    由于您正在寻找速度,您可能需要考虑使用 Rcpp。可以在 nrussell's adapted version of René Richter's code 中找到负责 NA 和 tie 的 Rcpp rank

    nr <- 811e3
    nc <- 16
    DT <- as.data.table(matrix(sample(c(1:200, NA), nr*nc, replace=TRUE), nrow=nr))[, 
        ack := .I]
    
    #assuming that you have saved nrussell code in avg_rank.cpp
    library(Rcpp)
    system.time(sourceCpp("rcpp/avg_rank.cpp"))
    #   user  system elapsed 
    #   0.00    0.13    6.21 
    
    nruss_rcpp <- function() {
        DT[, as.list(avg_rank(unlist(.SD))), by=ack]
    }
    
    data.table.frank <- function() {
        melt(DT, id="ack")[, f := frank(value, na.last="keep", ties.method="dense"), by=ack][, 
            dcast(.SD, ack ~ variable, value.var="f")]
    }
    
    
    library(microbenchmark)
    microbenchmark(nruss_rcpp(), data.table.frank(), times=3L)
    

    时间安排:

    Unit: seconds
                   expr       min        lq     mean   median        uq       max neval cld
           nruss_rcpp()  10.33032  10.33251  10.3697  10.3347  10.38939  10.44408     3  a 
     data.table.frank() 610.44869 612.82685 613.9362 615.2050 615.68001 616.15501     3   b
    

    编辑:寻址 cmets

    1) 使用按引用更新为排名列设置列名

    DT[, (paste0("Rank", 1L:nc)) := as.list(avg_rank(unlist(.SD))), by=ack]
    

    2) 保持 NA 不变

    选项 A)avg_rank 获取输出后,在 R 中更改为 NA:

    for (j in 1:nc) {
        DT[is.na(get(paste0("V", j))), (paste0("Rank", j)) := NA_real_]
    }
    

    选项 B) 修改 Rcpp 中的avg_rank 代码如下:

    Rcpp::NumericVector avg_rank(Rcpp::NumericVector x)
    {
        R_xlen_t sz = x.size();
        Rcpp::IntegerVector w = Rcpp::seq(0, sz - 1);
        std::sort(w.begin(), w.end(), Comparator(x));
    
        Rcpp::NumericVector r = Rcpp::no_init_vector(sz);
        for (R_xlen_t n, i = 0; i < sz; i += n) {
            n = 1;
            while (i + n < sz && x[w[i]] == x[w[i + n]]) ++n;
            for (R_xlen_t k = 0; k < n; k++) {
                if (Rcpp::traits::is_na<REALSXP>(x[w[i + k]])) {  #additional code
                    r[w[i + k]] = NA_REAL;                        #additional code
                } else {
                    r[w[i + k]] = i + (n + 1) / 2.;
                }
            }
        }
    
        return r;
    }
    

    【讨论】:

    • 你好@chinsoon12,应该很棒,但我现在不知道如何从我的 Rstudio envt 获得 avg_rank(库(Rcpp)不够,我不知道如何``` `#assuming that you have saved nrussell code in avg_rank.cpp.
    • 对不起,我对 R 的低级知识:(
    • 我红色 TFM,安装了 Rtool 并获取 avg_rank.cpp 并再次启动,然后...... Greaaaaaat !!! 20 秒而不是 800 万!!!!如果我可以滥用。我希望 NA 值保持 NA 并保留列名称而不是 V1...VN。非常感谢!!!!!!
    • 你在几个小时内就完成了。最后两个问题对你来说毫无意义。
    • :)) 谢谢也鼓励我努力阅读。我解决了“列”问题(dt[, (cols) = ....]),但目前检查和修改 nrussel 代码对我来说太难了。所以我可以四处寻找一种方法来比较结果表和原始值的值,如果不是 NA 则打印结果值,否则 NA。但明智的做法是,在一次调用中,为 avg_rank() 提供一个参数,例如 na.last = "keep" 以计算该异常)。
    猜你喜欢
    • 2021-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-13
    • 2015-07-06
    • 1970-01-01
    • 2023-03-13
    相关资源
    最近更新 更多