【问题标题】:Replace definition of built-in function in R?替换R中内置函数的定义?
【发布时间】:2012-10-05 14:46:42
【问题描述】:

“sparcl”包使用标准“stat”包中的“kmeans”函数。我想让它改用我自己的 kmeans++ 实现。

执行此操作的一种方法是编辑 sparcl 包本身中的代码。我宁愿避免这种情况,因为它会很混乱,而且我不确定如何在 R 中安装编辑后的代码。

不幸的是,超赋值运算符“

> kmeans <<- function(x) print("hi!")
Error: cannot change value of locked binding for 'kmeans'

“分配”也没有:

assign("kmeans",function(x) {print("HI THERE!"); return(FALSE)},pos="package:sparcl")
Error in assign("is.null", function(x) { : 
  cannot add bindings to a locked environment

那么编辑包代码是唯一的方法吗?

谢谢!

【问题讨论】:

  • 你能用trace这个吗?
  • 您想要替换旧版本而不是仅仅编写新版本并使用该版本的原因是什么?
  • 创建您自己的调用kmeans 的(导出的)函数版本并更改这些实例以便它们调用您自己的自定义函数不是更容易吗? (也许@Dason 也是这么说的……?)
  • 我投票支持@joran 的建议。如果您确实想打包函数,请创建自己的迷你包(可能从 stats 导入)而不是编辑 stats 包的源代码!
  • @joran 的建议并不完美,因为kmeans 在 sparcl 包的多个文件中的多个函数中被多次调用,所以我还必须复制和编辑所有这些。 ..但这就是我最初打算这样做的方式。

标签: r function package


【解决方案1】:

如果您确实想在交互式会话期间编辑函数的主体(而不是其参数),您可以使用trace(),如下所示:

trace("kmeans", edit=TRUE)

然后,在弹出的编辑器中,编辑正文,使其看起来像这样(例如):

function (x, centers, iter.max = 10, nstart = 1, algorithm = c("Hartigan-Wong", 
"Lloyd", "Forgy", "MacQueen")) 
{
    plot(rnorm(99), col = "red")
}

保存编辑好的函数定义,然后退出编辑器。

回到R命令行,可以查看编辑好的函数并试一试:

body(kmeans)  # To view the tracing code
kmeans()      # To use the edited function

最后,要恢复到未编辑的功能,只需执行untrace("kmeans")。 (我通常更喜欢使用trace()assignInNamespace() 和朋友,因为untrace() 可以很容易地撤消更改。)

【讨论】:

  • 谢谢,我不知道 trace()。虽然我认为它不能完全解决我的问题,因为 sparcl 包在不同功能中对 kmeans 进行了大量调用......
  • @martas 也许只是获取包的源代码,用一个好的文本编辑器更改kmeans 调用的所有实例不会花费太多时间,然后在本地重建包。我认为人们只是对涉及覆盖 stats 包代码本身的解决方案持怀疑态度,您以后可能会后悔。
  • trace() 编辑namespace:stats 中的函数,因此它将编辑任何调用它的函数看到的kmeans() 的版本。这不是你想要的,还是我很困惑? (不是相互排斥的可能性,我知道......)
  • 上帝之母,它(您的编辑)有效!谢谢你。我想这是可行的,因为 kmeans 不是显式导入的 sparcl 代码包的一部分,对吧?我仍然对 R 处理命名空间的方式一无所知...
  • @martas -- 哎呀。您可能希望将“接受”移至我的其他答案。 (我认为它与之前的答案有很大不同,所以我应该将其拆分。我会覆盖之前的答案 re: trace,但看起来它本身可能很有用。)
【解决方案2】:

进一步思考(并在重新阅读您的问题后),这里有一个简单的解决方案,应该适合您。

您所需要做的就是将编辑后的kmeans() 分配给全局环境中的符号kmeans。换句话说,在命令行执行以下操作:

kmeans <- function(...) plot(rnorm(99), col="red") # but using your own edits

## Then run an example from ?KMeansSparseCluster to see that it works.
library(sparcl)
x <- matrix(rnorm(50*300),ncol=300)
x[1:25,1:50] <- x[1:25,1:50]+1
x <- scale(x, TRUE, TRUE)
KMeansSparseCluster.permute(x,K=2,wbounds=seq(3,9,len=15),nperms=5)

这是因为KMeansSparseCluster()(以及对package:sparcl 中的任何其他函数的调用)在namespace:sparcl 中查找kmeans first,在@987654328 中查找then @,然后namespace:base,然后在.GlobalEnv,它会找到你重新定义的kmeans,然后再找到package:stats中的那个。要自己看看,试试这个:

parent.env(asNamespace("sparcl"))
parent.env(parent.env(asNamespace("sparcl")))
parent.env(parent.env(parent.env(asNamespace("sparcl"))))
## etc., also wrapping any of the environments above in calls to ls() 
## to see what's in 'em

很好,stats 包中使用 kmeans() 的函数不会被您的版本中断,因为它们会在符号搜索进入全局环境之前在自己的命名空间中找到 kmeans

【讨论】:

  • @JoshOBrien: 包是不是可以专门从 stats 包中导入kmeans,所以它会在imports:sparcl 中?那将是做到这一点的“正确”方式,是吗?
  • @Aaron -- 我同意:这将是正确的做法,但这不是 sparcl 的作者所做的。我检查了imports:sparcl,它是空的。不过,你的大点是一个好点。如果包作者将import("stats") 放在他们的 NAMESPACE 文件中,则此解决方案将不起作用。
  • 感谢乔希的更新。很高兴检查imports:sparcl 中的内容。
猜你喜欢
  • 2018-08-17
  • 1970-01-01
  • 2011-04-29
  • 2021-03-23
  • 2016-05-27
  • 1970-01-01
  • 2014-09-14
  • 2014-08-17
  • 1970-01-01
相关资源
最近更新 更多