如何在data.table中使用未知数量的键列

时间:2012-07-26 20:22:18

标签: r data.table

我想做与解释here相同的操作,即向data.table添加缺失的行。我面临的另一个困难是我希望关键列的数量,即用于自连接的那些行是灵活的。

这是一个小例子,它基本上重复了上面提到的链接:

df <- data.frame(fundID   = rep(letters[1:4], each=6),
                 cfType   = rep(c("D", "D", "T", "T", "R", "R"), times=4),
                 variable = rep(c(1,3), times=12),
                 value    = 1:24)
DT <- as.data.table(df)
idCols <- c("fundID", "cfType")
setkeyv(DT, c(idCols, "variable"))
DT[CJ(unique(df$fundID), unique(df$cfType), seq(from=min(variable), to=max(variable))), nomatch=NA]

困扰我的是最后一行。我希望idCols具有灵活性(例如,如果我在函数中使用它),所以我不想手动键入unique(df$fundID), unique(df$cfType)。但是,我没有找到任何解决方法。根据{{​​1}}的需要,我尝试自动将df的子集拆分为向量的尝试失败,错误消息 setkeyv中的错误(x,cols,verbose = verbose):列'V1 '是类型'列表',它不是(当前)允许作为键列类型。

CJ

我也试过自己构建表达式:

CJ(sapply(df[, idCols], unique))
CJ(unique(df[, idCols]))
CJ(as.vector(unique(df[, idCols])))
CJ(unique(DT[, idCols, with=FALSE]))

但后来我不知道如何使用str <- "" for (i in idCols) { str <- paste0(str, "unique(df$", i, "), ") } str <- paste0(str, "seq(from=min(variable), to=max(variable))") str [1] "unique(df$fundID), unique(df$cfType), seq(from=min(variable), to=max(variable))" 。这一切都失败了:

str

有没有人知道一个好的解决方法?

2 个答案:

答案 0 :(得分:4)

迈克尔的答案很棒。确实需要do.call以这种方式灵活地呼叫CJ,afaik。

要了解表达式构建方法并从代码开始,但删除df$部分(不需要而不是在链接答案中完成,因为iDT范围内进行评估{1}}):

str <- "" 
for (i in idCols) { 
  str <- paste0(str, "unique(", i, "), ") 
} 
str <- paste0(str, "seq(from=min(variable), to=max(variable))") 
str 
[1] "unique(fundID), unique(cfType), seq(from=min(variable), to=max(variable))" 

然后是:

expr <- parse(text=paste0("CJ(",str,")"))
DT[eval(expr),nomatch=NA]

或者动态地构建和评估整个查询:

eval(parse(text=paste0("DT[CJ(",str,"),nomatch=NA")))

如果这样做很多,那么创建自己的辅助函数可能是值得的:

E = function(...) eval(parse(text=paste0(...)))

将其减少为:

E("DT[CJ(",str,"),nomatch=NA")

答案 1 :(得分:3)

我从来没有使用过data.table包,所以请原谅我,如果我在这里错过了标记,但我想我已经得到了它。这里有很多事情要做。首先阅读do.call,它允许您以某种非传统方式评估任何函数,其中参数由提供的列表指定(其中列表中的每个元素在位置上与函数参数匹配,除非明确命名)。另请注意,我必须指定min(df$variable)而不是min(variable)。请阅读Hadley's page on scoping以了解此问题。

CJargs <- lapply(df[, idCols], unique)
names(CJargs) <- NULL
CJargs[[length(CJargs) +1]] <- seq(from=min(df$variable), to=max(df$variable))
DT[do.call("CJ", CJargs),nomatch=NA]