首页 文章

data.table:为什么不能总是直接传递列名?

提问于
浏览
14

开始使用 data.table 包(作者/维护者:Matt Dowle) . 很棒的套餐 . 我喜欢我可以写 dt[, x1] 而不是 dt[, dt$x1]df["x1"] ,对于data.table dt ,列名 x1 ,data.frame df . 能够直接传递列名是data.table的一个有吸引力的特性 . 但是在列名称周围省略引号(写 x1 而不是 "x1" )并不总是可行的 . 为什么?

编程问题:是否有任何理由不能始终将列名向量直接传递给data.table或包提供的辅助函数?例如,已经为data.table包重写了 subsetmergemelt 函数,但是 subset 可以直接处理列名, mergemelt 不能(见下文) .

澄清一下,我的问题不是何时或如何,而是为什么 . 有很好的相关讨论,非常有用的提示,例如Select / assign to data.table variables which names are stored in a character vectorr - passing variables as data.table column names . 有了这些答案和一些试验和错误,我能够找到报价/不引用区别的方法 . 我的问题是为什么目前不能总是在列名称周围省略引号:它有设计吗?这是一个过渡性的情况吗?有编程困难吗?

下面,为了清楚起见,我举一些例子并对这些例子进行编号

# load the package
library("data.table") # because I cannot do install.packages(data.table)!!

(i)

# make a data.table
set.seed(1)
dt <- data.table(id = 1:5, x1 = 1:5, x2 = 5:1, x3 = round(runif(5, 1, 5), 0), key = "id")

我可以用 id = 1:10"id" = 1:10 定义data.table,但我必须用 key = "id" 定义键,因为 key = id 不起作用:

dt <- data.table(id = 1:5, x1 = 1:5, x2 = 5:1, x3 = round(runif(5, 1, 5), 0), key = id)
##Error in data.table(id = 1:5, x1 = 1:5, x2 = 5:1, x3 = round(runif(5,  : 
##  object 'id' not found

如果要在列名中查找密钥,那么'd think finding ' id'对于密钥来说应该相当容易吗?以编程方式声音是否允许删除 key 的RHS上的引号?

(ii)

我可以 subset 使用列向量或列名向量:

subset(dt, select = c(x1, x3))
##   x1 x3
##1:  1  2
##2:  2  2
##3:  3  3
##4:  4  5
##5:  5  2

subset(dt, select = c("x1", "x3"))
##   x1 x3
##1:  1  2
##2:  2  2
##3:  3  3
##4:  4  5
##5:  5  2

不错,灵活 .

(iii)

我可以 merge 使用列名矢量:

merge(dt, dt, by = c("x1", "x2"))
##       id x1 x2 x3
##1:  1  1  5  2
##2:  2  2  4  2
##3:  3  3  3  3
##4:  4  4  2  5
##5:  5  5  1  2

(愚蠢的例子是!)但不是列的向量:

merge(dt, dt, by = c(x1, x2))
##Error in merge.data.table(dt, dt, by = c(x1, x2)) : object 'x1' not found

有什么关于 merge 阻止它以 subset 的方式接受列向量?

(iv)

同样, melt 必须使用带引号的列名(或对应于列号的整数) .

帮助描述是特定的 melt 接受"character vectors",而 merge 的帮助只是"vectors of column names,",但显然 mergemelt 字符向量一样 .

(v)

j 参数的情况下,引用变量名称通常不是正确的方法:

# Good:
dt[, .(x1, x2)]
##   x1 x2
##1:  1  5
##2:  2  4
##3:  3  3
##4:  4  2
##5:  5  1

# Bad 
dt[, .("x1", "x2")]
##   V1 V2
##1: x1 x2
# This feature is well documented in the FAQs
# FAQ 2.3: "I'm using c() in the j and getting strange results."

注意读者根本不熟悉 data.tables.()list() 的简写, dt[, c(x1, x2)] 不太可能是这里所需的命令 - dt[i, j]j 参数非常需要列表 .

(vi)

但是,在 dt[i, j]j 参数内,"assignment by reference"运算符 := 的LHS有一个令人困惑的约定 .

如果LHS是单列,则可以不带引号传递 . 但是,如果它有多个列,则必须将它们作为引用列名称的向量传递 . 该手册仅说“列名称的向量”,但实验表明它们必须引用:

# Good:
dt[, c("x1", "x2") := NULL][]
##   id x3
##1:  1  2
##2:  2  2
##3:  3  3
##4:  4  5
##5:  5  2

# Bad:
dt[, c(x1, x2) := NULL]
##Error in eval(expr, envir, enclos) : object 'x1' not found

错误消息不是特别有启发性 . 但现在我记得常见问题解答的建议,"If 2 or more columns are required, use list() or .() instead."傻我, c(x1, x2) 无法正常工作,因为没有办法告诉 x1 结束和 x2 开始 . 但是, .(x1, x2) 可以工作,不是吗?

# Bad:
dt[, .(x1, x2) := NULL]
##Error in eval(expr, envir, enclos) : object 'x1' not found

不,考虑到所有事情, := 的LHS期望引用列名称的向量 . 手册应该更新,或者,如果可行的话, data.table 扩展到接受LHS上未加引号的列的列表 .

等一下 . 要删除多个列名,我可以将引用名称列表传递给LHS吗?不是 . 列表通常是可取的,但不是 := 的LHS . 错误消息很明确:

# Bad:
dt[, .("x1", "x2") := NULL][]
##Error in `[.data.table`(dt, , `:=`(.("x1", "x2"), NULL)) : 
##  LHS of := must be a symbol, or an atomic vector (column names or positions).

(vii)

dt[i]i 参数也用于接受未加引号的列,即"expression of column names"

dt[.(x1, x2)]
##   id x1 x2 x3 V2
##1:  1  1  5  2  5
##2:  2  2  4  2  4
##3:  3  3  3  3  3
##4:  4  4  2  5  2
##5:  5  5  1  2  1

请注意,如果想要将两列 x1x2 分配,则应该在 j 参数内完成,即 dt[,.(x1, x2)]

dt[.("x1", "x2")]
##Error in bmerge(i, x, leftcols, rightcols, io, xo, roll, rollends, nomatch,  : 
##  typeof x.id (integer) != typeof i.V1 (character)

dt[c(x1, x2)]
##id x1 x2 x3
## 1:  1  1  5  2
## 2:  2  2  4  2
## 3:  3  3  3  3
## 4:  4  4  2  5
## 5:  5  5  1  2
## 6:  5  5  1  2
## 7:  4  4  2  5
## 8:  3  3  3  3
## 9:  2  2  4  2
##10:  1  1  5  2

dt[c("x1", "x2")]
##Error in bmerge(i, x, leftcols, rightcols, io, xo, roll, rollends, nomatch,  : 
##  typeof x.id (integer) != typeof i.V1 (character)

我在这里展示了几种情况,其中列必须作为 x1"x1" 传递,并且两种情况都可以完成 . 这些差异可能会给像我这样的新用户造成混淆 . 我怀疑这两种方法共存的原因不止一个 . 如果有人可以澄清这个问题我会很感激,对于我的一些例子,如果不是所有的话 .

1 回答

  • 6

    (i),(iii)和(iv)听起来像功能请求(FR);见here(所以,是的,这部分是由于 data.table 尚未达到完全成熟) .

    至于(v)你说“ dt[, c(x1, x2)] 不太可能是这里所需的命令”,但实际上我已经看到了 cc 的使用情况就是我所追求的 . 像(v)这样的情况就是这样 [.data.tablewith 参数适用于 .

    在(vi)和其他地方,你建议"The manual only says 'a vector of column names', but experimentation suggests they must be quoted";但我认为这是毫不含糊的 . 列名称向量表示 character 向量, c(x1,x2) 不是,除非 x1x2 某处定义为 character 向量本身 . 您还可以在GitHub上添加FR文档 .

    在_(vii)之后我是'm not sure what you',但在 i 中,名称向量用于连接或键控子集(也是连接形式);见vignette on fast subsetting .

相关问题