【问题标题】:R Function to convert character column in two data tables to numeric and maintain consistent numbering in eachR函数将两个数据表中的字符列转换为数字并在每个数据表中保持一致的编号
【发布时间】:2016-03-04 16:29:47
【问题描述】:

这个问题出现在机器学习情况下,其中有一组初始数据(称为 dtrain)和随后的一组相似数据(称为 dtest),我希望首先考虑一列类字符和然后转换为数字。我希望将字符串分解为数字映射以在两个数据表中保持一致。由于我会先遇到 dtrain,所以我只会看到数据的一个子集。当我遇到 dtest 时,我会想重用相同的级别并为新的唯一字符串添加新级别。

一个简单的例子:

dtrain <- data.table( colors = c('red','white','blue'))
dtest <- data.table( colors = c('green','white','red'))

factor(dtrain$colors)
[1] red   white blue 
Levels: blue red white

dtrain$colors <- as.numeric(factor(dtrain$colors))
dtrain$colors
[1] 2 3 1

color_levels <- c(c('blue','red','white'), c('green'))
dtest$colors <- as.numeric(factor(dtest$colors, color_levels))
dtest$colors
[1] 4 3 2

这里我们看到 white(3) 和 red(2) 在两者中是一致的,green(4) 在 dtest 中而不是 dtrain 中找到,因此在 dtrain$color 所需的所有级别之后得到一个级别。

我能够编写一个函数,该函数将根据两个数据表和目标列适当地构建级别。

ab.levels <- function( da, db, col_x ) {
  arguments <- as.list(match.call())
  ax <- sort(unique(eval(arguments$col_x, da)))
  bx <- unique(eval(arguments$col_x, db))
  return(c(ax, sort(setdiff(bx,ax))))
}

dtrain <- data.table( colors = c('red','white','blue'))
dtest <- data.table( colors = c('green','white','red'))

ab.levels( dtrain, dtest, colors)
[1] "blue"  "red"   "white" "green"

现在我想要一个可以随意应用的函数,它将列转换为数字并保持从第一个表到第二个表的级别顺序。这是我使用 ab.levels() 的幼稚代码,但不起作用:

ab.char.to.numeric <- function (da, db, col_x)
{
  col_levels <- ab.levels( da, db, col_x)
  da$col_x <- as.numeric(factor(da$col_x, col_levels))
  db$col_x <- as.numeric(factor(db$col_x, col_levels))
}

【问题讨论】:

    标签: r function parameters data.table


    【解决方案1】:

    不要在 data.table 列上使用&lt;-,请阅读按引用更新:= 运算符。

    可能有更简单的方法来实现它,但至少它有效 - 假设我理解正确。
    sk 代表代理键

    library(data.table)
    dtrain = data.table( colors = c('red','white','blue'))
    dtest = data.table( colors = c('green','white','red'))
    
    decode_to_sk = function(da, db, col_x){
        sk = unique(c(da[, unique(eval(as.name(col_x)))], db[, unique(eval(as.name(col_x)))]))
        lookup = setNames(seq_along(sk), sk)
        j.upd = call(":=", as.name(col_x), call("[", as.name("lookup"), as.name(col_x)))
        # j.upd builds call for `j` arg, here: colors := lookup[colors]
        da[, eval(j.upd)]
        db[, eval(j.upd)]
        return(TRUE)
    }
    print(dtrain)
    #   colors
    #1:    red
    #2:  white
    #3:   blue
    print(dtest)
    #   colors
    #1:  green
    #2:  white
    #3:    red
    decode_to_sk(dtrain, dtest, col_x = "colors")
    #[1] TRUE
    print(dtrain)
    #   colors
    #1:      1
    #2:      2
    #3:      3
    print(dtest)
    #   colors
    #1:      4
    #2:      2
    #3:      1
    

    如果您希望它用于更大的项目,您想查看我的 R6 类 IM(代表 身份管理),它是 anchormodeling 包的一部分。
    不确定,但对于大型数据集,使用 lookup 作为 data.table 而不是命名向量可能会更快,并且 update on join 而不是当前的colors := lookup[colors]

    【讨论】:

    • 'unique' 是否保证保持看起来的秩序?如果是这样,这符合所有要求。我并不真正理解使这一切都作为一个函数工作所必需的每个步骤的复杂性,但会更多地咀嚼它。谢谢。
    • 关于unique 是的,您可以轻松查看。唯一棘手的部分是准备j 中使用的未计算表达式,两次call
    【解决方案2】:

    首先,您的 ab.char.to.numeric 函数将仅返回最后一条语句的结果或明确指定的返回值。以下是我将如何解决这个问题:

    ab.char.to.numeric <- function (da, db, col_x)
    {
        require(data.table)
        stopifnot(is.data.table(da))
        stopifnot(is.data.table(dt))
    
        data <- rbindlist(list(da, dt))
        data$col_x <- as.numeric(as.factor(data$col_x))
        da <- data[1:nrow(da)]
        db <- data[(nrow(da) + 1):.N]]
    
        return list(da, db)
    }
    

    或者你可以一起跳过这个函数,只绑定、转换和拆分

    【讨论】:

    • 我应该澄清一下,我不希望有任何回报。我的期望是调用后 data.tables da 和 db 中的 col_x 将是数字的副作用。
    • 如果您指定 da 和 db 作为函数的参数,它们将在该函数的上下文(名称空间)中处理。这意味着这是两组不同的变量——一组在全局空间中,一组在函数中。您可以通过使您的函数搜索调用/父环境来改变父环境中的变量,但不建议这样做。如果您坚持,请不要在函数调用中指定 da & db。
    • 我认为你应该为读者分解db &lt;- data[(nrow(da) + 1):.N]]
    • 我对您在没有使用测试数据的情况下根据训练数据训练模型的情况感兴趣。测试数据在它知道“红色”、“白色”和“蓝色”后变得可用,但不知道“绿色”。模型在训练期间永远不会看到“绿色”,它只会看到对应于“蓝色”、“红色”、“白色”的 1、2、3。当 dtest 出现时,它引入了“绿色”……绿色不能是 1、2 或 3,因为这是模型从其他颜色中学到的。绿色必须是其他数字,例如 4。在上面的代码中,它将是“2”并与“红色”冲突。
    • data 表示两个数据表 da 和 db 相互堆叠。与 R 的股票 rbind 相比,data.table rbindlist 非常快。 db &lt;- data[(nrow(da) + 1):.N]] 只是获取组合 datta.table 的底部。 .N 返回 data[] 中的行数 - 最后一行的编号。希望这会有所帮助。
    猜你喜欢
    • 2020-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-21
    • 1970-01-01
    • 2018-06-27
    • 1970-01-01
    相关资源
    最近更新 更多