R Error - cannot change value of locked binding for 'df'

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")

Answer:

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'

Incidentally:

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.


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.

Tags:

R