data.table
如果变量名称存储在字符向量中,如何在a中引用变量?例如,这适用于data.frame
:
df <- data.frame(col1 = 1:3)
colname <- "col1"
df[colname] <- 4:6
df
# col1
# 1 4
# 2 5
# 3 6
如何使用或不使用:=
符号对data.table执行相同的操作?显而易见的事情是dt[ , list(colname)]
行不通的(我也没想到)。
Answers:
以编程方式选择变量的两种方法:
with = FALSE
:
DT = data.table(col1 = 1:3)
colname = "col1"
DT[, colname, with = FALSE]
# col1
# 1: 1
# 2: 2
# 3: 3
'dot dot'(..
)前缀:
DT[, ..colname]
# col1
# 1: 1
# 2: 2
# 3: 3
有关“点点”(..
)表示法的进一步说明,请参见1.10.2中的新功能(帮助文本中目前未对此功能进行说明)。
要分配给变量,请将LHS:=
括在括号中:
DT[, (colname) := 4:6]
# col1
# 1: 4
# 2: 5
# 3: 6
后者称为列plonk,因为您通过引用替换了整个列向量。如果存在子集i
,它将通过引用进行子分配。周围的括号(colname)
是CRAN十月版本v1.9.4引入了一个速记2014年这里是新闻项目:
现在,在所有情况下都不建议使用
with = FALSE
with:=
,因为:=
在一段时间内首选将LHS加上括号。
colVar = "col1" DT[, colVar := 1, with = FALSE] # deprecated, still works silently DT[, (colVar) := 1] # please change to this DT[, c("col1", "col2") := 1] # no change DT[, 2:4 := 1] # no change DT[, c("col1","col2") := list(sum(a), mean(b)] # no change DT[, `:=`(...), by = ...] # no change
另请参阅中的“详细信息”部分?`:=`
:
DT[i, (colnamevector) := value]
# [...] The parens are enough to stop the LHS being a symbol
为了回答评论中的其他问题,这是一种方法(照常,有很多方法):
DT[, colname := cumsum(get(colname)), with = FALSE]
# col1
# 1: 4
# 2: 9
# 3: 15
或者,你可能会发现更容易阅读,编写和调试只是eval
一个paste
类似于构建一个动态的SQL语句发送到服务器:
expr = paste0("DT[,",colname,":=cumsum(",colname,")]")
expr
# [1] "DT[,col1:=cumsum(col1)]"
eval(parse(text=expr))
# col1
# 1: 4
# 2: 13
# 3: 28
如果您经常这样做,则可以定义一个辅助函数EVAL
:
EVAL = function(...)eval(parse(text=paste0(...)),envir=parent.frame(2))
EVAL("DT[,",colname,":=cumsum(",colname,")]")
# col1
# 1: 4
# 2: 17
# 3: 45
既然data.table
1.8.2会自动优化j
效率,那么最好使用该eval
方法。例如get()
,输入j
阻止进行某些优化。
或者,有set()
。的低开销,函数形式:=
,在这里很好。请参阅?set
。
set(DT, j = colname, value = cumsum(DT[[colname]]))
DT
# col1
# 1: 4
# 2: 21
# 3: 66
.
,或..
避免任何潜在的掩盖,如果DT
将来以后确实包含该符号作为列名(并遵循列名不以开头的约定.
)。有一些功能要求可以使其更加健壮地解决诸如添加.()
和等问题..()
。
fn$
gsubfn包中的准Perl类型的字符串插值 来提高EVAL解决方案的可读性:library(gsubfn); fn$EVAL( "DT[,$colname:=cumsum($colname)]" )
。
*这并不是真正的答案,但我没有足够的街头信誉来发表评论:/
无论如何,对于任何想在数据表中实际创建一个新列且名称存储在变量中的人来说,我都有以下工作。我不知道它的表现。有什么改进建议吗?是否可以安全地假设一个无名的新列将始终被命名为V1?
colname <- as.name("users")
# Google Analytics query is run with chosen metric and resulting data is assigned to DT
DT2 <- DT[, sum(eval(colname, .SD)), by = country]
setnames(DT2, "V1", as.character(colname))
注意,我可以在sum()中引用它,但是似乎无法在同一步骤中对其进行分配。顺便说一句,我需要这样做的原因是名称,将基于Shiny应用程序中的用户输入。
V1
是新名称是不安全的。例如,如果您使用阅读csv,fread
并且有一个未命名的列,它将具有V1
名称(并read.csv
给出X
)。因此,您的表可能已经有一个V1
。也许是通过names(DT)[length(names(DT))]
对于多个列,一个函数应用于列值。
从函数更新值时,RHS必须是一个列表对象,因此使用.SD
with循环lapply
可以解决问题。
下面的示例将整数列转换为数字列
a1 <- data.table(a=1:5, b=6:10, c1=letters[1:5])
sapply(a1, class) # show classes of columns
# a b c1
# "integer" "integer" "character"
# column name character vector
nm <- c("a", "b")
# Convert columns a and b to numeric type
a1[, j = (nm) := lapply(.SD, as.numeric ), .SDcols = nm ]
sapply(a1, class)
# a b c1
# "numeric" "numeric" "character"
library(data.table)
x <- data.table(this=1:2,that=1:2,whatever=1:2)
# === explicit call
x[, .(that, whatever)]
x[, c('that', 'whatever')]
# === indirect via variable
# ... direct assignment
mycols <- c('that','whatever')
# ... same as result of a function call
mycols <- grep('a', colnames(x), value=TRUE)
x[, ..mycols]
x[, .SD, .SDcols=mycols]
# === direct 1-liner usage
x[, .SD, .SDcols=c('that','whatever')]
x[, .SD, .SDcols=grep('a', colnames(x), value=TRUE)]
全部产生
that whatever
1: 1 1
2: 2 2
我找到了.SDcols
最优雅的方式。
列名<-as.name(“ COL_NAME”)
DT2 <-DT [,list(COL_SUM = sum(eval(colname,.SD))),by = c(group)]