【问题标题】:Select values from different columns based on a variable containing column names [duplicate]根据包含列名的变量从不同列中选择值[重复]
【发布时间】:2015-10-23 19:37:41
【问题描述】:

我有一个这样的 data.table:

col1   col2   col3  new  
1       4     55    col1 
2       3     44    col2
3       34    35    col2
4       44    87    col3

我想填充另一列 matched_value,其中包含来自 new 列中给出的相应列名称的值:

col1   col2   col3  new    matched_value
1       4     55    col1        1
2       3     44    col2        3
3       34    35    col2        34
4       44    87    col3        87 

例如,在第一行中,new 的值为“col1”,因此matched_value 采用来自col1 的值,即 1。

如何在 R 中对非常大的 data.table 有效地执行此操作?

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    借口使用不起眼的.BY

    DT[, newval := .SD[[.BY[[1]]]], by=new]
    
       col1 col2 col3  new newval
    1:    1    4   55 col1      1
    2:    2    3   44 col2      3
    3:    3   34   35 col2     34
    4:    4   44   87 col3     87
    

    它是如何工作的。 这会根据new 中的字符串将数据分成组。每个组的字符串值存储在newname = .BY[[1]] 中。我们使用这个字符串通过.SD[[newname]]选择.SD的对应列。 .SD 代表 SData 的子集。

    替代方案。 get(.BY[[1]]) 应该可以代替 .SD[[.BY[[1]]]] 工作。根据@David 运行的基准测试,这两种方法同样快。

    【讨论】:

    • 可爱。非常快 看起来像其他语言。它是如何工作的?等待解释!谢谢!
    • +1 因为这个解决方案比我的dt[, matched_value := as.integer(get(new)), by=1:nrow(dt)] 解决方案更快。
    • @David 我认为get 可能和get(.BY[[1]]), by=new 一样快(也许更快?)
    • @Frank 出于好奇,我在 10^6 行上使用 system.time 测试了 get(.BY[[1]]).SD[[.BY[[1]]]]。两者在时间上无法区分。
    • @Frank 请注意,在 2020 年,.SD[[.BY[[1]]]] 似乎不再适用于指示找不到 .SD 的消息。幸运的是,get(.BY[[1]]) 继续工作!
    【解决方案2】:

    我们可以match'new' 列和数据集的列名得到列索引,cbind 和行索引(1:nrow(df1))并根据行提取数据集的对应元素/列索引。它可以分配给一个新列。

    df1$matched_value <- df1[-4][cbind(1:nrow(df1),match(df1$new, colnames(df1) ))]
    df1
    #  col1 col2 col3  new matched_value
    #1    1    4   55 col1             1
    #2    2    3   44 col2             3
    #3    3   34   35 col2            34
    #4    4   44   87 col3            87
    

    注意:如果 OP 有 data.table,一个选项是转换为 data.frame 或在子集化时使用 with=FALSE

     setDF(df1) #to convert to 'data.frame'.
    

    基准测试

    set.seed(45)
    df2 <- data.frame(col1= sample(1:9, 20e6, replace=TRUE),
    col2= sample(1:20, 20e6, replace=TRUE), 
    col3= sample(1:40, 20e6, replace=TRUE),
    col4=sample(1:30, 20e6, replace=TRUE),
    new= sample(paste0('col', 1:4), 20e6, replace=TRUE), stringsAsFactors=FALSE)
    system.time(df2$matched_value <- df2[-5][cbind(1:nrow(df2),match(df2$new, colnames(df2) ))])
    #   user  system elapsed 
    #  2.54    0.37    2.92 
    

    【讨论】:

    • 你能给出一个等效的数据表语法吗?另外,会不会很快?我有一个非常大的数据集,大约有 2000 万行。
    • @user3664020 行/列索引很快,但cbinding 可能是一个瓶颈。你有多少列?
    • 5 列。而且是数据表。
    • 也许使用fastmatch ?
    • 大概new 是一个因素,所以你真正需要的是transform(dat, val=dat[cbind(1:nrow(dat), new)])
    猜你喜欢
    • 2021-10-03
    • 1970-01-01
    • 2016-06-23
    • 2022-01-22
    • 2017-06-17
    • 1970-01-01
    • 2017-05-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多