Thanks for all your advice. My remaining question is this:
Can I replace column name 'sulphate' in the following statement ...
dataclean <- datatable$sulfate[!datanas]
.... with a reference to a parameter 'pollutant', which may or may not have a value of 'sulfate'?
When you attach values to arguments, they appear as they would be objects in your workspace. But the environment is not workspace but that of the function.
So in your case, directory would be a character string and it would work. For the first time. Your working directory is now changed and you need to revert back to the previous one for the function to work again. This can get pretty messy so what I like to do is just refer to raw files by full path. See ?list.files for more info.
For your second question, your best bet is to refer to a certain level within the variable, is to do
x[, pollutant]
It is convenient to add drop = FALSE argument there, in order to keep the what I'm assuming is a data.frame.
You could improve your function by also implementing the datatable argument. That way you have all the objects bundled together nicely.
The most important thing to note here would be "debugging". You should learn to use at least browser(). This function will stop the execution of your function at the very step where it was called. This enables you, in the R console, to inspect elements in the function and run code to see what's going. This way you can speed up the development of code, at least initially when you usually haven't internalized all the data structures and paradigms yet.
Related
I am developing a package in R (3.4.3) that has some internal data and I would like to be able to let users refresh it without having to do too much work. But let me be more specific.
The package contains several functions that rely on multiple parameters but I am really only exporting wrapper functions that do all the work. Because there is a large number of parameters to pass to those wrapper functions, I am putting them in a .csv file and passing them as a list. The data is therefore a simple .csv file called param.csv with the names of the parameters in one column and their value in the next column
parameter_name,parameter_value
ticker, SPX Index
field, PX_LAST
rolling_window,252
upper_threshold,1
lower_threshold,-1
signal_positive,1
signal_neutral,0
signal_negative,-1
I process it running
param.df <- read.csv(file = "param.csv", header = TRUE, sep = ",")
param.list <- split(param.df[, 2], param.df[, 1])
and I put the list inside the package like this
usethis::use_data(param.list, overwrite = TRUE)
Then, when reset the R interactive window and execute
devtools::document()
devtools::load_all()
devtools::install()
require(pkg)
everything works out great, the data is available and can be passed to functions.
First problem: when I change param.csv, save it and repeat the above four lines of code, the internal param.list is not updated. Is there something I am missing here ? Or is it by nature that the developer of the package should run usethis::use_data(param.list, overwrite = TRUE) each time he changes the .csv file where the data comes from?
Because it is intended to be a sort of map, the users will want to tweak parameters for (manual) model calibration. To try and solve this issue I let users provide the functions with their own parameter list (as a .csv file like before). I have a function get_param_list("path/to/file.csv") that does exactly the same processing that is done above and returns the list. It allows the users to pass their own parameter list. De facto, the internal data param.list is considered a default setting of parameters.
Second Problem: I'd like to let users modify this default list of parameters param.list from outside the package in a safe way.
So since the internal object is a list, the users can simply modify the element of their choice in the list from outside the package. But that is pretty dangerous in my mind because users could
- forget parameters, so I have default values inside the functions for that case
- replace a parameter with another type which can provoke an error or worse, side effects.
Is there a way to let users modify the internal list of parameters without opening the package ? For example I had thought about a function that could replace the .csv file by a new one provided by the user but this brings me back to the First Problem, when I restart the R prompt and reinstall the package, nothing happens unless I usethis::use_data(param.list, overwrite = TRUE) from inside the package. Would another type of data be more useful (e.g. make the data internal) ?
This is my first question on this website but I realize it's quite long and might be taken as a coding style question. But if anyone can see an obvious mistake or misunderstanding of package development in R from my side that would really help me.
Cheers
There's a bit of a preamble before I get to my question, so hang with me!
For an R package I'm working on I'd like to make it as easy as possible for users to partially apply functions inline. I had the idea of using the [] operators to call my partial application function, which I've name "partialApplication." What I aim to achieve is this:
dnorm[mean = 3](1:10)
# Which would be exactly equivalent to:
dnorm(1:10, mean = 3)
To achieve this I tried defining a new [] method for objects of class function, i.e.
`[.function` <- function(...) partialApplication(...)
However, R gives a warning that the [] method for function objects is "locked." (Is there any way to override this?)
My idea seemed to be thwarted, but I thought of one simple solution: I can invent a new S3 class "partialAppliable" and create a [] method for it, i.e.
`[.partialAppliable` = function(...) partialApplication(...)
Then, I can take any function I want and append 'partialAppliable' to its class, and now my method will work.
class(dnorm) = append(class(dnorm), 'partialAppliable')
dnorm[mean = 3](1:10)
# It works!
Now here's my question/problem: I'd like users to be able to use any function they want, so I thought, what if I loop through all the objects in the active environment (using ls) and append 'partialAppliable' to the class of all functions? For instance:
allobjs = unlist(lapply(search(), ls))
#This lists all objects defined in all active packages
for(i in allobjs) {
if(is.function(get(i))) {
curfunc = get(i)
class(curfunc) = append(class(curfunc), 'partialAppliable')
assign(i, curfunc)
}
}
Voilà! It works. (I know, I should probably assign the modified functions back into their original package environments, but you get the picture).
Now, I'm not a professional programmer, but I've picked up that doing this sort of thing (globally modifying all variables in all packages) is generally considered unwise/risky. However, I can't think of any specific problems that will arise. So here's my question: what problems might arise from doing this? Can anyone think of specific functions/packages that will be broken by doing this?
Thanks!
This is similar to what the Defaults package did. The package is archived because the author decided that modifying other package's code is a "very bad thing". I think most people would agree. Just because you can do it, does not mean it's a good idea.
And, no, you most certainly should not assign the modified functions back to their original package environments. CRAN does not like when packages modify the users search path unnecessarily, so I would be surprised if they allowed a package to modify other package's function arguments.
You could work around that by putting all the modified functions in an environment on the search path. But then you have to ensure that environment is always searched first, which means modifying the search path every time another package is loaded.
Changing arguments for functions in other packages also has the potential to make it very difficult for others to reproduce your results because they must have all your argument settings. Unless you always call functions with all their arguments specified, which defeats the purpose of what you're trying to do.
I'm writing some fairly involved R code spread across multiple files and collected together into a package. A problem I've run into on occasion is that I will define a utility function in one file that has the same name as another utility function defined in another file. One of the two definitions gets replaced, leading to unintended behavior. Is there any sort of tool to check for this kind of accidental redefinition? Something that would check that no two top-level assignments foo <- ... in the package assign to the same name?
As pointed out in the comments, the right way to do this is to use packages. Packages give functions their own namespaces automatically, plus they make it very easy to reuse and share code. If you're using RStudio, you can create one with very little effort from the New Project menu.
However, if you can't use packages or namespaces for some reason, there's still a way to do what you want: you can lock a variable (including a function) so that it's not possible to overwrite it.
> pin <- 11
> lockBinding("pin", .GlobalEnv)
> pin <- 12
Error: cannot change value of locked binding for 'pin'
See Binding and Environment Locking for details.
using R and debugging, I often might have a function with several arguments set by default.
e.g.
foo <- function(x=c(3,4,5), y= 'house', dateidx = '1990-01-01'){}
Often I just want to manually run through some lines in the function, while using the pre-set parameters. If the parameter list is long, I have to type or paste each argument to the console manually before stepping through the function.
x=c(3,4,5)
y= 'house'
dateidx = '1990-01-01'
It's ok if the list of arguments is small but if there is a long list of arguments, it gets tedious. Is there some way to just copy the whole set of arguments, paste to console, and do something like unlist, so that all the arguments are passed to the console as if I manually passed each one?
p.s. I'm weakly familiar with the debug tool, but sometimes I find it easier and faster to just troubleshoot lines quickly and manually as above.
There is no easy pre-existing way to do this--mainly because this a problem solved by the debugger.
One could imagine hacking something together that might parse these parameters with a regex and set them automatically--or something like that. However, the effort would be much better spent learning how to use the debugger.
It should be quite quick to test the part of the code you are interested in with the debugger if you learn how to use it. RStudio has a visual debugger. Using this, you can simply mark the command you are interested in testing with a breakpoint and run the script. The script will run until it reaches the breakpoint, then stop there so you can inspect what is happening.
Is there a way, in R, to pop up an error message if a function uses a variable
not declared in the body of the function: i.e, i want someone to flag this type of functions
aha<-function(p){
return(p+n)
}
see; if there happens to be a "n" variable lying somewhere, aha(p=2) will give me an "answer" since R will just take "n" from that mysterious place called the "environment"
If you want to detect such potential problems during the code-writing phase and not during run-time, then the codetools package is your friend.
library(codetools)
aha<-function(p){
return(p+n)
}
#check a specific function:
checkUsage(aha)
#check all loaded functions:
checkUsageEnv(.GlobalEnv)
These will tell you that no visible binding for global variable ‘n’.
Richie's suggestion is very good.
I would just add that you should consider creating unit test cases that would run in a clean R environment. That will also eliminate the concern about global variables and ensures that your functions behave the way that they should. You might want to consider using RUnit for this. I have my test suite scheduled to run every night in a new environment using RScript, and that's very effective and catching any kind of scope issues, etc.
Writing R codes to check other R code is going to be tricky. You'd have to find a way to determine which bits of code were variable declarations, and then try and work out whether they'd already been declared within the function.
EDIT: The previous statement would have been true, but as Aniko pointed out, the hard work has already been done in the codetools package.
One related thing that may be useful to you is to force a variable to be taken from the function itself (rather than from an enclosing environment).
This modified version of your function will always fail, since n is not declared.
aha <- function(p)
{
n <- get("n", inherits=FALSE)
return(p+n)
}