【问题标题】:Why do I get a " Invalid .internal.selfref detected" warning (but no output) even if I am not using list(),key<-, names<-, or attr<-?为什么即使我没有使用 list()、key<-、names<- 或 attr<-,我也会收到“检测到无效的 .internal.selfref”警告(但没有输出)?
【发布时间】:2019-12-02 15:54:59
【问题描述】:

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

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

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

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

警告信息: 在 [.data.table(x, , :=(Sex, stringr::str_to_title(sex))) 中: 通过获取 data.table 的(浅)副本检测并修复无效的 .internal.selfref ,以便 := 可以通过引用添加此新列。在较早的时候,此 data.table 已由 R 复制(或使用 structure() 或类似方法手动创建)。避免 names

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)) 显然是错误的......

根据要求/为了让 cmets 中的讨论更清楚,我展示了我的代码如何产生问题

    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. 

【问题讨论】:

  • 罪魁祸首似乎是magrittr。如果您只是执行setDT(df),这将按预期工作。
  • 如果您查看`%&gt;%` 的源代码,您会发现很多函数都可以很好地解决此类问题。
  • 谢谢。但我需要 Magrittr,因为我在实际应用程序中不需要通过另一个函数设置 df。 IE。 “myotherfunction”返回 df。但它需要是 "myotherfunction %>% setDT() 或 setDT(myotherfunction)。
  • 但是请点赞。这是一个写得很好的问题,有一个很好的可重复的例子。
  • @Roland 不确定是否是同一个问题,但我遇到了相关问题 github.com/Rdatatable/data.table/issues/1628 链接到 stackoverflow.com/a/26072152,其中 Arun 在 2014 年以“到目前为止的想法是使用 @987654333 @ 在将其提供给函数之前将其转换为 data.tables。但我希望解决这些情况"

标签: r data.table


【解决方案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 没有足够的“pre-allocated”空间容纳额外的列,即使这种解决方法也会失败

data.table 接口基于传递 data.frame 或 data.table 的名称/符号,而不是值(这是 get 提供的),因为 explained by Arun 其中一个 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 &lt;- data.frame(...)中的assignment target的名字,貌似没有好的选择。或许可以看到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 代码不知道对象的名称,那么您只是设计得很糟糕。我想不出需要任意对象名称的正当理由。
  • 亲爱的弗兰克,非常感谢,我会检查一下。亲爱的 Roland,如果我对 28 个数据集 (x) 执行 prepare(x) 命令,我并不总是想输入数据集的名称。这就是为什么我认为我需要任意对象名称的原因。我真的很想知道为什么这对您来说如此“无关紧要”?
  • 弗兰克,好吧,整个“df_nm =”df”# 以某种方式得到这个......希望不是通过命令历史的正则表达式”部分之后的#令人困惑,因为我从未遇到过任何其他方式得到这个东西。但除此之外,有趣的方法。我仍然需要更多时间来完全理解代码。我对 R 很陌生。
猜你喜欢
  • 2012-05-25
  • 1970-01-01
  • 2017-06-24
  • 1970-01-01
  • 1970-01-01
  • 2019-11-22
  • 2013-06-13
  • 2023-02-17
  • 2018-04-10
相关资源
最近更新 更多