29

我创建了小 data.table DT = data.table(a=1:2, a=1:2)

如果我使用names(DT) <- c("b","b")

我收到警告

In `names<-.data.table`(`*tmp*`, value = c("b", "b")) :
  The names(x)<-value syntax copies the whole table. This is due to <- in R itself. Please change to setnames(x,old,new) which does not copy and is faster. See help('setnames'). You can safely ignore this warning if it is inconvenient to change right now. Setting options(warn=2) turns this warning into an error, so you can then use traceback() to find and change your names<- calls.

但是如果我使用setnames(DT, names(DT), c("b","b"),那么我会得到错误

Error in setnames(DT, names(DT), c("b", "b")) : 
  Some duplicates exist in 'old': a

如果相同的示例与 data.frame 相比DT = data.frame(a=1:2, a=1:2)并使用, names(DT) <- c("b","b")那么我不会收到任何错误。

4

2 回答 2

35

不提供oldnew你就不会有问题。然而,这不是问题。在base::data.frame你不能有同名的列,所以......

#  What you actually get...
DT = data.frame(a=1:2, a=1:2); names(DT)
#[1] "a"   "a.1"

但似乎data.table你可以有同名的列......

DT = data.table(a=1:2, a=1:2); names(DT)
[1] "a" "a"

但是setnames会抛出一个错误,我猜是因为它不知道a在调用两列时它指的是哪一列adata.frame转到路由时不会出错,data.table因为您没有重复的列名。

首先,我会说不要创建具有相同名称的列,如果您打算以编程方式使用您的列,这是一件非常糟糕的事情data.table(但正如@MatthewDowle 在评论中指出的那样,这是为用户提供最大自由的设计选择中data.table)。

如果你需要这样做,那么setnames只使用给定的参数,当没有给出时old,它实际上将被视为new名称。new如果您传入old名称新名称的向量,则会找到旧名称并将其更改为相应的新名称(因此old,当与 3 个参数一起使用new时,长度必须相同)。将通过以下方式捕捉任何歧义:setnamessetnames

if (any(duplicated(old))) 
           stop("Some duplicates exist in 'old': ", paste(old[duplicated(old)],
                collapse = ","))
if (any(duplicated(names(x)))) 
           stop("'old' is character but there are duplicate column names: ", 
                paste(names(x)[duplicated(names(x))], collapse = ",")) 

just old提供setnames时,将重新分配名称 fromoldDTcolumn-wise using的列.Call(Csetcharvec, names(x), seq_along(names(x)), old),所以从第一个到最后...

DT = data.table(a=1:2, a=1:2)
setnames(DT, c("b","b") )
DT
#   b b
#1: 1 1
#2: 2 2

应要求从 Matthew 添加。里面有?setnames一些背景:

一般来说,使用列号而不是名称不是好的编程习惯。这就是为什么 setkey 和 setkeyv 只接受列名,以及为什么建议 setnames() 中的 old 为名称。如果您使用列号,那么如果在代码的其他地方进行了更改,那么随着时间的推移,错误(可能是无声的)会更容易地潜入您的代码;例如,如果您在几个月内添加、删除或重新排序列,则按列号设置的键将引用不同的列,可能会返回不正确的结果而没有警告。(类似的概念存在于 SQL 中,当需要一个健壮、可维护的系统时,“select * from ...”被认为是糟糕的编程风格。)如果您真的希望使用列号,这是可能的,但故意稍微难一点;例如,setkeyv(DT,colnames(DT)[1:2])。

[截至 2017 年 7 月,上述注释不再出现在 中?setnames,但该问题在常见问题解答顶部附近讨论,vignette('datatable-faq').]

所以想法setnames是很容易地按名称更改一列名称。

setnames(DT, "oldname", "newname")

如果"oldname"不是列名,或者您的意图有任何歧义(在现在的数据中,或者在您的同事更改源数据库或上游其他代码或将他们自己的数据传递给您的模块后的几个月后),那么data.table将为你抓住它。这实际上很难在基地中轻松setnames完成(包括安全检查)。

于 2013-09-12T09:51:11.887 回答
9

setnames可用于一次更改多个列名:

setnames(DT, old = c("oldname1", "oldname2", "oldname3"), new = c("newname1", "newname2", "newname3"))
于 2017-07-14T13:41:47.833 回答