This is probably a trivial matter but I just hope to see if there's is a better way to do this.
I wrote a custom function and would like it to display a summary or introduction text to console when I enter it without any parameters, i.e. name-of-custom-function(). Apart from showing me the arguments list (something a simple str() can do), I intended it as a reminder for myself in future.
To resolve this currently, I set the first argument to default NULL and run an 'if' statement to check for NULL. My question is: Does R have a built-in function/method to set certain user-specified text as the default introduction when writing custom functions, or do I have to handle it myself (e.g. using my 'is.null test') within the function itself?
A sample code is shown below:
myprint <- function(x=NULL, y, ...)
{
if(is.null(x)==TRUE)
{
writeLines( "\nNOTE: This function prints to console what the user keys in.\nmyprint() Function ver 0.1")
}else
{
print(x)
}
} ### End of myprint() function ###
I couldn't find similar questions/resources online. I found https://stackoverflow.com/questions/19226816/how-can-i-view-the-source-code-for-a-function & https://stackoverflow.com/questions/20924877/inputs-into-r-functions-an-easier-display, but the issues are not the same.
Would appreciate any help given. Thanks in advance!
Related
I am trying to track the number of times certain functions are called from the console.
My plan is to add a simple function such as "trackFunction" in each function that can check whether they have been called from the console or as underlying functions.
Even though the problem sounds straight-forward I can't find a good solution to this problem as my knowledge in function programming is limited. I've been looking at the call stack and rlang::trace_back but without a good solution to this.
Any help is appreciated.
Thanks
A simple approach would be to see on which level the current frame lies. That is, if a function is called directly in the interpreter, then sys.nframe() returns 1, otherwise 2 or higher.
Relate:
Rscript detect if R script is being called/sourced from another script
myfunc <- function(...) {
if (sys.nframe() == 1) {
message("called from the console")
} else {
message("called from elsewhere")
}
}
myfunc()
# called from the console
g <- function() myfunc()
g()
# called from elsewhere
Unfortunately, this may not always be intuitive:
ign <- lapply(1, myfunc)
# called from elsewhere
for (ign in 1) myfunc()
# called from the console
While for many things the lapply-family and for loops are similar, they behave separately here. If this is a problem, perhaps the only way to mitigate this is the analyze/parse the call stack and perhaps "ignore" certain functions. If this is what you need, then perhaps this is more appropriate:
R How to check that a custom function is called within a specific function from a certain package
Lets say I have a function with arguments x.
func<-function(x){
if(x=='a'){.......}
else if (x=='b'){........}
else if (x=='c'){.........}
}
....... represents some code that I wish to execute.
Whenever I call the function from the console, hitting TAB lets me see the arguments the function takes, how do I also display the possible values of the argument so that the user can choose one.
You can create a package and add documentation to your functions. E.g., you can run devtools::create("~/mypackage") (change ~/mypackage if you want). Then create a file at ~/mypackage/R called myfunc.R. In this file set the following
#' #title What does this function do?
#' #param x if \code{a} then 1, if \code{b} then 2, if \code{c} then 3.
#' #param ... may have an effect in the future.
#' #export
myfunc <- function(x, ...){
if(x=='a'){
1
}
else if (x=='b'){
2
}
else if (x=='c'){
3
} else
stop(sQuote(x), " not implemented")
}
Then run setwd("~/mypackage"), devtools::document(), and devtools::install(). After that you can call library(mypackage) after which you can see the following in Rstudio
The above uses roxygen2 and a R package which might be more then you ask for. Though, it
... display the possible values of the argument so that the user can choose one.
I assume you're referring to the console in RStudio. My understanding is that this is not currently a feature of RStudio. Maybe someone has developed an RStudio Addin that will do that for you though.
I need to modify the function gamGPDfit() in the package QRM to solve a problem. The function gamGPDfit() in turn calls other functions fit.GPD() and gamGPDfitUp() to compute the estimates of the parameters.
The structure of the function is shown below:
#######################################################
gamGPDfit<-function (..., init = fit.GPD(...) , ...)
{
...
Par<-gamGPDfitUp(...)
...
return (list(...))
}
<environment: namespace:QRM>
#######################################################
Now, when I call fit.GPD(), I get the function on the command window to make the necessary modifications. However, the other function gamGPDfitUp() returns
> gamGPDfitUp
Error: object 'gamGPDfitUp' not found
The question is, how do I get such an in-built function within another function? Does it have to do with the environment QRM? If so how do I obtain the function to modify it?.
I have attached the function and the call of the gamGPDfitUp() is indicated in colour red.
There's a couple of things that may come in handy.
One is help(":::") - Accessing exported and internal variables in a namespace. You can access GamGPDfitUp probably by prefixing it with QRM:::.
Another function is fixInNamespace, which allows you to modify functions inside packages. The help page for this one lists a few more interesting tools. Play around with this and it should solve most of your problems.
I am trying to understand the way the YourCast R package works and make it work with my data.
For example, if a function produces errors, I
get the source code of that function using YourCast:::bad.fn
add outputs of critical
values at critical stages
use reassignInPackage(name="original.fn", package="YourCast", value="my.fn")
Once I find the cause of the error, I fix it in the function and reassign it in the package.
However, for some strange reason this does not work for non-hidden functions.
For example:
install.packages("YourCast")
Library(YourCast)
YourCast:::check.depvar
This will print the hidden function check.depvar. One line if (all(ix == 1:3)) will produce an error message if any of the x is missing.
Thus, I change the whole function to the following and replace the original formula:
mzuba.check.depvar <- function(formula)
{
return (grepl("log[(]",as.character(formula)[2]))
}
reassignInPackage("check.depvar",
pkgName="YourCast",
mzuba.check.depvar)
rm(mzuba.check.depvar)
Now YourCast:::check.depvar will print my version of that function, and everything is fine.
However
YourCast::yourcast or YourCast:::yourcast or simply yourcast will print the non-hidden function yourcast. Suppose I want to change that function as well.
reassignInPackage(name="yourcast",
pkgName="YourCast",
value=test)
Now, YourCast::yourcast and YourCast:::yourcast will print the new, modified version but yourcast still gives the old version!
That might not a problem if I could simply call YourCast::yourcast instead of yourcast, but that produces some kind of error that I can't trace back because suddenly R-Studio does not print error messages at all anymore!, although it still does something if it is capable to:
> Uagh! do something!
> 1 + 1
[1] 2
> Why no error msg?
>
Restarting the R-session will solve the error-msg problem, though.
So my question is: How do I reassign non-hidden functions in packages?
Furthermore (this would faciliate testing a lot), is there a way to make all hidden functions available without using the ::: operator? I.e., How to export all functions from a package?
This is not just a coding style question. If you know python (and I think also Ruby has something like this), you can have a docstring in a function, such that you can readily get that string by issuing a "help" command. e.g.:
def something(t=None):
'''Do something, perhaps to t
t : a thing
You may not want to do this
'''
if t is not None:
return t ** 2
else:
return 'Or maybe not'
Then help(something) returns the following:
Help on function something in module __main__:
something(t=None)
Do something, perhaps to t
t : a thing
You may not want to do this
The way things work in R, you can get the full text of the defined code snippet, so you could see comments (including those at the beginning of the function), but that can be a lot of scrolling and visual filtering. Is there any better way?
I recently wrote a package to do just this task. The docstring package allows one to write their documentation as roxygen style comments within the function they are documenting. For example one could do
square <- function(x){
#' Square a number
return(x^2)
}
and then to view the documentation either call the docstring function
docstring(square)
or use the built in ? support and do
?square
The comments can either be a single chunk like shown above or fully roxygen style to take advantage of some of the keywords provided
square <- function(x){
#' Square a number
#'
#' Calculates the square of the input
#'
#' #param x the input to be squared
return(x^2)
}
This is on CRAN now: https://cran.r-project.org/package=docstring so you can just install using
install.packages("docstring")
or if you want the latest development version you can install from github:
library(devtools)
install_github("dasonk/docstring")
You can add any attributes you like to R objects, including function. So something like
describe <- function(obj) attr(obj, "help")
foo <- function(t=NULL) ifelse(!is.null(t), t^2, "Or maybe not")
attr(foo, "help") <- "Here is the help message"
produces more or less the desired output
> foo(2)
[1] 4
> foo()
[1] "Or maybe not"
> describe(foo)
[1] "Here is the help message"
Sort-of -- look at the roxygen2 package on CRAN (vignette here). You write a declarative header, and among other things a help page is created for you when you 'roxygen-ize' your sources.
It may not be the easiest package to use, see here on SO for questions pertaining to it as well as its mailing list. But it probably is the closest match.
RStudio helps you to create documentation quite easily.
See their documentation for more information.
The new reference class system has something very similar to docstrings for documenting methods of a class. Here is an example:
Something <- setRefClass("Something",
methods=list(
something=function(t=NULL) {
"Do something, perhaps to t
t : a thing
You may not want to do this
"
if(!is.null(t))
t^2
else
"Or maybe not"
}
))
a <- Something$new()
a$something(2)
a$something()
Something$help("something") ## to see help page
I had another idea as I'm finally wrapping my head around the fact that "R is a (very poor) LISP". Specifically, you can get access to the source code (usually) using the deparse command. So, this function would be a start towards defining your own custom source-code parsing help function:
docstr <- function(my.fun) {
# Comments are not retained
# So, we can put extra stuff here we don't want
# our docstr users to see
'This is a docstring that will be printed with extra whitespace and quotes'
orig.code.ish <- deparse(my.fun)
print(orig.code.ish[3])
}
docstr(docstr)
The above illustrates that deparse really does deparse, and is different from what you'd print at the REPL prompt if you typed docstr: quotes are changed to (default) double-quotes, opening curly brace gets moved to the second line, and blank lines (including comments) are removed. This actually helps a lot if you want to design a robust function. Would be trivial to look for e.g., opening and closing quotes down through the first line that doesn't start with a quote.
Another way to do it would be to get the list of call objects that make up the body list with body(docstr). The string would be in body(docstr)[[2]]. I have to admit that I'm a bit out of my depth here, as I don't fully understand the return value of body, and don't know where to find documentation! In particular, note that body(docstr)[2] returns an object of type and mode 'call'.
This latter approach seems much more LISPy. I'd love to hear other's thoughts on the general idea, or have pointers to actual language reference materials for this!