I\'m trying to filter some data, with functions in R relatives to data frames. But in the second function, it gives me the following error: cannot change the value of locked bin
Sometimes, one does want to violate the spirit of functional languages.
I am not sure why, in R, a locked function prevents you from creating a variable at the same level, but the problem the OP has is because the function df()
is locked in an environment above that of the current environment (which <<-
finds).
Here is some code (probably not the best) to find the environment in which df
is (already) bound (i.e., already exists), and then check to see if it is, in fact, locked in that environment.
> x <- environment(); while(TRUE) { print(exists("df", x)); x <- parent.env(x) }
[1] TRUE
[1] TRUE
[1] TRUE
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
Error in parent.env(x) : the empty environment has no parent
> bindingIsLocked("df", parent.env(parent.env(environment())))
[1] TRUE
>
In this case, creating a variable df
in the "global" context should solve the problem. i.e.,
df <- NULL
filter.data = function(x, dir = ".") {
load.data(x, dir)
df <<- dados_reais[,c(1,2,3,4,9,10,12)]
}
Alternatively, changing the name of the variable (from df
, say, to myveryowndf
) should also fix the problem.
The reason is that <<-
and <-
work differently.
x <- val
means "assign the value val
to the name x
in the current scope." That's the assignment operator you should usually use.
x <<- val
means "go search for a name x
in the current scope and its enclosing scopes. As soon as you find it, assign the value val
to it and stop. If you don't find it, create a new variable x
in the broadest scope (global) and assign it the value val
."
In your case, your name choice of df
was somewhat unlucky: there's a built-in function df
(in the stats
namespace) for computing the density of Snedecor's F distribution function. Your <<-
assignment found that, tried to change its value to dados_reais[,c(1,2,3,4,9,10,12)]
, and refused (because the built-in df
function is "locked", i.e. immutable). An easier example showing the issue is this:
df <<- 5
# Error: cannot change value of locked binding for 'df'
As demonstrated, R's variables and functions share the same namespaces (or, more accurately: R's functions are typically stored in the same symbol tables [environments] that all the other variables are, they're not "special" like in many other languages). So does that mean that you shouldn't ever use a variable like df
or min
or q
or t
, that clashes with a built-in function's name? No, generally it's not a big deal, because when you do min(x)
, R knows to look for a function called min
, not any old symbol table entry called min
, so it uses something like get("min", mode="function")
to make sure it doesn't accidentally find some variable you've defined that happens to be called min
.
That said, sometimes you do get some name collisions that are a little sneaky. For example, if you think you have a data.frame
called df
, but you forgot to actually create it, you might see an error like this:
df[1, 5]
# Error in df[1, 5] : object of type 'closure' is not subsettable
It's saying that the function df
(a function in R is an "object of type 'closure'") can't be indexed with square brackets like that. File that somewhere in your brain, because if you work with R long enough, you're bound to see that error once in a while.
You should be avoiding <<-
. That creates function with side-effects which run contrary to the spirit of functional languages. Try
load.data <- function(x,dir = ".") {
read.csv(paste(dir,x,sep="/"), header = FALSE, sep = "\t", dec = ".", col.names = c("Seq","Allele","Peptide","Identity","Pos","Core","Core-Rel", "Um-log50k(aff)","Affinity(nM)","Rank","Exp_Bind","Binding Level"))
}
filter.data <- function(x, dir = ".") {
load.data(x, dir)[,c(1,2,3,4,9,10,12)]
}
df <- filter.data("mypath.csv")
I had this problem when creating function inside a package, and I was using <<-
inside exported function to overwrite some functions from different package. I've created hack with env <- parent.env()
and adding this to parent env instead since the function was always called at top level. So env will be scope of the code that call the function not the package.
here is complete code of this function, it overwrite shiny and current package functions:
#' @export
useMocks <- function() {
observeEvent <- battery::observeEventMock
assignInNamespace("observeEvent", observeEvent, "battery")
assignInNamespace("observeEvent", observeEvent, "shiny")
isolate <- battery::isolate
assignInNamespace("isolate", isolate, "shiny")
makeReactiveBinding <- battery::makeReactiveBinding
assignInNamespace("makeReactiveBinding", makeReactiveBinding, "shiny")
renderUI <- battery::renderUI
assignInNamespace("renderUI", renderUI, "shiny")
## we modify the parent frame so it update environment when function is called not the package
env <- parent.frame()
env$observeEvent <- observeEvent
env$isolate <- isolate
env$renderUI <- renderUI
}
using <<-
throw this error here because you can't modify the package scope.
observeEvent <<- battery::observeEventMock
this don't work