为什么即使我没有使用list(),key <-,names <-或attr <-,也收到“检测到无效的.internal.selfref”警告(但没有输出)?

时间:2019-07-24 13:15:17

标签: r data.table

在一个新的用户创建的函数中,我喜欢做一些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. 

1 个答案:

答案 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仅在交互使用中有效
  • 在人类输入上使用正则表达式(以交互方式键入的代码)既复杂又有风险
  • 如果传递给prepit的data.table 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-allocatedexplained by Arun