r - 即使我没有使用 list()、key<-、names<- 或 attr<-,为什么会收到 "Invalid .internal.selfref detected"警告(但没有输出)?

标签 r data.table

在一个新的用户创建的函数中,我喜欢做一些 data.table 转换,特别是我喜欢使用 ':=' 命令创建一个新列。

假设我想创建一个名为 Sex 的新列,该列在我的示例 data.frame df 中将 df$sex 列的第一个字母大写。

我的准备函数的输出应该是一个 data.table,其名称与以前相同,但带有附加的“大写”列。

我尝试了几种方法来循环 data.table。但是我总是收到以下警告(并且没有正确的输出):

Warning message: In [.data.table(x, , :=(Sex, stringr::str_to_title(sex))) : Invalid .internal.selfref detected and fixed by taking a (shallow) copy of the data.table so that := can add this new column by reference. At an earlier point, this data.table has been copied by R (or was created manually using structure() or similar). Avoid names<- and attr<- which in R currently (and oddly) may copy the whole data.table. Use set* syntax instead to avoid copying: ?set, ?setnames and ?setattr. If this message doesn't help, please report your use case to the data.table issue tracker so the root cause can be fixed or this message improved.

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. 

最佳答案

按照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根据我对文档的阅读,仅在交互式使用中有效
  • 在人工输入(交互式输入代码)上使用正则表达式既复杂又危险
  • 如果 data.table x 即使这个解决方法也会失败传递给 prepit 的“pre-allocated”空间不足以容纳额外的列

data.table 接口(interface)基于传递 data.frame 或 data.table 的名称/符号,而不是值(这是 get 提供的),如 explained by Arun data.table 作者之一。请注意,该地址也不能传递。 z$address很快就无法匹配 address(df)在上面的所有示例中。

<小时/>

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.

一个想法:

# 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(...)中赋值目标的名称,看起来没有什么好的选择。也许看到How do I access the name of the variable assigned to the result of a function within the function?Get name of x when defining `(<-` operator

关于r - 即使我没有使用 list()、key<-、names<- 或 attr<-,为什么会收到 "Invalid .internal.selfref detected"警告(但没有输出)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57183861/

相关文章:

r - 样本回归,x = 月,巨大的带宽

r - ggplot2 分面标签中的双重不等式

r - 将 data.table 中变量的唯一字符串与 data.table 中另一个变量的重复值连接起来

r - 有效地创建变量,指示日期变量是否先于事件(按组)

r - 使用 is.null() 有条件地子集 r data.table

r - R 中日期向量的核密度估计

R Corrplot : italicize Y-axis labels

r - CRAN检查警告:依赖R版本 '3.4.3'而不是patchlevel 0

r - 删除空 data.table 中的因子列时出错

r - 为什么 data.table 行索引上的 for 循环比 data.frame 慢?