R函数将两个数据表中的字符列转换为数字并保持每个数据表中的编号一致

R Function to convert character column in two data tables to numeric and maintain consistent numbering in each

我在机器学习情况下出现了这个问题,其中有一组初始数据(称为 dtrain)和后续的一组相似数据(称为 dtest),我希望首先分解一列 class 字符,然后转换为数字。我希望将字符串分解为数字映射在两个数据 table 中保持一致。由于我会首先遇到 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$ 所需的所有级别之后颜色.

我能够编写一个函数,在给定两个数据 tables 和目标列的情况下适当地构建级别。

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"

现在我想要一个我可以随意应用的函数,它将列转换为数字并保持从第一个 table 到第二个的级别顺序。这是我的天真代码,它使用 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))
}

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

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.table 列上使用 <-,阅读有关 通过引用更新 := 运算符。

可能有更简单的方法实现它,但至少它有效 - 假设我理解正确。
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 class 称为 IM(代表 身份管理)可作为anchormodeling 包的一部分。
不确定,但对于大型数据集,使用 lookup 作为 data.table 而不是命名向量可能会更快,并且在 join 上执行 更新而不是当前 colors := lookup[colors] .