What are Replacement Functions in R?
Asked Answered
I

4

52

I searched for a reference to learn about replacement functions in R, but I haven't found any yet. I'm trying to understand the concept of the replacement functions in R. I have the code below but I don't understand it:

"cutoff<-" <- function(x, value){
 x[x > value] <- Inf
 x
 }

and then we call cutoff with:

 cutoff(x) <- 65

Could anyone explain what a replacement function is in R?

Interlinear answered 19/7, 2012 at 14:35 Comment(3)
This is a perfectly acceptable Q for SOSkyrocket
possible duplicate of levels<-( What sorcery is this?Horizon
See Extending R.- John Chambers (2016): page 73 to 79 of the Chapter 5Catholicize
S
55

When you call

cutoff(x) <- 65

you are in effect calling

x <- "cutoff<-"(x = x, value = 65)

The name of the function has to be quoted as it is a syntactically valid but non-standard name and the parser would interpret <- as the operator not as part of the function name if it weren't quoted.

"cutoff<-"() is just like any other function (albeit with a weird name); it makes a change to its input argument on the basis of value (in this case it is setting any value in x greater than 65 to Inf (infinite)).

The magic is really being done when you call the function like this

cutoff(x) <- 65

because R is parsing that and pulling out the various bits to make the real call shown above.

More generically we have

FUN(obj) <- value

R finds function "FUN<-"() and sets up the call by passing obj and value into "FUN<-"() and arranges for the result of "FUN<-"() to be assigned back to obj, hence it calls:

obj <- "FUN<-"(obj, value)

A useful reference for this information is the R Language Definition Section 3.4.4: Subset assignment ; the discussion is a bit oblique, but seems to be the most official reference there is (replacement functions are mentioned in passing in the R FAQ (differences between R and S-PLUS), and in the R language reference (various technical issues), but I haven't found any further discussion in official documentation).

Skyrocket answered 19/7, 2012 at 14:46 Comment(0)
D
10

Gavin provides an excellent discussion of the interpretation of the replacement function. I wanted to provide a reference since you also asked for that: R Language Definition Section 3.4.4: Subset assignment.

Denunciate answered 19/7, 2012 at 16:54 Comment(1)
useful, but link-only answer ... I'm editing @GavinSimpson's answer to add it ...Miranda
S
10

As a complement to the accepted answer I would like to note that replacement functions can be defined also for non standard functions, namely operators (see ?Syntax) and control flow constructs. (see ?Control).

Note also that it is perfectly acceptable to design a generic and associated methods for replacement functions.

operators

When defining a new class it is common to define S3 methods for $<-, [[<- and [<-, some examples are data.table:::`$<-.data.table`, data.table:::`[<-.data.table`, or tibble:::`$.tbl_df`.

However for any other operator we can write a replacement function, some examples :

`!<-` <- function(x, value) !value
x <- NULL # x needs to exist before replacement functions are used!
!x <- TRUE
x
#> [1] FALSE

`==<-` <- function(e1, e2, value) replace(e1, e1 == e2, value)
x <- 1:3
x == 2 <- 200
x
#> [1]   1 200   3

`(<-` <- function(x, value) sapply(x, value, USE.NAMES = FALSE)
x <- c("foo", "bar")
(x) <- toupper
x 
#> [1] "FOO" "BAR"

`%chrtr%<-` <- function(e1, e2, value) {
  chartr(e2, value, e1)
}

x <- "woot"
x %chrtr% "o" <- "a"
x
#> [1] "waat"

we can even define <-<-, but the parser will prevent its usage if we call x <- y <- z, so we need to use the left to right assignment symbol

`<-<-` <- function(e1, e2, value){
  paste(e2, e1, value)
}
x <- "b"
"a" -> x <- "c"
x
#> [1] "a b c"

Fun fact, <<- can have a double role

x <- 1:3
x < 2 <- NA # this fails but `<<-` was called!
#> Error in x < 2 <- NA: incorrect number of arguments to "<<-"

# ok let's define it then!
`<<-` <- function(x, y, value){
  if (missing(value)) {
    eval.parent(substitute(.Primitive("<<-")(x, y)))
  } else {
    replace(x, x < y, value)
  }
}
x < 2 <- NA
x
#> [1] NA  2  3
x <<- "still works"
x
#> [1] "still works"

control flow constructs

These are in practice seldom encountered (in fact I'm responsible for the only practical use I know, in defining for<- for my package pbfor), but R is flexible enough, or crazy enough, to allow us to define them. However to actually use them, due to the way control flow constructs are parsed, we need to use the left to right assignment ->.

`repeat<-` <- function(x, value) replicate(value, x)
x <- "foo"
3 -> repeat x
x
#> [1] "foo" "foo" "foo"

function<-

function<- can be defined in principle but to the extent of my knowledge we can't do anything with it.

`function<-` <- function(x,value){NULL}

3 -> function(arg) {}
#> Error in function(arg) {: target of assignment expands to non-language object
Senatorial answered 6/11, 2019 at 16:7 Comment(1)
I can’t decide whether to up- or downvote this answer …! 😋Bookcraft
D
3

Remember, in R everything operation is a function call (therefore also the assignment operations) and everything that exists is an object. Replacement functions act as if they modify their arguments in place such as in

colnames(d) <- c("Input", "Output")

They have the identifier <- at the end of their name and return a modified copy of the argument object (non-primitive replacement functions) or the same object (primitive replacement functions)

At the R prompt, the following will not work:

> `second` <- function(x, value) {
+   x[2] <- value
+   x
+ }
> x <- 1:10
> x
 [1]  1  2  3  4  5  6  7  8  9 10
> second(x) <- 9
Error in second(x) <- 9: couldn't find function "second<-"

As you can see, R is searching the environment not for second but for second<-. So lets do the same thing but using such a function identifier instead:

> `second<-` <- function(x, value) {
+   x[2] <- value
+   x
+ }

Now, the assignment at the second position of the vector works:

> second(x) <- 9
> x
 [1]  1  9  3  4  5  6  7  8  9 10

I also wrote a simple script to list all replacement functions in R base package, find it here.

Dort answered 7/5, 2016 at 19:45 Comment(4)
Wrote a comment on your blog regarding the script to pull out all the replacement functions: "Aren't you missing those replacement functions, that have the "<-" in between? Like e.g. split<-.default and split<-.data.frame? "Ornstead
@ManuelS I'm checking: > split<-.data.frame Fehler: Objekt '.data.frame' nicht gefunden > split<-.default Fehler: Objekt '.default' nicht gefunden >Dort
@ManuelS Hadley didn't write anything about such named functions in Advanced R. Do you have a reference for the functions you mention?Dort
You have to call them with backticks e.g. `split<-.data.frame`. For a way to extract them see my questionOrnstead

© 2022 - 2024 — McMap. All rights reserved.