Override a function that is imported in a namespace - function

As the termplot function in R is containing some weird code that is giving me annoying bugs, I want to override it in my own test code until I find a more permanent solution. Problem is that the changed function is not loaded by the mgcv package. The mgcv package loads termplot from the stats package in its namespace, using importFrom() in the NAMESPACE file.
How can I convince mgcv to use the changed termplot? I tried :
unlockBinding("termplot", as.environment("package:stats"))
assign("termplot", my.termplot, as.environment("package:stats"))
lockBinding("termplot", as.environment("package:stats"))
and when applied to lm-objects, this works and the altered termplot is used. But when using gam-objects made by the mgcv package, this doesn't work. I'm not really going to build the stats package from source if I can avoid it...
To clarify, I also tried with
assignInNamespace("termplot", my.termplot, ns="stats")
assignInNamespace("termplot", my.termplot, ns="mgcv")
in all possible combinations, before attaching mgcv, after attaching mgcv, and I didn't manage to get it working.
EDIT :
I tried all options given here (apart from rebuilding either package), and couldn't get it to work. The easy way out for me is using a wrapper function. That discussion can be found here. Thanks for all the tips.
A reproducible example :
my.termplot <- function (x) print("my new termplot")
unlockBinding("termplot", as.environment("package:stats"))
assignInNamespace("termplot", my.termplot, ns="stats", envir=as.environment("package:stats"))
assign("termplot", my.termplot, as.environment("package:stats"))
lockBinding("termplot", as.environment("package:stats"))
y <- 1:10
x <- 1:10
xx <- lm(y~x)
termplot(xx)
require(mgcv)
dat <- gamSim(1, n = 400, dist = "normal", scale = 2)
b <- gam(y ~ s(x0) + s(x1) + s(x2) + x3, data = dat)
plot(b,all=TRUE)
plot.gam calls termplot for the non-smooth terms (x3 in this case), but fails to find the new termplot function.
EDIT2 : apparently, my example works. I see now I solved my own question: In the first code, I didn't add both the namespace and the package in assignInNamespace. It is important to remember to change the function both in the namespace and the package before loading the other package. Thx #hadley for pointing me in the right direction, #Marek for testing the code and reporting it works, and the rest for taking the effort to answer.

I'm stumped - I can't figure out how plot.gam is locating termplot - it's not using the ordinary scoping rules as far as I can tell. This seems to need a deeper understanding of namespaces than I currently possess.
my.termplot <- function (x) print("my new termplot")
# where is it defined?
getAnywhere("termplot")
# in package and in namespace
unlockBinding("termplot", as.environment("package:stats"))
assign("termplot", my.termplot, "package:stats")
unlockBinding("termplot", getNamespace("stats"))
assign("termplot", my.termplot, getNamespace("stats"))
getAnywhere("termplot")[1]
getAnywhere("termplot")[2]
# now changed in both places
y <- 1:10
x <- 1:10 + runif(10)
xx <- lm(y ~ x)
termplot(xx) # works
library("mgcv")
b <- gam(y ~ s(x), data = data.frame(x, y))
plot(b) # still calls the old termplot
# I'm mystified - if try and find termplot as
# seen from the environment of plot.gam, it looks
# like what we want
get("termplot", environment(plot.gam))

Try overwriting the function that you are calling termplot from. At a guess, this is plot.gam in the mgcv package.
First load the necessary package.
library(mgcv)
Here's your alternate termplot function, added to the stats namespace.
my.termplot <- function (model, ...)
{
message("In my.termplot")
}
unlockBinding("termplot", as.environment("package:stats"))
assign("termplot", my.termplot, as.environment("package:stats"))
lockBinding("termplot", as.environment("package:stats"))
Likewise, here's the wrapper function, added to the mgcv namespace.
my.plot.gam <- function (x, ...)
{
message("In my.plot.gam")
my.termplot()
}
unlockBinding("plot.gam", as.environment("package:mgcv"))
assign("plot.gam", my.plot.gam, as.environment("package:mgcv"))
lockBinding("plot.gam", as.environment("package:mgcv"))
Here's an example to test it, taken from ?gam.
dat <- gamSim(1, n = 400, dist = "normal", scale = 2)
b <- gam(y ~ s(x0) + s(x1) + s(x2) + s(x3), data = dat)
plot(b)

I think the trace() function does automatically what is attempted above. Do:
trace('termplot', edit='gedit')
Where 'gedit' is the name of a text editor. The editor will open with the original code and you can paste whatever substitution code you desire.
To return to the original version just untrace('termplot')
Caveat: I tried using this when the text editor had many files open and it didn't work. So I use 'gedit', a text editor on my system that I don't use often. This way I am sure that R will open a new instance of 'gedit'.
I'm not positive this will help, but I think it's worth a try. The search sequence when there are namespaces is really confusing.

Related

Wrong output and with a special sign "[square]" although same code works in "Octave-online"

Octave 6.1.0 (GUI)
This is a spin-off from Octave: How to turn a vector of integers into a cell array of strings?.
>> a = 1:3;
>> cellstr(int2str(a(:)))
ans =
{
[1,1] = "[square]"
}
While the output should be:
ans =
{
[1,1] = 1
[2,1] = 2
[3,1] = 3
}
octave-online.net:
How to fix this?
Only for your information and not as an answer, a workaround without this issue would be cellstr(num2str(a(:))).
This is caused by a terrible beginner's mistake.
The first helping comment under the question has already shown the way.
If the online Octave is totally different from your GUI, it is probably your fault.
In order to find the problem, check the functions that might cause the difference:
>> which('num2str')
'num2str' is a function from the file C:\Users\USERNAME\AppData\Local\Programs\GNU Octave\Octave-6.1.0\mingw64\share\octave\6.1.0\m\general\num2str.m
>> which('cellstr')
'cellstr' is a built-in function from the file libinterp/octave-value/ov-cell.cc
which('int2str')
>> which('int2str')
'int2str' is a variable`
Since I have for some unknown reason tested only other functions, but not the 'int2str', I accidentally found out about the error when using the shadowed function instead:
>> strcat('x', num2cell(int2str(1:10)))
error: int2str(10): out of bound 3 (dimensions are 1x3)
(note: variable 'int2str' shadows function)
For whatever reason, I had accidentally shadowed the function by assigning int2str = [1:3], leading to the strange behaviour.
Arbeitsumgebung = work environment:

How to change the value of a variable that is declared out of a function?

Let's say you've a variable called "ENV.myvar" in your code. Then you've have written a function, "myfun", that has some goal and return an object (for example an array). ENV.myvar is used for computation and for some reason it has been modified during the process.
ENV.myvar <- 0;
myfun <- function(in1,in2,...,inN){
#declaring the output object
fun.myarray <- c(0,0,0);
#some code
...
#here ENV.myvar is modified for some reason
ENV.myvar = ENV.myvar + 20;
#code that works on fun.myarray
...
#return the object
fun.myarray
}
Finally we get to the point of the question: the function returns only the object but should overwrite permanently an external variable used during the process. Is it possible in some way?
Yes, you can, even though it's not recommended, because it's not the expected behaviour of a function to change anything outside its environment. Still, this is how you could do it:
myvar <- 0
myf <- function() {
assign("myvar", 10, envir = parent.env(environment()))
return("Value of 'myvar' changed to 10 in parent environment")
}
myf()
myvar
# [1] 10
You can change the parent.env(environment()) part to get a specific environment, or just .GlobalEnv, depending on the call stack and your needs.

integrating manipulate outputs with knitr

I was wondering if there is a way in which its possible to integrate the manipulate package or gWidgetsManipulate package so that their outputs can be viewable/manipulable in the html/markdown output file, as I think that this would be extremely useful when developing reproducible interactive research reports. I know that googleVis has some functionality that allows it to be integrated with knitr so that the outputs go into the html file by using options like results='asis' but googleVis is currently quite restrictive in its capabilities when using sliders for example.
If the package outputs of manipulate or gWidgetsManipulate hasn't quite been integrated yet, would it be possible to suggest a workaround for the time being that will allow it to be viewed in the html file?
i.e. my current code in my Rmd file before running knitr-ing to html looks like the below...but I get the following errors.
```{r}
library(manipulate)
manipulate(plot(1:x), x = slider(5, 10))
```
with output
library(manipulate)
## Error: there is no package called 'manipulate'
manipulate(plot(1:x), x = slider(5, 10))
## Error: could not find function "manipulate"
so trying the package gWidgetsManipulate instead...
```{r}
library(gWidgetsManipulate)
manipulate(plot(1:x), x = slider(5, 10))
```
you get the error...
library("gWidgetsManipulate")
## Loading required package: gWidgets
manipulate(plot(1:x), x = slider(5, 10))
## Error: unable to find an inherited method for function ".gwindow", for signature "NULL"
I have tried to specify a guiToolkit to fix this error by using things like
options(guiToolkit="WWW")
but to no avail...
Any help would be greatly appreciated, thanks in advance
If you don't absolutely need to use the gwidgets, I have a solution with Rook and googleVis that does what you want: displaying an interactive chart in html.
The script for the slider: it contains a little javascript function to display the currently chosen value. It also submits the form at each change. You can easily change the min/max/... values here.
slider_script <- '
<input type="range" min="5" max="10" name="plot_max" value="%s" step="1" onchange="document.form1.submit(); showValue(this.value);" />
<span id="range">%s</span>
<script type="text/javascript">
function showValue(newValue)
{
document.getElementById("range").innerHTML=newValue;
}
</script>
'
We build the code of the web page. The structure is typical for rook: html code is written inside res$write().
### this script builds the webpage
webreport_app <- function(
){
newapp = function(env) {
req = Rook::Request$new(env)
res = Rook::Response$new()
# initialise variables for first execution
if (is.null(req$POST())){
plot_max <- 5
} else{
plot_max <- as.numeric(req$POST()[["plot_max"]])
}
res$write('<body style="font-family:Arial">')
res$write("<H3>My App</H3>")
res$write('<form name = "form1" method="POST">\n')
res$write('<br> Number of dots: \n')
res$write(sprintf(slider_script, plot_max, plot_max))
res$write('<br><input type="submit" name="Go!">\n</form>\n')
if (!is.null(req$POST())) {
# generate the plot
library(googleVis)
data_for_plot <- data.frame(x_var = 1:plot_max, y_var = 1:plot_max)
Scatter1 <- gvisScatterChart(data_for_plot)
# extract chart script
chart_script <- capture.output(print(Scatter1, 'chart'))
# write to html
res$write(paste(chart_script, collapse="\n"))
res$write("<br><br></body></html>")
}
res$finish()
}
return(newapp)
}
And finally launch the setup and launch the html server via Rook:
library(Rook)
# launch the web app
if (exists("report_server")){
report_server$remove(app, all = TRUE)
report_server$stop()
rm(report_server)
}
report_server = Rhttpd$new()
report_server$add(app = webreport_app(), name = "My_app")
report_server$start()
report_server$browse("My_app")
report_server$browse()

Cannot pass function handle as an argument of a function

I'm new to Matlab and I'm trying to write custom function in matlab that would take function handle as one of its arguments.
I'm getting this error all the time:
Error using subsindex
Function 'subsindex' is not defined for values of class 'function_handle'.
Trying to debug I performed following test: I run command x = fminbnd(#humps, 0.3, 1). I proceeded as expected - I got result x = 0.6370.
So I created custom function called train and I copied ALL the code of function fminbnd to the file train.m. The only thing that I changed is the name, so that code of functions fminbnd and train is now identical except for the names.
Now I run both functions with the same argument and the custom function throws error while original fminbnd returns correct answer.
Here is the code:
>> x = fminbnd(#humps, 0.3, 1)
x =
0.6370
>> x = train(#humps, 0.3, 1)
Error using subsindex
Function 'subsindex' is not defined for values of class 'function_handle'.
Here is header of function train (everything else is copied from fminbnd):
function [xf,fval,exitflag,output] = train(funfcn,ax,bx,options,varargin)
Where is the problem?
Doing a which train showed me that there is a function in the neural network toolbox of the same name.
/Applications/MATLAB_R2009b.app/toolbox/nnet/nnet/#network/train.m % network method
You may be running the nnet train.m rather than the one you think you're running. Are you in the directory containing your train.m? When I made sure I was in the right directory, I got it to work:
>> which train
/Users/myuserid/train.m
>> x = train(#humps,0.3,1)
x =
0.6370
Maybe you can name your file something else like myfminbnd.m instead?
Instead of duplicating the whole fminbnd function, try:
function varargout = myfminbnd(varargin)
varargout = cell(1,nargout(#fminbnd));
[varargout{:}] = fminbnd(varargin{:});
end
this will work as an "alias" to the existing function:
>> fminbnd(#(x)x.^3-2*x-5, 0, 2)
ans =
0.8165
>> myfminbnd(#(x)x.^3-2*x-5, 0, 2)
ans =
0.8165
(you can get the other output arguments as well)

R function scope

I am trying to create a function for a series of rounding work in R, I have percentage, decimals, etc, each of them need to be rounded differently.
I start writing the function by first picking up the object I want, but I fail to do that already, here's the code:
roundings <- function(obj.head)
{obj.list <- ls(pattern=obj.head)
obj.list
}
Suppose I have two object A1 and B1, I suppose if I run roundings("A"), A1 should appear as the function output, but it didn't.
What have I done wrong? Thanks.
The call to ls is searching the current environment within the function and does not find any objects to match. You can specify the envir parameter in ls with .GlobalEnv. Thus your code becomes:
roundings <- function(obj.head)
{
obj.list <- ls(pattern = obj.head, envir = .GlobalEnv)
obj.list
}
I found the reason, I should have added envir=.GlobalEnv inside the ls parameters.