【问题标题】:Access data.table columns with strings使用字符串访问 data.table 列
【发布时间】:2015-08-14 06:27:58
【问题描述】:

对于一个很明显我通常在 Python/pandas 中工作的问题,我深表歉意,但我一直坚持这一点。如何使用字符串选择data.table 列?

dt$"string"
dt$as.name("string")
dt$get("string")

我确信这非常简单,但我不明白。非常感谢任何帮助!


------------- 已编辑添加 ----------

在下面的一些有用的 cmets 和提示之后,我想我已经缩小了问题的范围,并有了一个可重现的例子。考虑:

dt = data.table(ID = c("a","a","a","b","b","b"), col1=rnorm(6), col2=rnorm(6)*100)

假设我们要将col2 中的值分配给col1。正如我在下面了解到的,data.table 的语法是dt[,col1:=col2],简洁明了。当j 参数中的一个(或两个)变量是字符串时,问题就开始了。我发现了以下内容:

dt[, "col1":=col2] 按预期工作

dt[, "col1":="col2"] 按预期失败(尝试将字符 col2 分配给双向量 col1

dt[, "col1":=get("col2")] 按预期工作

dt[, get("col1")] 按预期返回col1

但是:dt[, get("col1"):=col2] 或任何其他分配失败。

一些上下文:这样做的原因是我在循环中构造字符串,以访问大量名为colname_colnumber的列,即我循环遍历colname和colnumber然后访问列paste0(colname,colnumber)。

【问题讨论】:

  • 顺便说一句,dt$"string" 应该可以正常工作,到底是什么错误?尽管 $ 的 RHS 表达式永远不会起作用,请参阅 this faq
  • 你说得有道理——我想我在循环内构造字符串时遇到了错误,所以使用了$paste(x,y)之类的东西,然后在提问之前忘记尝试不使用表达式!
  • 尚不清楚为什么 dt[,"col1":=get("col2")] 选项不足以满足您的目的,例如for (ii in 1:2) dt[,paste0("col",ii):=get("col2")] 似乎是你想要的。也许您需要稍微扩大示例的范围。
  • 点了 - 你提到的选项会起作用(这就是我要做的)。出现问题是因为我首先存储了构造的字符串,因为我也在其他地方使用它。如果我再做var<-paste0("col","1"),我将不得不再次依赖get() 方法,这是行不通的。因此,尽管您的建议在我的情况下有效,但我仍然想了解为什么 data.table 的行为会在简单的 get() 调用和带有分配的 get() 调用之间发生变化。
  • @Nilsgudat 这是一个公平的问题。我不在电脑前,但我的直觉告诉我,一旦你使用get,分配选项就不再可用,因为已经返回了一个向量。您可以将向量分配给列名,但不能将名称向量分配给向量(不加小心)

标签: r string indexing data.table


【解决方案1】:

您可以使用单括号将get() 用作j 参数:

library(data.table)
dt <- data.table(iris)
dt[, get("Species")]

结果:

[1] setosa     setosa     setosa     setosa     setosa     setosa .....

您也可以直接在双括号运算符内使用字符串,如下所示:

dt[["Species"]]

【讨论】:

  • get() 可以解决问题,但由于某种原因,它在赋值操作中不起作用。我究竟做错了什么?假设我有 var1 &lt;- "string1" 和 var2 &lt;- "string2", where my data table has columns named string1` 和 string2。 dt[, get(var1)] 和 dt[,get(var2)] 按预期工作,但如果我想使用 dt[,get(var1)] &lt;- dt[,get(var2)] 将第 2 列中的值分配给第 1 列,我会得到 Error in get(var1): object 'string1' not found。有什么想法吗?
  • 或者更简单:dt[, get(var1)] 有效,而dt[,get(var1)] &lt;- 0 抛出object not found 错误。
  • 您应该尽可能避免在data.table 中分配&lt;-。尝试dt[,(var1):=var2] 或dt[,(var1):=0] 作为您的另一个示例。我推荐starter vignettes 来获取一些非常易读的基础知识示例。
  • 该语法肯定更简洁,但仍然无法正常工作 - dt[,get(var1):=0] 会抛出相同的错误。我想不出任何理由可以通过简单调用访问列但在分配调用中不可用,但我一定会通读您链接的文档!
  • @NilsGudat 嗯,在这种情况下,我不知道该告诉你什么。如果在您“完成阅读”后问题仍然存在,请在您的帖子中编辑一个可重现的示例。我不确定您为什么要专注于使用 get,因为有几种替代方案应该可行,但也许需要更多上下文 - 如果是这样,请将其添加到您的帖子中。
【解决方案2】:

我会补充一点,如果你想要一堆列,你可能希望使用类似的东西:

dt[ , c("id", paste0("col", 1:10)), with = FALSE]

正如@Arun 在下面添加的,获取多列的其他选项是:

dt[ , mget(c("id", paste0("col", 1:5)))]

和

dt[ , .SD, .SDcols = c("id", paste0("col", 1:5))]

在最新版本的data.table(例如当前的 CRAN)中,您还可以使用“up-a-level”表示法,例如:

keep_cols = c('id', paste0('col', 1:5))
dt[ , ..keep_cols]

供参考,mget 好像很慢; .SDcols 最快,但与 with = FALSE 竞争;我个人认为所有这些在不同的情况下都是有用的/最自然的。

这是一个简单的基准:

NN <- 10000L
MM <- 100L
mm <- 10L

DT = data.table(id = 1:NN)
DT[ , paste0("col", 1:MM) := lapply(integer(MM), function(x) runif(NN))]

sdcols = function(...) DT[ , .SD, .SDcols = paste0("col", sample(MM, size = mm))]
m.get = function(...) DT[ , mget(paste0("col", sample(MM, size=mm)))]
withF = function(...) DT[ , paste0("col", sample(MM, size = mm)), with = FALSE]

library(microbenchmark)
microbenchmark(times=100L, sdcols(), m.get(), withF())
# Unit: microseconds
#      expr      min        lq      mean    median        uq      max neval cld
#  sdcols()  780.201  810.4350  865.3564  827.4970  853.4875 2354.577   100 a  
#   m.get() 2792.293 2864.1225 3052.3872 2899.9370 3031.9260 4831.963   100   c
#   withF()  897.822  927.7105 1005.3166  945.9495  981.0580 2600.445   100  b 

【讨论】:

  • @Arun 从来不知道mget。很棒的东西!
【解决方案3】:

您可以不使用 get 而是使用方括号进行赋值:

dt[, ("col1"):=col2]

代替:

dt[, get("col1"):=col2]

更多解释见:Select / assign to data.table variables which names are stored in a character vector

【讨论】:

    猜你喜欢
    • 2013-02-07
    • 2022-11-19
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-24
    • 1970-01-01
    • 2014-07-15
    相关资源
    最近更新 更多