在一个新的用户创建的函数中,我喜欢做一些data.table转换,尤其是我喜欢使用':='命令创建一个新列。
假设我想创建一个名为Sex的新列,该列在我的示例data.frame df中将df $ sex列的首字母大写。
我的prepare函数的输出应该是一个data.table,其名称与以前相同,但带有附加的“大写”列。
我尝试了几种遍历data.table的方法。但是,我总是收到以下警告(并且没有正确的输出):
警告信息: 在
[.data.table
(x,,:=
(Sex,stringr :: str_to_title(sex)))中: 通过获取data.table的(浅)副本来检测并修复了无效的.internal.selfref,以便:=可以通过引用添加此新列。早先,此data.table已由R复制(或使用structure()或类似方法手动创建)。避免使用name <-和attr <-,它们在R中当前(奇怪地)可能会复制整个data.table。请改用set *语法以避免复制:?set,?setnames和?setattr。如果此消息无济于事,请将您的用例报告给data.table问题跟踪器,以便可以解决根本原因或改进此消息。
library(data.table)
library(magrittr)
library(stringr)
df <- data.frame("age" = c(17, 04),
sex = c("m", "f"))
df %>% setDT()
is.data.table(df)
这是编写我的函数的最简单方法:
prepare1<-function(x){
x[,Sex:=stringr::str_to_title(sex)]
}
prepare1(df)
#--> WARNING. (as block quoted above)
prepare2<-function(x){
x[, `:=`(Sex, stringr::str_to_title(sex))]
}
prepare2(df)
#--> WARNING. . (as block quoted above)
prepare3<-function(x){
require(data.table)
y <-as.data.table(list(x))
y <- y[,Sex:=stringr::str_to_title(sex)]
x <<- y
}
prepare3(df)
最后一个版本不会引发警告,但是会创建一个名为x的新数据集。但是我想覆盖我放入函数中的数据集(如果我必须那样做的话)。
从:= help file中我也知道我可以使用set,但是我无法适应该命令。如果可以解决我的问题,我也很高兴获得帮助! set(x, i = NULL, Sex, str_to_title(sex))
显然是错误的...
根据要求/使注释中的讨论更加清楚,我演示了我的代码是如何产生问题的
library(data.table)
library(stringr)
df <- data.frame("age" = c(17, 04),
sex = c("m", "f"))
GetLastAssigned <- function(match = "<- *data.frame",
remove = " *<-.*") {
f <- tempfile()
savehistory(f)
history <- readLines(f)
unlink(f)
match <- grep(match, history, value = TRUE)
get(sub(remove, "", match[length(match)]))
}
#ok, no need for magrittr
setDT(GetLastAssigned())
#check the last function worked
is.data.table(df)
prepare1<-function(x){
x[,Sex:=stringr::str_to_title(sex)]
}
prepare1(GetLastAssigned())
# I get a warning and it does not work.
prepare1(df)
# I get a warning and it does not work, either.
#If I manually type setDT(df) everything works fine but I cannot type the "right" dfs at all the places where I need to do this transformation.
答案 0 :(得分:1)
OP的解决方法:
library(data.table)
library(stringr)
GetLastAssigned2 <- function(match = "<- *data.frame", remove = " *<-.*") {
f <- tempfile()
savehistory(f)
history <- readLines(f)
unlink(f)
match <- grep(match, history, value = TRUE)
nm <- sub(remove, "", match[length(match)])
list(nm = as.name(nm), addr = address(get(nm)))
}
prepit <- function(x){
x[,Sex:=stringr::str_to_title(sex)]
}
# usage
df <- data.frame("age" = c(17, 04), sex = c("m", "f"))
z <- GetLastAssigned2()
eval(substitute(setDT(x), list(x=z$nm)))
str(df) # it seemingly works, since there is a selfref
# usage 2
df <- data.frame("age" = c(17, 04), sex = c("m", "f"))
setDT(df)
prepit(df)
str(df) # works
# usage 3
df <- data.frame("age" = c(17, 04), sex = c("m", "f"))
z <- GetLastAssigned2()
eval(substitute(setDT(x), list(x=z$nm)))
eval(substitute(prepit(x), list(x=z$nm)))
str(df) # works
一些重大警告:
savehistory
仅在交互使用中有效x
的空间不足以容纳多余的列,则此解决方法也将失败 data.table接口基于传递data.frame或data.table的名称/符号,而不是作为documentation之一的值(get
提供的值) data.table作者。请注意,该地址也不能传递。在以上所有示例中,z$address
很快都无法与address(df)
匹配。
如果我手动键入setDT(df),一切正常,但是无法在需要进行此转换的所有位置键入“正确的” dfs。
一个主意:
# helper to compose expressions
subit = function(cmd, df_nm)
do.call("substitute", list(cmd, list(x=as.name(df_nm))))
# list of expressions with x where the df name belongs
my_cmds = list(
setDT = quote(setDT(x)),
prepit = quote(x[,Sex:=stringr::str_to_title(sex)])
)
# usage 4
df = data.frame("age" = c(17, 04), sex = c("m", "f"))
df_nm = "df" # somehow get this... hopefully not via regex of command history
eval(subit(my_cmds$setDT, df_nm))
eval(subit(my_cmds$prepit, df_nm))
# usage 5
df = data.frame("age" = c(17, 04), sex = c("m", "f"))
df_nm = "df"
for(ex in lapply(my_cmds, subit, df_nm = df_nm)) eval(ex)
我认为这与data.table的推荐编程用法更加一致。
也许可以通过将envir=
参数更改为eval()
来将其包装到函数中,但是我对此一无所知。
关于如何在nm <- data.frame(...)
中获得分配目标的名称,似乎没有好的选择。也许看到pre-allocated或explained by Arun