This question already has an answer here:
How can I read the source code for an R function?
(1 answer)
Closed 9 years ago.
Frequently when I am working with R and I want to find out what the function does, I type in the name of the function and scroll through the code. However, sometimes when I type in the name of the function I get a response that does not tell me anything.
> library(limma)
> plotMDS #can't get to the code
function (x, ...)
UseMethod("plotMDS")
<environment: namespace:limma>
> limma:::plotMDS
function (x, ...)
UseMethod("plotMDS")
<environment: namespace:limma>
> heatmap #im expecting something more like this
function (x, Rowv = NULL, Colv = if (symm) "Rowv" else NULL,
distfun = dist, hclustfun = hclust, reorderfun = function(d,
w) reorder(d, w), add.expr, symm = FALSE, revC = identical(Colv,
"Rowv"), scale = c("row", "column", "none"), na.rm = TRUE,
margins = c(5, 5), ColSideColors, RowSideColors, cexRow = 0.2 +
1/log10(nr), cexCol = 0.2 + 1/log10(nc), labRow = NULL,
labCol = NULL, main = NULL, xlab = NULL, ylab = NULL, keep.dendro = FALSE,
verbose = getOption("verbose"), ...)
{
scale <- if (symm && missing(scale))
"none"
else match.arg(scale)
/* ... many lines removed ... */
}
invisible(list(rowInd = rowInd, colInd = colInd, Rowv = if (keep.dendro &&
doRdend) ddr, Colv = if (keep.dendro && doCdend) ddc))
}
<bytecode: 0x16199b8>
<environment: namespace:stats>
Thus, I was wondering if there is a way to import a package's namespace into the default namespace so I can look at code in functions (and debug things easier). I've been reading up on namespace but most of the time it is written for developers so it is talking about how to export namespaces for packages.
plotMDS is the generic function. What you access via plotMDS and limma:::plotMDS is exactly the same thing, the latter just less-efficiently. What you want to get at are the methods for this generic function.
To see the list of method for plotMDS try
methods(plotMDS)
That will return a vector of function names. I can't install limma so here is what we see for the base plot generic [in my current session]:
> methods(plot)
[1] plot.acf* plot.correspondence* plot.data.frame*
[4] plot.decomposed.ts* plot.default plot.dendrogram*
[7] plot.density plot.ecdf plot.factor*
[10] plot.formula* plot.function plot.hclust*
[13] plot.histogram* plot.HoltWinters* plot.isoreg*
[16] plot.lda* plot.lm plot.mca*
[19] plot.medpolish* plot.mlm plot.ppr*
[22] plot.prcomp* plot.princomp* plot.profile*
[25] plot.profile.nls* plot.ridgelm* plot.spec
[28] plot.stepfun plot.stl* plot.table*
[31] plot.ts plot.tskernel* plot.TukeyHSD
Non-visible functions are asterisked
To access the code of non-starred functions we just enter the full function name, e.g.
> plot.density
function (x, main = NULL, xlab = NULL, ylab = "Density", type = "l",
zero.line = TRUE, ...)
{
....
To see the code for starred functions/methods you need the pkg:::function structure, e.g. for the plot.data.frame method
> plot.data.frame
Error: object 'plot.data.frame' not found
> graphics:::plot.data.frame
function (x, ...)
{
....
If you don't know which namespace a method belongs to, then use getAnywhere, e.g.
> getAnywhere(plot.data.frame)
A single object matching ‘plot.data.frame’ was found
It was found in the following places
registered S3 method for plot from namespace graphics
namespace:graphics
with value
function (x, ...)
{
....
The printed results indicate the relevant namespace (in this case graphics) plus return the value of the function, or the code.
This is a really crude alternative, but it does what was requested:
Firstly, copy the contents of the namespace to a list in the global environment:
L <- as.list(asNamespace("yourpackage"))
Now you can either navigate L or copy all its contents to equally named objects in the global environment with this:
invisible(lapply(names(L), function(x) eval(parse(text=paste0(x,"<-L[['",x,"']]")), globalenv())))
warning: this will overwrite whatever object you have defined with the same name! So use with care.
Related
I want to run a moving average (preferably witch custom weights) on a data.table object. frollmean is very fast compared to frollapply (especially) and even TTR::EMA (which does not allow custom weights but only wilder=T/F)--performance comparison at the end. I tried to use data.table:::froll (which frollmean uses and is not exported). It's first argument must be the (character) name of the function (for frollmean it is mean).
data.table:::froll('f.roll', x$c, 10)
Error in data.table:::froll("f.roll", x$c, 10) :
Internal error: invalid fun argument in rolling function, should have been caught before. please report to data.table issue tracker.
I tried to access the data.table package as an environment:
e.dt <- as.environment('package:data.table')
e.dt$froll <- data.table:::froll
Error in e.dt$froll <- data.table:::froll (from c.ta.R#287) :
cannot add bindings to a locked environment
e.dt2 <- new.env(parent=e.dt)
e.dt2$froll <- data.table:::froll
e.dt2$froll('f.roll', top.., n, na.rm=T)
Error in e.dt2$froll("f.roll", x$c, n, na.rm = T) :
Internal error: invalid fun argument in rolling function, should have been caught before. please report to data.table issue tracker.
I also tried attaching my custom function f.roll to e.dt2:
e.dt2$f.roll <- f.roll
e.dt2$froll('f.roll', top.., n, na.rm=T)
Error in e.dt2$froll("f.roll", top.., n, na.rm = T) :
Internal error: invalid fun argument in rolling function, should have been caught before. please report to data.table issue tracker.
froll calls a C function (CfrollfunR):
data.table:::froll
function (fun, x, n, fill = NA, algo = c("fast", "exact"), align = c("right",
"left", "center"), na.rm = FALSE, hasNA = NA, adaptive = FALSE)
{
stopifnot(!missing(fun), is.character(fun), length(fun) ==
1L, !is.na(fun))
algo = match.arg(algo)
align = match.arg(align)
ans = .Call(CfrollfunR, fun, x, n, fill, algo, align, na.rm,
hasNA, adaptive)
ans
}
<bytecode: 0x0000000013738b40>
<environment: namespace:data.table>
data.table:::CfrollfunR
$name
[1] "CfrollfunR"
$address
<pointer: 0x000000001ef93e80>
attr(,"class")
[1] "RegisteredNativeSymbol"
$dll
DLL name: datatable
Filename: C:/bin/cygwin/cygwin64/home/Adi/R/win-library/4.0/data.table/libs/x64/datatable.dll
Dynamic lookup: FALSE
$numParameters
[1] -1
attr(,"class")
[1] "CallRoutine" "NativeSymbolInfo"
However, while my custom function f.roll doesnt' work, mean as the first parameter works:
tail(e.dt2$froll('mean', x$c, 10, na.rm=T))
[1] 43.506 43.148 42.855 42.548 42.331 42.200
I inspected the data.table DLL C:\bin\cygwin\cygwin64\home\Adi\R\win-library\4.0\data.table\libs\x64\datatable.dll (using DLL Export Viewer) but it doesn't show any function called mean. data.table package also doesn't have any mean function (either exported or not exported).
data.table:::mean
Error in get(name, envir = asNamespace(pkg), inherits = FALSE) :
object 'mean' not found
data.table::mean
Error: 'mean' is not an exported object from 'namespace:data.table'
Any suggestion would be greatly appreciated.
PS Here's the performance comparison from microbenchmark for reference:
## Unit: milliseconds
## expr min lq mean median uq max neval
## frollapply 131736.6469 131736.6469 131736.6469 131736.6469 131736.6469 131736.6469 1
## EMA 262.9931 262.9931 262.9931 262.9931 262.9931 262.9931 1
## frollmean 97.0388 97.0388 97.0388 97.0388 97.0388 97.0388 1
PS Since froll isn't a solution to my problem (as #Waldi points out) I'm looking for an alternate solution but I left this as is and asked a separate, new question: fast way to calculate moving average/rolling function which allows custom weights
C source code of froll, shows that this internal function is only designed for mean or sum, the error isn't linked to the environment :
enum {MEAN, SUM} sfun;
if (!strcmp(CHAR(STRING_ELT(fun, 0)), "mean")) {
sfun = MEAN;
} else if (!strcmp(CHAR(STRING_ELT(fun, 0)), "sum")) {
sfun = SUM;
} else {
error(_("Internal error: invalid %s argument in %s function should have been caught earlier. Please report to the data.table issue tracker."), "fun", "rolling"); // # nocov
}
I am working with R. I found this previous post on stackoverflow which shows how to get a "list" of all functions that belong to a given library:
How to find all functions in an R package?
For example:
#load desired library
library(ParBayesianOptimization)
#find out all functions from this library
getNamespaceExports("ParBayesianOptimization")
[1] "addIterations" "getLocalOptimums" "bayesOpt" "getBestPars" "changeSaveFile" "updateGP"
The above code tells me the name of all functions that are used in the "ParBayesianOptimization" library. From here, I could manually inspect each one of these functions - for example:
# manually inspect any one of these functions
getAnywhere(bayesOpt)
A single object matching ‘bayesOpt’ was found
It was found in the following places
package:ParBayesianOptimization
namespace:ParBayesianOptimization
with value
#function stats here
function (FUN, bounds, saveFile = NULL, initGrid, initPoints = 4,
iters.n = 3, iters.k = 1, otherHalting = list(timeLimit = Inf,
minUtility = 0), acq = "ucb", kappa = 2.576, eps = 0,
parallel = FALSE, gsPoints = pmax(100, length(bounds)^3),
convThresh = 1e+08, acqThresh = 1, errorHandling = "stop",
plotProgress = FALSE, verbose = 1, ...)
{
startT <- Sys.time()
optObj <- list()
etc etc etc ...
saveFile = saveFile, verbose = verbose, ...)
return(optObj)
}
#function ends here
<bytecode: 0x000001cbb4145db0>
<environment: namespace:ParBayesianOptimization>
Goal : Is it possible to take each one of these functions and create a notepad file with their full definitions?
Something that would look like this:
My attempt:
I thought I could first make an "object" in R that contained all the functions found in this library:
library(plyr)
a = getNamespaceExports("ParBayesianOptimization")
my_list = do.call("rbind.fill", lapply(a, as.data.frame))
X[[i]]
1 addIterations
2 getLocalOptimums
3 bayesOpt
4 getBestPars
5 changeSaveFile
6 updateGP
Then, I could manually create an "assignment arrow":
header_text <- rep("<-")
Then, "paste" this to each function name:
combined_list <- as.character(paste(my_list, header_text, sep = ""))
But this is not looking correct:
combined_list
[1] "c(\"addIterations\", \"getLocalOptimums\", \"bayesOpt\", \"getBestPars\", \"changeSaveFile\", \"updateGP\")<- "
The goal is to automate the process of manually copying/pasting :
function_1 = getAnywhere("first function ParBayesianOptimization library")
function_2 = getAnywhere("second function ParBayesianOptimization library")
etc
final_list = c(function_1, function_2 ...)
And removing the generic description from each function:
A single object matching ‘bayesOpt’ was found
It was found in the following places
package:ParBayesianOptimization
namespace:ParBayesianOptimization
with value
In the end, if I were to "call" the final_list object, all the functions from this library should get recreated and reassigned.
Can someone please show me how to do this?
Thanks
You can use the dump function for this
pkg <- "ParBayesianOptimization"
dump(getNamespaceExports(pkg), file="funs.R", envir = asNamespace(pkg))
This code will help you write the function definitions of all the functions in a library to a text file.
fn_list <- getNamespaceExports("ParBayesianOptimization")
for(i in seq_along(fn_list)) {
header <- paste('\n\n####Function', i, '\n\n\n')
cat(paste0(header, paste0(getAnywhere(fn_list[i]), collapse = '\n'), '\n\n'),
file = 'function.txt', append = TRUE)
}
My fingers are starting to tire of typing update.packages(checkBuilt = T, ask = F). I was wondering whether it's possible to redefine the default parameters within the update.packages() function. So far, I've tried adding the following to my .Rprofile file:
utils::assignInNamespace(
"update.packages",
function(checkBuilt = TRUE, ask = FALSE, ...) {
update.packages(checkBuilt = checkBuilt, ask = ask, ...)
},
"utils"
)
But when I try to use the function in R I get the following error:
update.packages()
Error: C stack usage 7976404 is too close to the limit
I've also just tried using formals() with the following in the .Rprofile:
local({
args_new <- alist(lib.loc = .libPaths(), ask = FALSE, checkBuilt = TRUE)
ind <- which(methods::formalArgs(update.packages) %in% names(args_new))
formals(update.packages)[ind] <- args_new
})
But that results in the following error upon launching R:
Error in formals(update.packages) : object 'update.packages' not found
As #Roland said in the comments, your definition is recursive. You shouldn't bother with the assignInNamespace: keeping the new function in your workspace is good enough. Then you can use utils::update.packages in its definition, e.g.
update.packages <- function(checkBuilt = TRUE, ask = FALSE, ...)
utils::update.packages(checkBuilt = checkBuilt, ask = ask, ...)
You should avoid using assignInNamespace for the reasons listed in its help page.
Is it possible to change default argument(s) of S3 Methods in R?
It's easy enough to change arguments using formals ...
# return default arguments of table
> args(table)
function (..., exclude = if (useNA == "no") c(NA, NaN), useNA = c("no",
"ifany", "always"), dnn = list.names(...), deparse.level = 1)
# Update an argument
> formals(table)$useNA <- "always"
# Check change
> args(table)
function (..., exclude = if (useNA == "no") c(NA, NaN), useNA = "always",
dnn = list.names(...), deparse.level = 1)
But not S3 methods ...
# View default argument of S3 method
> formals(utils:::str.default)$list.len
[1] 99
# Attempt to change
> formals(utils:::str.default)$list.len <- 99
Error in formals(utils:::str.default)$list.len <- 99 :
object 'utils' not found
At #nicola's generous prompting here is an answer-version of the comments:
You can edit S3 methods and other non-exported functions using assignInNamespace(). This lets you replace a function in a given namespace with a new user-defined function (fixInNamespace() will open the target function in an editor to let you make a change).
# Take a look at what we are going to change
formals(utils:::str.default)$list.len
#> [1] 99
# extract the whole function from utils namespace
f_to_edit <- utils:::str.default
# make the necessary alterations
formals(f_to_edit)$list.len<-900
# Now we substitute our new improved version of str.default inside
# the utils namespace
assignInNamespace("str.default", f_to_edit, ns = "utils")
# and check the result
formals(utils:::str.default)$list.len
#> [1] 900
If you restart your R session you'll recover the defaults (or you can put them back manually in the current session).
Can one use t statistics from a Student's t-test directly in the fdrtool() function of fdrtool package (ver. 1.2.12)? The paper (Strimmer-K BMC Bioinfo. 2008, 9:303) mentions this but as far as I can see the parameters only recognize "normal", "correlation" and "pvalue". Is there a workaround for a non-statistician ?
I think it's a typo.
I took a look at the source for the fdrtool function and found that the statistic argument first gets passed through match.arg and then to fdrtool:::get.nullmodel.
Then, lo and behold:
args(fdrtool:::get.nullmodel)
# function (statistic = c("normal", "correlation", "pvalue", "studentt"))
# NULL
and indeed there is a fully-implemented case in that function for the student t:
if (statistic == "studentt") {
f0 = function(x, param, log = FALSE) {
return(dt(x, df = param, log = log))
}
F0 = function(x, param) {
return(pt(x, df = param))
}
iqr = function(param) {
return(qt(0.75, df = param) - qt(0.25, df = param))
}
get.support = function() return(c(1, 1000))
}
Now, before I tell you how to access this option, I want to warn you that it's very possible it was disabled on purpose. I can't imagine why, because at first glance it seems like it should work fine. But if you're planning to use this in a research result you ought to document the fact that this was essentially a "hidden" option and that you had to do some hacking to access it. Moreover, I haven't actually tested this on my computer, so beware of typos.
Now, as for that hacking, the easiest way to get this to work would be to first simply type fdrtool into the R console. Then, copy and paste the output to a new R script (or use sink if you're fancy like that). The first few lines should look like:
function (x, statistic = c("normal", "correlation", "pvalue"),
plot = TRUE, color.figure = TRUE, verbose = TRUE, cutoff.method = c("fndr",
"pct0", "locfdr"), pct0 = 0.75)
{
statistic = match.arg(statistic)
...
Then all you have to do is change c("normal", "correlation", "pvalue") to c("normal", "correlation", "pvalue", "studentt"). That is, the first few lines should now look like
function (x, statistic = c("normal", "correlation", "pvalue", "studentt"),
plot = TRUE, color.figure = TRUE, verbose = TRUE, cutoff.method = c("fndr",
"pct0", "locfdr"), pct0 = 0.75)
{
statistic = match.arg(statistic)
...
Finally, reassign this function to fdrtool (don't worry, this won't break the underlying package, it will just act like a "mask" until you remove it with rm):
fdrtool <- function (x, statistic = c("normal", "correlation", "pvalue", "studentt"),
plot = TRUE, color.figure = TRUE, verbose = TRUE, cutoff.method = c("fndr",
"pct0", "locfdr"), pct0 = 0.75)
{
statistic = match.arg(statistic)
...
And run the whole thing or source the script. Then you should be good to go.
Turns out that the maintainer of the package, Korbinian Strimmer, disabled the t-score based function on purpose. The reason for that is that it has been used incorrectly too often.
Prof. Strimmer is a nice guy and responded to my help request quickly and very comprehensively. This is what he suggests: T-scores in practice often do not follow a t-distribution but show rather an over- or underdispersion, which is why you should better use the normal option.
Before that, however, you will have to center your data
z.centered = z-median(z)
fdrtool(z.centered, statistic="normal")