r super assignment operator

Secure Your Spot in Our PCA Online Course Starting on April 02 (Click for More Info)

Joachim Schork Image Course

Assignment Operators in R (3 Examples) | Comparing = vs. <- vs. <<-

On this page you’ll learn how to apply the different assignment operators in the R programming language .

The content of the article is structured as follows:

Let’s dive right into the exemplifying R syntax!

Example 1: Why You Should Use <- Instead of = in R

Generally speaking, there is a preference in the R programming community to use an arrow (i.e. <-) instead of an equal sign (i.e. =) for assignment.

In my opinion, it makes a lot of sense to stick to this convention to produce scripts that are easy to read for other R programmers.

However, you should also take care about the spacing when assigning in R. False spacing can even lead to error messages .

For instance, the following R code checks whether x is smaller than minus five due to the false blank between < and -:

A properly working assignment could look as follows:

However, this code is hard to read, since the missing space makes it difficult to differentiate between the different symbols and numbers.

In my opinion, the best way to assign in R is to put a blank before and after the assignment arrow:

As mentioned before, the difference between <- and = is mainly due to programming style . However, the following R code using an equal sign would also work:

In the following example, I’ll show a situation where <- and = do not lead to the same result. So keep on reading!

Example 2: When <- is Really Different Compared to =

In this Example, I’ll illustrate some substantial differences between assignment arrows and equal signs.

Let’s assume that we want to compute the mean of a vector ranging from 1 to 5. Then, we could use the following R code:

However, if we want to have a look at the vector x that we have used within the mean function, we get an error message:

Let’s compare this to exactly the same R code but with assignment arrow instead of an equal sign:

The output of the mean function is the same. However, the assignment arrow also stored the values in a new data object x:

This example shows a meaningful difference between = and <-. While the equal sign doesn’t store the used values outside of a function, the assignment arrow saves them in a new data object that can be used outside the function.

Example 3: The Difference Between <- and <<-

So far, we have only compared <- and =. However, there is another assignment method we have to discuss: The double assignment arrow <<- (also called scoping assignment).

The following code illustrates the difference between <- and <<- in R. This difference mainly gets visible when applying user-defined functions .

Let’s manually create a function that contains a single assignment arrow:

Now, let’s apply this function in R:

The data object x_fun1, to which we have assigned the value 5 within the function, does not exist:

Let’s do the same with a double assignment arrow:

Let’s apply the function:

And now let’s return the data object x_fun2:

As you can see based on the previous output of the RStudio console, the assignment via <<- saved the data object in the global environment outside of the user-defined function.

Video & Further Resources

I have recently released a video on my YouTube channel , which explains the R syntax of this tutorial. You can find the video below:

The YouTube video will be added soon.

In addition to the video, I can recommend to have a look at the other articles on this website.

  • R Programming Examples

In summary: You learned on this page how to use assignment operators in the R programming language. If you have further questions, please let me know in the comments.

assignment-operators-in-r How to use different assignment operators in R – 3 R programming examples – R programming language tutorial – Actionable R programming syntax in RStudio

Subscribe to the Statistics Globe Newsletter

Get regular updates on the latest tutorials, offers & news at Statistics Globe. I hate spam & you may opt out anytime: Privacy Policy .

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Post Comment

Joachim Schork Statistician Programmer

I’m Joachim Schork. On this website, I provide statistics tutorials as well as code in Python and R programming.

Statistics Globe Newsletter

Get regular updates on the latest tutorials, offers & news at Statistics Globe. I hate spam & you may opt out anytime: Privacy Policy .

Related Tutorials

Locate & Extract Regular Expression Match in R (2 Examples)

Locate & Extract Regular Expression Match in R (2 Examples)

Sys.timezone Function in R (Example) | Get Name of Current Time Zone

Sys.timezone Function in R (Example) | Get Name of Current Time Zone

Beginning Computer Science with R

14 functional programming in r.

It was simple, but you know, it’s always simple when you’ve done it.

In this Chapter we aren’t going to cover any fundamentally new R powers. Instead we’ll get acquainted with just one aspect of a computer programming paradigm known as functional programming . We will examine a set of R-functions for which functions themselves are supplied as arguments. These functions allow us to accomplish a great deal of computation in rather concise and expressive code. Not only are they useful in R itself, but they help you to reason abstractly about computation and prepare you for functional-programming aspects of other programming languages.

14.1 Programming Paradigms

Let us begin by exploring the notion of a programming paradigm in general. We will go on in this Chapter to consider two programming paradigms for which R provides considerable support. In the next Chapter we will consider a third programming paradigm that exists in R.

A programming paradigm is a way to describe some of the features of programming languages. Often a paradigm includes principles concerning the use of these features, or embodies a view that these features have special importance and utility in good programming practice.

14.1.1 Procedural Programming

One of the older programming paradigms in existence is procedural programming . It is supported in many popular languages and is often the first paradigm within which beginners learn to program. In fact, if one’s programming does not progress beyond a rudimentary level, one may never become aware that one is working within the procedural paradigm—or any paradigm at all, for that matter.

Before we define procedural programming, let’s illustrate it with an example. Almost any of the programs we have written so far would do as examples; for specificity, let’s consider the following snippet of code that produces from the data frame m111survey a new, smaller frame consisting of just the numerical variables:

By now there is nothing mysterious about the above code-snippet. What we want to become conscious of is the approach we have taken to the problem of selecting the numerical variables. In particular, observe that:

  • We worked throughout with data , some of which, like m111survey , was given to us and some of which we created on our own to help solve the problem. For example, we created the variable cols . Note also the very helpful index-variable i in the for -loop. We set up the data structure isNumerical in order to hold a set of data ( TRUE s and FALSE s).
  • We relied on various procedures to create data and to manipulate that data in order to produce the desired result. Some of the procedures appeared as special blocks of code—most notably the for -loop. Other procedures took the form of functions. As we know, a function encapsulates a useful procedure so that it can be easily reused in a wide variety of circumstances, without the user having to know the details of how it works. We know that names() will give us the vector of names of the columns of m111survey , that length() will tell us how many names, there are, that is.numeric() will tell us whether or not a given variable in m111survey is a numerical variable, and so on. The procedures embodied in these functions were written by other folks and we could examine them if we had the time and interest, but for the most part we are content simply to know how to access them.

Procedural programming is a paradigm that solves problems with programs that can be broken up into collections of variables, data structures and procedures. In this paradigm, there is a sharp distinction between variables and data structures on the one hand and procedures on the other. Variables and data structures are data —they are the “stuff” that a program manipulates to produce other data, other “stuff.” Procedures do the manipulating, turning stuff into other stuff.

14.2 The Functional Programming Paradigm

Let us now turn to the second of the two major programming paradigms that we study in this Chapter: Functional Programming.

14.2.1 The Ubiquity of Functions in R

Let’s a bit more closely at our code snippet. Notice how prominently functions figure into it, on nearly every line. In fact, every line calls at least one function! This might seem unbelievable: after all, consider the line below:

There don’t appear to be any functions being called, here! But in fact two functions get called:

The so-called assignment operator <- is actually a function in disguise: the more official—albeit less readable—form of variable <- value is:

Thus, to assign the value 3 to that variable a one could write:

The sub-setting operator for vectors [ , more formally known as extraction (see help(Extract) ) is also a function. The expression m111survey[, isNumerical] is actually the following function-call in disguise:

Indeed functions are ubiquitous in R. This is part of the significance of the following well-known remark by a developer of S, the precursor-language of R:

“To understand computations in R, two slogans are helpful:

  • Everything that exists is an object.
  • Everything that happens is a function call.”

—John Chambers

The second slogan indicates that functions are everywhere in R. It also corresponds to the first principle of the functional programming paradigm, namely:

Computation is regarded as the evaluation of functions.

14.2.2 Functions as First-Class Citizens

So functions are ubiquitous in R. Another interesting thing about them is that even though they seem to be associated with procedures—after all, they make things happen—they are, nevertheless, also objects. They are data, or “stuff” if you like.

This may not seem obvious at first. But look at the following code, where you can ask what type of thing a function is:

The so-called “primitive” functions of R—the functions written not in R but in C-code—are “built in” objects. On the other hand, consider this user-defined function:

Functions other than primitive functions are objects of type “closure.” 34

If a function can be a certain type of thing, then it must be a “thing”—an object, something you can manipulate. For example, you can put functions in a list:

Very importantly, you can make functions serve as argument for other functions, and functions can return other functions as their results. The following example demonstrates both of these possibilities.

In fact, in R functions can be treated just like any variable. In computer programming, we say that such functions are first-class citizens .

Although it is not often stated as a separate principle of the functional programming paradigm it is true that in languages that provide support for functional programming, the following principle holds true:

Functions are first-class citizens.

14.2.3 Minimize Side Effects

In the code-snippet under consideration, we note that there are two types of functions:

  • functions that return a value;
  • functions that provide output to the console or make a change in the Global Environment.

Example of the first type of function included:

  • seq_along()
  • is.numeric()
  • the extraction-function `[`()

A function that produced output to the console was str() .

The assignment function `<-`() added cols , isNumerical and numsm111 to the Global Environment, and also made changes to isNumerical in the course of the for -loop.

Of course we have seen examples of functions that do two of these things at once, for example:

In computer programming, output to the console, along with changes of state —changes to the Global Environment or to the file structure of your computer—are called side-effects . Functions that only return values and do not produce side-effects are called pure functions.

A third principle of the functional programming paradigm is:

Functions should be pure.

Now this principle is difficult to adhere to, and in fact if you were to adhere strictly to it in R then your programs would never “do” anything. There do exist quite practical programming languages in which all of the functions are pure—and this leads to some very interesting features such as that the order in which operations are evaluated doesn’t affect what the function returns—but these “purely functional” languages manage purity by having other objects besides functions produce the necessary side-effects. In R we happily let our functions have side-effects: we certainly want to do some assignment, and print things out to the console from time to time.

One way that R does support the third principle of functional programming is that it makes it easy to avoid having your functions modify the Global Environment. To see this consider the following example:

This is as we expect: the variable heavenly_ash exists only in the run-time environment that is created in the call to add_three() . As soon as the function finishes execution that environment dies, and heavenly_hash dies long with it. In particular, it never becomes part of the Global Environment.

If you really want your functions to modify the Global Environment—or any environment other than its run-time environment, for that matter—then you have to take special measures. You could, for example, use the super-assignment operator <<- :

The super-assignment operator looks for the name heavenly_hash in the parent environment of the run-time environment, If if finds heavenly_hash there then it changes its value to 5 and stops. Otherwise it looks in the next parent up, and so on until it reaches the Global Environment, at which point if it doesn’t find a heavenly_hash it creates one and gives it the value. In the example above, assuming you ran the function from the console, the parent environment is the Global Environment and the function has made a change to it: a side-effect.

Except in the case of explicit assignment functions like `<-`() , changes made by functions to the Global Environment can be quite problematic. After all, we are used to using functions without having to look inside them to see how they do their work. Even if we once wrote the function ourselves, we may not remember how it works, so if it creates side effects we may not remember that it does, and calling them could interfere with other important work that the program is doing. (If the program already has heavenly_hash in the Global Environment and the we call a function that changes it value, we could be in for big trouble.) Accordingly, R supports the third principle of functional programming to the extent of making it easy for you to avoid function calls that change your Global Environment.

14.2.4 Procedures as Higher-Order Function Calls

The last principle of the functional programming paradigms that we will state here isn’t really a formal principle: it is really more an indication of the programming style that prevails in languages where functions are first-class objects and that provide other support for functional programming. The final principle is:

As much as possible, procedures should be accomplished by function calls, In particular, loops should be replaced by calls to higher-order functions.

A higher-order function is simply a function that takes other functions as arguments. R provides a nice set of higher-order functions, many of which substitute for iterative procedures such as loops. In subsequent sections we will study the some of the most important higher-order functions, and see how they allow us to express some fairly complex procedures in a concise and readable way. You will also see how this style really blurs the distinction—so fundamental to procedural programming—between data and procedures. In functional programming, functions ARE data, and procedures are just function calls.

14.2.5 Functional Programming: A Summary

For our purposes, the principles of the functional programming paradigm are as follows:

  • Computation consists in the evaluation of functions.
  • Functions are first-class citizens in the language.
  • Functions should only return values; they should not produce side-effects. (At the very least they should not modify the Global Environment unless they are dedicated to assignment in the first place.)
  • As much as possible, procedures should be written in terms of function calls. In particular, loops should be replaced by calls to higher-order functions.

14.3 purrr Higher-Order Functions for Iteration

In the remainder of the Chapter we will study important higher-order functions: functions that take a function as an argument and apply that function to each element of another data structure. As we have said previously, such functions often serve as alternatives to loops.

The higher-order functions we study come from the package purrr , which is attached whenever we load the tidy-verse.

14.3.1 map() and Variations

Suppose that we want to generate five vectors, each of which consists of ten numbers randomly chosen between 0 and 1. We accomplish the task with a loop, as follows:

If we wanted the vectors to have length \(1, 4, 9, 16,\) and 25, then we could write:

In the first example, the elements in the vector 1:5 didn’t matter—we wanted a vector of length ten each time—and in the second case the elements in the 1:5 did matter, in that they determined the lengths of the five vectors produced. Of course in general we could apply runif() to each element of any vector at all, like this:

If we can apply runif() to each element of a vector, why not apply an arbitrary function to each element? That’s what the function map() will do for us. The general form of map() is:

In the template above:

  • .x can be a list or any atomic vector;
  • .f is a function that is to be applied to each element of .x . In the default operation of map() , each element of .x becomes in turn the first argument of .f .
  • ... consists of other arguments that are supplied as arguments for the .f function, in case you have to set other parameters of the function in order to get it to perform in the way you would like.

The result is always a list.

With map() we can get the list in our second example as follows:

If we had wanted the random numbers to be between—say—4 and 8, then we would supply extra arguments to runif() as follows:

The default behavior of map() is that the .x vector supplies the first argument of .f . However, if some ... parameters are supplied then .x substitutes for the first parameter that is not mentioned in ... . In the above example, the min and max parameters are the second and third parameters for runif() so .x substitutes for the first parameter—the one that determines how many random numbers will be generated. In the example below, the vector lower_bounds substitutes for min , the second parameter of runif() :

Sometimes we wish to vary two or more of the parameters of function. In that case we use pmap() . The first parameter of pmap() is named .l and takes a list of vectors (or lists). For example:

Observe that pmap() knows to interpret the first element of the input-list—the vector how_many as giving the values of the first argument of runif() . The second parameter of runif() ( min ) is set at 0, so pmap() deduces that upper_bounds —the second element of the input-list—gives the values for the next next parameter in line, the parameter max .

One might just as well use pmap() to vary all three parameters:

The .f parameter can be any function, including one that you define yourself. Here’s an example:

You could also set f to be a function that you write on the spot, without even bothering to give it a name:

In computer programming a function is called anonymous when it is not the value bound to some name. .

map() allows a shortcut for defining anonymous functions. The above call could have been written as:

The ~ indicates that the body of the function is about to be begin. The . stands for the parameter of the function.

When we introduced map() we said that .x was a vector or a list, In fact .x could be an object that can be coerced into a list. Hence it is quite common to use map() with the data frames: the frame is turned into a list, each element of which is a column of the frame. Here is an example:

Note that the elements of the returned list inherit the names of the input data frame. This holds for any named input:

When the result can take on a form more simple than a list, it is possible to use variants of map() such as:

Thus we could obtain a named integer vector of the number of NA -values for each variable in m11survey as follows:

Here are the types of each variable:

Here is a statement of whether or not each variable is a factor:

14.3.2 walk() and Variations

walk() is similar to map() , but is used when we are interested in producing side-effects. It applies its .f argument to each element of .x is was given, but also returns the .x in case we want to pipe it into some other function.

Here we use walk() only for its side-effect: we re-write a familiar function to print a pattern to the Console without using a loop.

The next example illustrates the use of the return-value of walk() . We would like to save plots of all numerical variables from the data frame m111survey , and also print summaries of them to the Console.

First we create a directory to hold the plots:

Next, we get the numerical variables in m111survey :

We used purrr::keep() , which retains only the elements of its input .x such that its second argument .p ( a function that returns a single TRUE or FALSE ) returns TRUE .

We will also need the names of the numerical variables:

We need a function to save the density plot of a single numerical variable:

We also need a function to produce a summary of a single numerical variable:

Now we walk through the process. We will actually use the function pwalk() , which will take the following inputs:

  • .x (a list with two elements: the data frame of numerical variables and the vector of the names of these variables), and
  • .f (the function saveGraph , to make and save a density plot)

We also use pmap_dfr() , which takes a list consisting of the data frame and variable-names and constructs a data frame row-by-row, with each row summarizing one of the variables.

Check the plots directory; it should contain these files:

  • density_diff.ideal.act.png
  • density_fastest.png
  • density.GPA.png
  • density_height.png
  • density_ideal_ht.png
  • density_sleep.png

14.3.3 Example: Flowery Meadow Redux

In Section 4.3.3.1 we simulated people walking through a meadow, picking flowers until they had picked a desired number of flowers of a desired color. In Section 9.3.3 we used lists to store the results of such a simulation. Now we’ll see how to store the results as a data frame.

First, we modify the helper-function that simulates one person picking flowers so that, instead of returning a vector of colors, it returns a data frame:

Note that the new function takes an extra parameter person , the name of the person picking the flowers.

Let’s try it out:

Now we write the function to make the data frame of results for a group of people. pmap() will come in handy.

Here are the results:

14.3.4 Practice Exercises

Use map() to produce a list of the squares of the whole numbers from 1 to 10.

Use map_dbl() to produce a numerical vector of the squares of the whole numbers from 1 to 10.

Use map_chr to state the type of each element of the following list:

Here are some people:

The following vector tells whether or not each person is a Grand Poo-Bah:

Use pwalk() to properly greet each person. The result in the console should be as follows:

14.3.5 Solutions to the Practice Exercises

This is more verbose, but works just as well:

Again the more verbose approach works just as well:

14.4 Other purrr Higher-Order Functions

14.4.1 keep() and discard().

keep() is similar to dplyr ’s filter() , but whereas filter() chooses rows of a data frame based on a given condition, keep() chooses the elements of the input list or vector .x based on a condition named .p .

discard(.x,, . p = condition) is equivalent to keep(.x, .p = !condition) . Thus:

14.4.2 reduce()

Another important member of the purrr family is reduce() . Given a vector .x and a function .f that takes two inputs, reduce() does the following:

  • applies f to elements 1 and 2 of .x , getting a result;
  • applies f to the result and to element 3 of .x , getting another result;
  • applies f to this new result and to element 4 of .x , getting yet another result …
  • … and so on until all of the elements of .x have been exhausted.
  • then reduce() returns the final result in the above series of operations.

For example, suppose that you want to add up the elements of the vector:

Of course you could just use:

After all, sum() has been written to apply to many elements at once. But what if addition could only be done two numbers at a time? How might you proceed? You could:

  • add the 3 and 1 of (the first two elements of vec ), getting 4;
  • then add 4 to 4, the third element of vec , getting 8;
  • then add 8 to 6, the final element of vec , getting 14;
  • then return 14.

reduce() operates in this way.

Can you see how reduce() gets its name? Step by step, it “reduces” its .x argument, which may consist of many elements, to a single value.

A common application of reduce() is to take an operation that is defined on only two items and extend it to operate on any number of items. Consider, for example, the function intersect() , , which will find the intersection of any two vectors of the same type:

You cannot intersect three or more vectors at once:

With reduce() you can intersect as many vectors as you like, provided that they are first stored in a list.

You can write your own function to supply as the argument for .f , but it has to be able to operate on two arguments. reduce() will take the first argument of the .f function to be what has been “accumulated” so far, and the second argument of the .f function—the value to be combined with what has been accumulated—will be provided by the current element of .x .

As a simple example, let’s write our own reduce-summer in a way that shows the user the reduction process at work:

When you write your own .f function, it’s a good idea to use names for the parameters that remind you of their role in the reduction process. acc (for “accumulated”) and curr (for “current”) are used above.

reduce() can take an argument called .init . When this argument is given a value, operation begins by applying to .f to .init and the first element of .x . For example:

14.4.2.1 An Extended Example of Reduction

Let’s apply reduce() with .init to the task of making a truth table : the set of all \(2^n\) logical vectors of a given length \(n\) .

The set \(S_1\) of vectors of length \(n = 1\) consists of only two vectors:

Now consider a systematic way to construct the set \(S_2\) of all the vectors of length two. We know that there are four such vectors:

Observe that the first two of them begin with TRUE and end with the set \(S_1\) of vectors of length one:

The last two of them begin with FALSE and also end with \(S_1\) :

Now consider \(S_3\) , the set of all eight vectors of length three:

Observe that the first four of them end begin with TRUE and and with the vectors of \(S_2\) :

The last four of them begin with FALSE and also end with the vectors of \(S_2\) :

The pattern is now clear. If for any \(m \ge 1\) you are in possession of the \(2^m \times m\) matrix \(S_m\) of all possible vectors of length \(m\) , then to obtain the \(2^{m+1} \times (m+1)\) matrix \(S_{m+1}\) of all possible vectors of length \(m+1\) you should:

  • stack \(2^m\) TRUE s on top of \(2^m\) FALSE s, creating a \(2^{m+1} \times 1\) matrix \(U\) ;
  • stack the \(S_m\) underneath itself, creating a \(2^{m+1} \times m\) matrix \(V\) ;
  • place \(U\) next to \(V\) .

reduce() with .init set to \(S_1\) is appropriate for this iterative building process. Here is an implementation:

We have included a verbose option so we can watch the process as it unfolds.

Note also that the parameters for the .f function are named:

  • acc (what has been “accumulated” up to the current step), and
  • value (the value of .x at the current step).

It’s conventional to give these or similar names to the parameters of the building-function.

Of course in practice we would not turn on the verbose option:

14.4.3 Practice Exercises

The operator * (multiplication) is really a function:

But it can only multiply two numbers at once. The R-function prod() cna handle as many numbers as you like:

Use reduce() and * to write your own function product() that takes a numerical vector vec and returns the product of the elements of the vector. It should work liek this:

( Hint : in the call to reduce() you will have to the refer to the * -function as `*` .)

Modify the function product() so that it in a single call to reduce() it multiplies the number 2 by the product of the elements of vec . ( Hint : set .init to an appropriate value.)

The data frame iris gives information on 150 irises. Use keep() to create a new data frame that includes only the numerical variables having a mean greater than 3.5.

14.4.4 Solutions to the Practice Exercises

The following does not work. Why?

14.5 Functionals vs. Loops

The higher-order functions we have studied in this chapter are often called functionals. As we pointed out earlier, they deliver results that could have been produced by a writing a loop of some sort.

Once you get used to functionals, you will find that they are often more “expressive” than loops—easier for others to read and to understand, and less prone to bugs. Also, many of them are optimized by the developers of R to run a bit faster than an ordinary loop written in R.

For example, consider the following list. It consists of ten thousand vectors, each of which contains 100 randomly-generated numbers.

If we want the mean of each vector, we could write a loop:

Or we could use map_dbl() :

Comparing the two using system.time() , on my machine I got:

For the loop, I get:

The map-function is a bit faster, but the difference is small.

Remember also that vectorization is much faster than looping, and is also usually quite expressive, so don’t struggle to take a functional approach when vectorization is possible. (This advice applies to a several examples from this Chapter, in which the desired computations had already been accomplished in earlier chapters by some form of vectorization.)

14.6 Conclusion

In this Chapter we have concentrated on only a single aspect of the Functional Programming paradigm: exploiting the fact that functions are first-class citizens in R, we studied a number of higher-order functions that can substitute for loops. There is certainly a great deal more to Functional Programming than the mere avoidance of loops, but we’ll end our study at this point. Familiarity with higher-order functions will stand you in good stead when you begin, in subsequent courses on web programming, to learn the JavaScript language. JavaScript makes constant use of higher-order functions!

A programming paradigm that solves problems with programs that can be broken up into collections of variables, data structures and procedures. This paradigm tends to draw a sharp distinction between variables and data structures on the one hand and procedures on the other.

A programming paradigm that stresses the central role of functions. Some of its basic principles are:

  • Functions should only return values; they should not produce side-effects.
  • As much as possible, procedures should be written in terms of function calls.

A function that does not produce side-effects.

A change in the state of the program (i.e., a change in the Global Environment) or any interaction external to the program (i.e., printing to the console).

A function that takes another function as an argument.

A function that does not have a name.

The act of rewriting computer code so that it performs the same task as before, but in a different way. (This is usually done to make the code more human-readable or to make it perform the task more quickly.)

Links to Class Slides

Quarto Presentations that I sometimes use in class:

  • Sections 14.1 - 14.2
  • Sections 14.3 - 14.4

r super assignment operator

Explain in words what the following line of code produces when given a numerical vector y :

In the course of your explanation, say whether the result is a vector or a list.

Which do you think works faster for a given numerical vector y ? This code:

Or this code?

Justify your answer with a convincing example, using system.time() . What moral do you draw from this?

To refactor computer code is to rewrite the code so that it does the same thing, but in a different way. We might refactor code in order to make it more readable by humans, or to make it perform its task more quickly.

Refactor the following code so that it uses keep() instead of a loop:

The following function produces a list of vectors of uniform random numbers, where the lower and upper bounds of the numbers are given by the arguments to the parameters lower and upper respectively, and the number of vectors in the list and the number of random numbers in each vector are given by a vector supplied to the parameter vecs .

Refactor the code for random_sims() so that it uses map() instead of a loop.

The following enhanced version of randomSims() is even more flexible, as it allows both the upper and lower limits for the randomly-generated numbers to vary with each vector of numbers that is produced.

Use pmap() to refactor the code for random_sims2() so as to avoid using the loop.

Supposing that y is a numerical vector, explain in words what the following code produces:

Write a line of code using the sub-setting operator [ that produces the same result as the code in the previous problem.

Use keep() to write a function called odd_members() that, given any numerical vector, returns a vector containing the odd numbers of the given vector. Your function should take a single argument called vec , the given vector. A typical example of use would be as follows:

You are given the following list of character vectors:

Use reduce() and the union() function to obtain a character vector that is the union of all the vectors in lst .

Remember the function subStrings() from the exercises of the Chapter on Strings? Refactor it so that it does EXACTLY the same thing but makes no use of loops.

Solve Part One of Advent of Code 2022 Day 3 . Save your input file in your submit folder with the filename input_aoc_2022-03.txt , and read in the input data, naming it input , using the following code:

Solve Part One of Advent of Code 2022 Day 25 . Save your input file in your submit folder with the filename input_aoc_2022-25.txt , and read in the input data, naming it input , using the following code:

( Hint : The snafu numbering-system bears some relationship to base-5 numbering. After reviewing Section 11.4 , write two helper-functions: one to convert numbers to “base-snafu” and another to convert base-snafu representations to numbers.)

10 Function factories

10.1 introduction.

A function factory is a function that makes functions. Here’s a very simple example: we use a function factory ( power1() ) to make two child functions ( square() and cube() ):

Don’t worry if this doesn’t make sense yet, it should by the end of the chapter!

I’ll call square() and cube() manufactured functions , but this is just a term to ease communication with other humans: from R’s perspective they are no different to functions created any other way.

You have already learned about the individual components that make function factories possible:

In Section 6.2.3 , you learned about R’s first-class functions. In R, you bind a function to a name in the same way as you bind any object to a name: with <- .

In Section 7.4.2 , you learned that a function captures (encloses) the environment in which it is created.

In Section 7.4.4 , you learned that a function creates a new execution environment every time it is run. This environment is usually ephemeral, but here it becomes the enclosing environment of the manufactured function.

In this chapter, you’ll learn how the non-obvious combination of these three features leads to the function factory. You’ll also see examples of their usage in visualisation and statistics.

Of the three main functional programming tools (functionals, function factories, and function operators), function factories are the least used. Generally, they don’t tend to reduce overall code complexity but instead partition complexity into more easily digested chunks. Function factories are also an important building block for the very useful function operators, which you’ll learn about in Chapter 11 .

Section 10.2 begins the chapter with an explanation of how function factories work, pulling together ideas from scoping and environments. You’ll also see how function factories can be used to implement a memory for functions, allowing data to persist across calls.

Section 10.3 illustrates the use of function factories with examples from ggplot2. You’ll see two examples of how ggplot2 works with user supplied function factories, and one example of where ggplot2 uses a function factory internally.

Section 10.4 uses function factories to tackle three challenges from statistics: understanding the Box-Cox transform, solving maximum likelihood problems, and drawing bootstrap resamples.

Section 10.5 shows how you can combine function factories and functionals to rapidly generate a family of functions from data.

Prerequisites

Make sure you’re familiar with the contents of Sections 6.2.3 (first-class functions), 7.4.2 (the function environment), and 7.4.4 (execution environments) mentioned above.

Function factories only need base R. We’ll use a little rlang to peek inside of them more easily, and we’ll use ggplot2 and scales to explore the use of function factories in visualisation.

10.2 Factory fundamentals

The key idea that makes function factories work can be expressed very concisely:

The enclosing environment of the manufactured function is an execution environment of the function factory.

It only takes few words to express these big ideas, but it takes a lot more work to really understand what this means. This section will help you put the pieces together with interactive exploration and some diagrams.

10.2.1 Environments

Let’s start by taking a look at square() and cube() :

It’s obvious where x comes from, but how does R find the value associated with exp ? Simply printing the manufactured functions is not revealing because the bodies are identical; the contents of the enclosing environment are the important factors. We can get a little more insight by using rlang::env_print() . That shows us that we have two different environments (each of which was originally an execution environment of power1() ). The environments have the same parent, which is the enclosing environment of power1() , the global environment.

env_print() shows us that both environments have a binding to exp , but we want to see its value 56 . We can do that by first getting the environment of the function, and then extracting the values:

This is what makes manufactured functions behave differently from one another: names in the enclosing environment are bound to different values.

10.2.2 Diagram conventions

We can also show these relationships in a diagram:

r super assignment operator

There’s a lot going on this diagram and some of the details aren’t that important. We can simplify considerably by using two conventions:

Any free floating symbol lives in the global environment.

Any environment without an explicit parent inherits from the global environment.

r super assignment operator

This view, which focuses on the environments, doesn’t show any direct link between cube() and square() . That’s because the link is the through the body of the function, which is identical for both, but is not shown in this diagram.

To finish up, let’s look at the execution environment of square(10) . When square() executes x ^ exp it finds x in the execution environment and exp in its enclosing environment.

r super assignment operator

10.2.3 Forcing evaluation

There’s a subtle bug in power1() caused by lazy evaluation. To see the problem we need to introduce some indirection:

What should square(2) return? You would hope it returns 4:

Unfortunately it doesn’t because x is only evaluated lazily when square() is run, not when power1() is run. In general, this problem will arise whenever a binding changes in between calling the factory function and calling the manufactured function. This is likely to only happen rarely, but when it does, it will lead to a real head-scratcher of a bug.

We can fix this problem by forcing evaluation with force() :

Whenever you create a function factory, make sure every argument is evaluated, using force() as necessary if the argument is only used by the manufactured function.

10.2.4 Stateful functions

Function factories also allow you to maintain state across function invocations, which is generally hard to do because of the fresh start principle described in Section 6.4.3 .

There are two things that make this possible:

The enclosing environment of the manufactured function is unique and constant.

R has a special assignment operator, <<- , which modifies bindings in the enclosing environment.

The usual assignment operator, <- , always creates a binding in the current environment. The super assignment operator , <<- rebinds an existing name found in a parent environment.

The following example shows how we can combine these ideas to create a function that records how many times it has been called:

r super assignment operator

When the manufactured function is run i <<- i + 1 will modify i in its enclosing environment. Because manufactured functions have independent enclosing environments, they have independent counts:

r super assignment operator

Stateful functions are best used in moderation. As soon as your function starts managing the state of multiple variables, it’s better to switch to R6, the topic of Chapter 14 .

10.2.5 Garbage collection

With most functions, you can rely on the garbage collector to clean up any large temporary objects created inside a function. However, manufactured functions hold on to the execution environment, so you’ll need to explicitly unbind any large temporary objects with rm() . Compare the sizes of g1() and g2() in the example below:

10.2.6 Exercises

The definition of force() is simple:

Why is it better to force(x) instead of just x ?

Base R contains two function factories, approxfun() and ecdf() . Read their documentation and experiment to figure out what the functions do and what they return.

Create a function pick() that takes an index, i , as an argument and returns a function with an argument x that subsets x with i .

Create a function that creates functions that compute the i th central moment of a numeric vector. You can test it by running the following code:

What happens if you don’t use a closure? Make predictions, then verify with the code below.

What happens if you use <- instead of <<- ? Make predictions, then verify with the code below.

10.3 Graphical factories

We’ll begin our exploration of useful function factories with a few examples from ggplot2.

10.3.1 Labelling

One of the goals of the scales package is to make it easy to customise the labels on ggplot2. It provides many functions to control the fine details of axes and legends. The formatter functions 57 are a useful class of functions which make it easier to control the appearance of axis breaks. The design of these functions might initially seem a little odd: they all return a function, which you have to call in order to format a number.

In other words, the primary interface is a function factory. At first glance, this seems to add extra complexity for little gain. But it enables a nice interaction with ggplot2’s scales, because they accept functions in the label argument:

r super assignment operator

10.3.2 Histogram bins

A little known feature of geom_histogram() is that the binwidth argument can be a function. This is particularly useful because the function is executed once for each group, which means you can have different binwidths in different facets, which is otherwise not possible.

To illustrate this idea, and see where variable binwidth might be useful, I’m going to construct an example where a fixed binwidth isn’t great.

r super assignment operator

Here each facet has the same number of observations, but the variability is very different. It would be nice if we could request that the binwidths vary so we get approximately the same number of observations in each bin. One way to do that is with a function factory that inputs the desired number of bins ( n ), and outputs a function that takes a numeric vector and returns a binwidth:

r super assignment operator

We could use this same pattern to wrap around the base R functions that automatically find the so-called optimal 58 binwidth, nclass.Sturges() , nclass.scott() , and nclass.FD() :

r super assignment operator

10.3.3 ggsave()

Finally, I want to show a function factory used internally by ggplot2. ggplot2:::plot_dev() is used by ggsave() to go from a file extension (e.g.  png , jpeg etc) to a graphics device function (e.g.  png() , jpeg() ). The challenge here arises because the base graphics devices have some minor inconsistencies which we need to paper over:

Most have filename as first argument but some have file .

The width and height of raster graphic devices use pixels units by default, but the vector graphics use inches.

A mildly simplified version of plot_dev() is shown below:

10.3.4 Exercises

  • Compare and contrast ggplot2::label_bquote() with scales::number_format()

10.4 Statistical factories

More motivating examples for function factories come from statistics:

  • The Box-Cox transformation.
  • Bootstrap resampling.
  • Maximum likelihood estimation.

All of these examples can be tackled without function factories, but I think function factories are a good fit for these problems and provide elegant solutions. These examples expect some statistical background, so feel free to skip if they don’t make much sense to you.

10.4.1 Box-Cox transformation

The Box-Cox transformation (a type of power transformation ) is a flexible transformation often used to transform data towards normality. It has a single parameter, \(\lambda\) , which controls the strength of the transformation. We could express the transformation as a simple two argument function:

But re-formulating as a function factory makes it easy to explore its behaviour with stat_function() :

r super assignment operator

In general, this allows you to use a Box-Cox transformation with any function that accepts a unary transformation function: you don’t have to worry about that function providing ... to pass along additional arguments. I also think that the partitioning of lambda and x into two different function arguments is natural since lambda plays quite a different role than x .

10.4.2 Bootstrap generators

Function factories are a useful approach for bootstrapping. Instead of thinking about a single bootstrap (you always need more than one!), you can think about a bootstrap generator , a function that yields a fresh bootstrap every time it is called:

The advantage of a function factory is more clear with a parametric bootstrap where we have to first fit a model. We can do this setup step once, when the factory is called, rather than once every time we generate the bootstrap:

I use rm(mod) because linear model objects are quite large (they include complete copies of the model matrix and input data) and I want to keep the manufactured function as small as possible.

10.4.3 Maximum likelihood estimation

The goal of maximum likelihood estimation (MLE) is to find the parameter values for a distribution that make the observed data most likely. To do MLE, you start with a probability function. For example, take the Poisson distribution. If we know \(\lambda\) , we can compute the probability of getting a vector \(\mathbf{x}\) of values ( \(x_1\) , \(x_2\) , …, \(x_n\) ) by multiplying the Poisson probability function as follows:

\[ P(\lambda, \mathbf{x}) = \prod_{i=1}^{n} \frac{\lambda ^ {x_i} e^{-\lambda}}{x_i!} \]

In statistics, we almost always work with the log of this function. The log is a monotonic transformation which preserves important properties (i.e. the extrema occur in the same place), but has specific advantages:

The log turns a product into a sum, which is easier to work with.

Multiplying small numbers yields even smaller numbers, which makes the floating point approximation used by a computer less accurate.

Let’s apply a log transformation to this probability function and simplify it as much as possible:

\[ \log(P(\lambda, \mathbf{x})) = \sum_{i=1}^{n} \log(\frac{\lambda ^ {x_i} e^{-\lambda}}{x_i!}) \]

\[ \log(P(\lambda, \mathbf{x})) = \sum_{i=1}^{n} \left( x_i \log(\lambda) - \lambda - \log(x_i!) \right) \]

\[ \log(P(\lambda, \mathbf{x})) = \sum_{i=1}^{n} x_i \log(\lambda) - \sum_{i=1}^{n} \lambda - \sum_{i=1}^{n} \log(x_i!) \]

\[ \log(P(\lambda, \mathbf{x})) = \log(\lambda) \sum_{i=1}^{n} x_i - n \lambda - \sum_{i=1}^{n} \log(x_i!) \]

We can now turn this function into an R function. The R function is quite elegant because R is vectorised and, because it’s a statistical programming language, R comes with built-in functions like the log-factorial ( lfactorial() ).

Consider this vector of observations:

We can use lprob_poisson() to compute the (logged) probability of x1 for different values of lambda .

So far we’ve been thinking of lambda as fixed and known and the function told us the probability of getting different values of x . But in real-life, we observe x and it is lambda that is unknown. The likelihood is the probability function seen through this lens: we want to find the lambda that makes the observed x the most likely. That is, given x , what value of lambda gives us the highest value of lprob_poisson ()?

In statistics, we highlight this change in perspective by writing \(f_{\mathbf{x}}(\lambda)\) instead of \(f(\lambda, \mathbf{x})\) . In R, we can use a function factory. We provide x and generate a function with a single parameter, lambda :

(We don’t need force() because length() implicitly forces evaluation of x .)

One nice thing about this approach is that we can do some precomputation: any term that only involves x can be computed once in the factory. This is useful because we’re going to need to call this function many times to find the best lambda .

Now we can use this function to find the value of lambda that maximizes the (log) likelihood:

Rather than trial and error, we can automate the process of finding the best value with optimise() . It will evaluate ll1() many times, using mathematical tricks to narrow in on the largest value as quickly as possible. The results tell us that the highest value is -30.27 which occurs when lambda = 32.1 :

Now, we could have solved this problem without using a function factory because optimise() passes ... on to the function being optimised. That means we could use the log-probability function directly:

The advantage of using a function factory here is fairly small, but there are two niceties:

We can precompute some values in the factory, saving computation time in each iteration.

The two-level design better reflects the mathematical structure of the underlying problem.

These advantages get bigger in more complex MLE problems, where you have multiple parameters and multiple data vectors.

10.4.4 Exercises

In boot_model() , why don’t I need to force the evaluation of df or model ?

Why might you formulate the Box-Cox transformation like this?

Why don’t you need to worry that boot_permute() stores a copy of the data inside the function that it generates?

How much time does ll_poisson2() save compared to ll_poisson1() ? Use bench::mark() to see how much faster the optimisation occurs. How does changing the length of x change the results?

10.5 Function factories + functionals

To finish off the chapter, I’ll show how you might combine functionals and function factories to turn data into many functions. The following code creates many specially named power functions by iterating over a list of arguments:

This idea extends in a straightforward way if your function factory takes two (replace map() with map2() ) or more (replace with pmap() ) arguments.

One downside of the current construction is that you have to prefix every function call with funs$ . There are three ways to eliminate this additional syntax:

For a very temporary effect, you can use with() :

I recommend this because it makes it very clear when code is being executed in a special context and what that context is.

For a longer effect, you can attach() the functions to the search path, then detach() when you’re done:

You’ve probably been told to avoid using attach() , and that’s generally good advice. However, the situation is a little different to the usual because we’re attaching a list of functions, not a data frame. It’s less likely that you’ll modify a function than a column in a data frame, so the some of the worst problems with attach() don’t apply.

Finally, you could copy the functions to the global environment with env_bind() (you’ll learn about !!! in Section 19.6 ). This is mostly permanent:

You can later unbind those same names, but there’s no guarantee that they haven’t been rebound in the meantime, and you might be deleting an object that someone else created.

You’ll learn an alternative approach to the same problem in Section 19.7.4 . Instead of using a function factory, you could construct the function with quasiquotation. This requires additional knowledge, but generates functions with readable bodies, and avoids accidentally capturing large objects in the enclosing scope. We use that idea in Section 21.2.4 when we work on tools for generating HTML from R.

10.5.1 Exercises

Which of the following commands is equivalent to with(x, f(z)) ?

  • It depends.

Compare and contrast the effects of env_bind() vs.  attach() for the following code.

Assignment Operators in R

R provides two operators for assignment: <- and = .

Understanding their proper use is crucial for writing clear and readable R code.

Using the <- Operator

For assignments.

The <- operator is the preferred choice for assigning values to variables in R.

It clearly distinguishes assignment from argument specification in function calls.

Readability and Tradition

  • This usage aligns with R’s tradition and enhances code readability.

Using the = Operator

The = operator is commonly used to explicitly specify named arguments in function calls.

It helps in distinguishing argument assignment from variable assignment.

Assignment Capability

  • While = can also be used for assignment, this practice is less common and not recommended for clarity.

Mixing Up Operators

Potential confusion.

Using = for general assignments can lead to confusion, especially when reading or debugging code.

Mixing operators inconsistently can obscure the distinction between assignment and function argument specification.

  • In the example above, x = 10 might be mistaken for a function argument rather than an assignment.

Best Practices Recap

Consistency and clarity.

Use <- for variable assignments to maintain consistency and clarity.

Reserve = for specifying named arguments in function calls.

Avoiding Common Mistakes

Be mindful of the context in which you use each operator to prevent misunderstandings.

Consistently using the operators as recommended helps make your code more readable and maintainable.

Quiz: Assignment Operator Best Practices

Which of the following examples demonstrates the recommended use of assignment operators in R?

  • my_var = 5; mean(x = my_var)
  • my_var <- 5; mean(x <- my_var)
  • my_var <- 5; mean(x = my_var)
  • my_var = 5; mean(x <- my_var)
  • The correct answer is 3 . my_var <- 5; mean(x = my_var) correctly uses <- for variable assignment and = for specifying a named argument in a function call.

Multiple assignment operators

Description.

Assign values to name(s).

%<-% and %->% invisibly return value .

These operators are used primarily for their assignment side-effect. %<-% and %->% assign into the environment in which they are evaluated.

Name Structure

At its simplest, the name structure may be a single variable name, in which case %<-% and %->% perform regular assignment, x %<-% list(1, 2, 3) or list(1, 2, 3) %->% x .

To specify multiple variable names use a call to c() , for example c(x, y, z) %<-% c(1, 2, 3) .

When value is neither an atomic vector nor a list, %<-% and %->% will try to destructure value into a list before assigning variables, see destructure() .

object parts

Like assigning a variable, one may also assign part of an object, c(x, x[[1]]) %<-% list(list(), 1) .

nested names

One can also nest calls to c() when needed, c(x, c(y, z)) . This nested structure is used to unpack nested values, c(x, c(y, z)) %<-% list(1, list(2, 3)) .

collector variables

To gather extra values from the beginning, middle, or end of value use a collector variable. Collector variables are indicated with a ... prefix, c(...start, z) %<-% list(1, 2, 3, 4) .

skipping values

Use . in place of a variable name to skip a value without raising an error or assigning the value, c(x, ., z) %<-% list(1, 2, 3) .

Use ... to skip multiple values without raising an error or assigning the values, c(w, ..., z) %<-% list(1, NA, NA, 4) .

default values

Use = to specify a default value for a variable, c(x, y = NULL) %<-% tail(1, 2) .

When assigning part of an object a default value may not be specified because of the syntax enforced by R . The following would raise an "unexpected '=' ..." error, c(x, x[[1]] = 1) %<-% list(list()) .

For more on unpacking custom objects please refer to destructure() .

Introduction to R

Assignment operators.

You can assign values or functions to R objects using <- operator.

assignOps: Assignment Operators

Assignment operators, description.

Assign a value to a name.

There are three different assignment operators: two of them have leftwards and rightwards forms.

The operators <- and = assign into the environment in which they are evaluated. The operator <- can be used anywhere, whereas the operator = is only allowed at the top level (e.g., in the complete expression typed at the command prompt) or as one of the subexpressions in a braced list of expressions.

The operators <<- and ->> are normally only used in functions, and cause a search to be made through parent environments for an existing definition of the variable being assigned. If such a variable is found (and its binding is not locked) then its value is redefined, otherwise assignment takes place in the global environment. Note that their semantics differ from that in the S language, but are useful in conjunction with the scoping rules of R . See ‘The R Language Definition’ manual for further details and examples.

In all the assignment operator expressions, x can be a name or an expression defining a part of an object to be replaced (e.g., z[[1]] ). A syntactic name does not need to be quoted, though it can be (preferably by backticks).

The leftwards forms of assignment <- = <<- group right to left, the other from left to right.

value . Thus one can use a <- b <- c <- 6 .

Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988) The New S Language . Wadsworth & Brooks/Cole.

Chambers, J. M. (1998) Programming with Data. A Guide to the S Language . Springer (for = ).

assign (and its inverse get ), for “subassignment” such as x[i] <- v , see [<- ; further, environment .

R Package Documentation

Browse r packages, we want your feedback.

r super assignment operator

Add the following code to your website.

REMOVE THIS Copy to clipboard

For more information on customizing the embed code, read Embedding Snippets .

Assignment Operators

Description.

Assign a value to a name.

There are three different assignment operators: two of them have leftwards and rightwards forms.

The operators <- and = assign into the environment in which they are evaluated. The operator <- can be used anywhere, whereas the operator = is only allowed at the top level (e.g., in the complete expression typed at the command prompt) or as one of the subexpressions in a braced list of expressions.

The operators <<- and ->> cause a search to made through the environment for an existing definition of the variable being assigned. If such a variable is found then its value is redefined, otherwise assignment takes place globally. Note that their semantics differ from that in the S language, but are useful in conjunction with the scoping rules of R . See ‘The R Language Definition’ manual for further details and examples.

In all the assignment operator expressions, x can be a name or an expression defining a part of an object to be replaced (e.g., z[[1]] ). The name does not need to be quoted, though it can be.

The leftwards forms of assignment <- = <<- group right to left, the other from left to right.

value . Thus one can use a <- b <- c <- 6 .

Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988) The New S Language . Wadsworth & Brooks/Cole.

Chamber, J. M. (1998) Programming with Data. A Guide to the S Language . Springer (for = ).

assign , environment .

Assignment Operators

Description.

Assign a value to a name.

There are three different assignment operators: two of them have leftwards and rightwards forms.

The operators <- and = assign into the environment in which they are evaluated. The operator <- can be used anywhere, whereas the operator = is only allowed at the top level (e.g., in the complete expression typed at the command prompt) or as one of the subexpressions in a braced list of expressions.

The operators <<- and ->> are normally only used in functions, and cause a search to be made through parent environments for an existing definition of the variable being assigned. If such a variable is found (and its binding is not locked) then its value is redefined, otherwise assignment takes place in the global environment. Note that their semantics differ from that in the S language, but are useful in conjunction with the scoping rules of R . See ‘The R Language Definition’ manual for further details and examples.

In all the assignment operator expressions, x can be a name or an expression defining a part of an object to be replaced (e.g., z[[1]] ). A syntactic name does not need to be quoted, though it can be (preferably by backtick s).

The leftwards forms of assignment <- = <<- group right to left, the other from left to right.

value . Thus one can use a <- b <- c <- 6 .

Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988) The New S Language . Wadsworth & Brooks/Cole.

Chambers, J. M. (1998) Programming with Data. A Guide to the S Language . Springer (for = ).

assign (and its inverse get ), for “subassignment” such as x[i] <- v , see [<- ; further, environment .

R-bloggers

R news and tutorials contributed by hundreds of R bloggers

6 life-altering rstudio keyboard shortcuts.

Posted on January 4, 2021 by Business Science in R bloggers | 0 Comments

[social4i size="small" align="align-left"] --> [This article was first published on business-science.io , and kindly contributed to R-bloggers ]. (You can report issue about the content on this page here ) Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

This article is part of a R-Tips Weekly, a weekly video tutorial that shows you step-by-step how to do common R coding tasks.

The RStudio IDE is amazing. You can enhance your R productivity even more with these simple keyboard shortcuts.

Here are the links to get set up. ?

  • Get the Code
  • YouTube Tutorial

6 Keyboard Shortcuts (that will change your life)

Let’s speed up common activities with these 6 super-useful keyboard shortcuts.

r super assignment operator

1: Commenting & Uncommenting Code [Ctrl + Shift + C]

I use this all the time to turn text into commented text. Works with multiple lines too.

Go from this…

r super assignment operator

To this…

r super assignment operator

2: Add the Pipe %>% [Ctrl + Shift + M]

My students absolutely love this. You can easily add the Pipe %>% in any spot you’d like! Perfect for data wrangling with dplyr.

r super assignment operator

3: Insert The Assignment Operator [Alt + -]

My code has tons of assignment operators. This is a simple, time-saver that will make you more productive in building functions and assigning variables values.

r super assignment operator

4: Cursor-Select Multiple Lines [Ctrl + Alt + Up/Down/Click]

This is a recent addition to my portfolio of must-know keyboard shortcuts. Using Multi-Cursor Select has now become a go-to for editing R code .

Multi-Line Select

r super assignment operator

…And edit!

r super assignment operator

5: Find in Files [Ctrl + Shift + F]

THIS IS A SUPER POWER. Seriously. Learn to use this one right now!

Find in Files

r super assignment operator

Found every instance of ggplot by file!

r super assignment operator

6: Keyboard Shortcut Cheat Sheet [Alt + Shift + K]

More shortcuts!!! Run this to get a Keyboard Shortcut Cheat Sheet.

r super assignment operator

Your coworkers will be jealous of your productivity. ?

But if you really want to improve your productivity…

Here’s how to master R. ?

What happens after you learn R for Business.

The look on your boss’s face after you’ve launched your first Shiny App . ?

This is career acceleration.

SETUP R-TIPS WEEKLY PROJECT

Sign Up to Get the R-Tips Weekly (You’ll get email notifications of NEW R-Tips as they are released): https://mailchi.mp/business-science/r-tips-newsletter

Set Up the GitHub Repo: https://github.com/business-science/free_r_tips

Check out the setup video (https://youtu.be/F7aYV0RPyD0). Or, Hit Pull in the Git Menu to get the R-Tips Code

Once you take these actions, you’ll be set up to receive R-Tips with Code every week. =)

  • Interactive Principal Component Analysis in R
  • How to Forecast with ARIMA Models in R
  • Automate Excel in R
  • Detect Relationships with Linear Regression

To leave a comment for the author, please follow the link and comment on their blog: business-science.io . R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job . Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Copyright © 2022 | MH Corporate basic by MH Themes

Never miss an update! Subscribe to R-bloggers to receive e-mails with the latest R posts. (You will not see this message again.)

r super assignment operator

UC Business Analytics R Programming Guide

Assignment & evaluation.

The first operator you’ll run into is the assignment operator. The assignment operator is used to assign a value. For instance we can assign the value 3 to the variable x using the <- assignment operator. We can then evaluate the variable by simply typing x at the command line which will return the value of x . Note that prior to the value returned you’ll see ## [1] in the command line. This simply implies that the output returned is the first output. Note that you can type any comments in your code by preceding the comment with the hashtag ( # ) symbol. Any values, symbols, and texts following # will not be evaluated.

Interestingly, R actually allows for five assignment operators:

The original assignment operator in R was <- and has continued to be the preferred among R users. The = assignment operator was added in 2001 primarily because it is the accepted assignment operator in many other languages and beginners to R coming from other languages were so prone to use it. However, R uses = to associate function arguments with values (i.e. f(x = 3) explicitly means to call function f and set the argument x to 3. Consequently, most R programmers prefer to keep = reserved for argument association and use <- for assignment.

The operators <<- is normally only used in functions which we will not get into the details. And the rightward assignment operators perform the same as their leftward counterparts, they just assign the value in an opposite direction.

Overwhelmed yet? Don’t be. This is just meant to show you that there are options and you will likely come across them sooner or later. My suggestion is to stick with the tried and true <- operator. This is the most conventional assignment operator used and is what you will find in all the base R source code…which means it should be good enough for you.

Lastly, note that R is a case sensitive programming language. Meaning all variables, functions, and objects must be called by their exact spelling:

Question 31: Super assignment operator

Mohit sharma: linkedin , twitter github site , repository, 23 november 2019.

Super assignment operator <<- can be used for assignment in parent environment. Which of the following is TRUE for the following code?

  • C > B > A
  • C < B < A

<<- is known as super assignment operator. In contrast to the regular assignment operator <- which does the assignment in current environment, <<- does the assignment in parent environment. If in case it doesn’t find the variable, it will create the variable in global environment. See for example below:

I have defined a function f with a variable y in it to which I have assigned 1. If I try to access y outside function, I get the error, as y doesn’t exist in global environment. It only exists in f ’s environment.

But if I rewrite the above code using <<- , I don’t get any error. y is now defined in global environment.

So for the above question, option \(2.\) \(C>B>A\) is correct.

As C=3 , B=2 , A=1 . At the first execution, f returns 1 and also updates x in global environment to 1 . Similarly for the subsequent executions, x gets updated globally with an increment 1.

In contrast, if I use <- , I would get A=B=C=1 .

References: Advanced R by Hadley Wickham

Thanks for reading. If you like the question, how about some love and coffee: Buy me a coffee

IMAGES

  1. R Operators

    r super assignment operator

  2. R Operators

    r super assignment operator

  3. Assignment Operators in R (3 Examples)

    r super assignment operator

  4. R Operators

    r super assignment operator

  5. R

    r super assignment operator

  6. R Operators

    r super assignment operator

VIDEO

  1. how to use the remainder assignment (%=) operator in JavaScript #coding #javascript #tutorial

  2. Assignment Operator│Python │Part# 11│Learn CSE Malayalam│കമ്പ്യൂട്ടർ സയൻസ്│മലയാളം

  3. Subset in R erzeugen

  4. (??)

  5. #6 Operator in Java part-2 ll Ternary operator ll Conditional ll Relational @Online Tech Hub

  6. 2 Feb 1shift Assistant Radio operator exam review UP Police radio operator exam analysis 🧐 2024

COMMENTS

  1. How do you use "<<-" (scoping assignment) in R?

    The key to managing variables at different levels is the double arrow assignment operator <<-. Unlike the usual single arrow ... Now I get it: purrr::map does its work inside a function and there only function scope applies. So, super-assignment <<-is required to modify variables outside of the purrr::map internal function (the .f argument ...

  2. r

    Working with data.tables, I have a large function do.everything() which includes other functions and returns a data.table. I run it with: DT.processed <- do.everything(datatable = DT.unprocesse...

  3. Assignment Operators in R (3 Examples)

    On this page you'll learn how to apply the different assignment operators in the R programming language. The content of the article is structured as follows: 1) Example 1: Why You Should Use <- Instead of = in R. 2) Example 2: When <- is Really Different Compared to =. 3) Example 3: The Difference Between <- and <<-. 4) Video ...

  4. 7 Environments

    7.1 Introduction. The environment is the data structure that powers scoping. This chapter dives deep into environments, describing their structure in depth, and using them to improve your understanding of the four scoping rules described in Section 6.4 . Understanding environments is not necessary for day-to-day use of R.

  5. [R] What does the "<<-" operator mean?

    It's the 'superassignment' operator. It does the assignment in the. enclosing environment. That is, starting with the enclosing frame, it. variable called ecov_xy, and then assigns to it. If it never finds. an existing ecov_xy it creates one in the global environment. that modify the state by using superassignment. a<-0.

  6. 14 Functional Programming in R

    The super-assignment operator looks for the name heavenly_hash in the parent environment of the run-time environment, If if finds heavenly_hash there then it changes its value to 5 and stops. Otherwise it looks in the next parent up, and so on until it reaches the Global Environment, at which point if it doesn't find a heavenly_hash it ...

  7. 10 Function factories

    The super assignment operator, <<-rebinds an existing name found in a parent environment. The following example shows how we can combine these ideas to create a function that records how many times it has been called: new_counter <-function {i <-0 function {i <<-i + 1 i}} counter_one <-new_counter counter_two <-new_counter

  8. What is `<<-` in R?

    4. It almost means global assignment (see whuber's comment, and the linked docs on scoping). So if you assign A the value of 2 using A <<- 2 within, for example, a function, and then call that function, other functions and the command line can then use the value of A. This has to do with the concept of scoping, and there are particulars of R's ...

  9. Assignment Operators in R

    For Assignments. The <- operator is the preferred choice for assigning values to variables in R. It clearly distinguishes assignment from argument specification in function calls. # Correct usage of <- for assignment x <- 10 # Correct usage of <- for assignment in a list and the = # operator for specifying named arguments my_list <- list (a = 1 ...

  10. R: Multiple assignment operators

    At its simplest, the name structure may be a single variable name, in which case %<-% and %->% perform regular assignment, x. %<-% list(1, 2, 3) or list(1, 2, 3) %->% x . To specify multiple variable names use a call to c(), for example c(x, y, z) %<-% c(1, 2, 3) . When value is neither an atomic vector nor a list, %<-% and %->% will try to ...

  11. Assignment operators

    Assignment operators. You can assign values or functions to R objects using <- operator. x <- 3 # assign 3 to 'x' x.

  12. assignOps: Assignment Operators

    There are three different assignment operators: two of them have leftwards and rightwards forms. The operators <- and = assign into the environment in which they are evaluated. The operator <- can be used anywhere, whereas the operator = is only allowed at the top level (e.g., in the complete expression typed at the command prompt) or as one of ...

  13. Assignment Operators in R

    R has five common assignment operators: <-. ->. <<-. ->>. =. Many style guides and traditionalists prefer the left arrow operator, <-. Why use that when it's an extra keystroke? <- always means assignment. The equal sign is overloaded a bit taking on the roles of an assignment operator, function argument binding, or depending on the context ...

  14. R: Assignment Operators

    Details. There are three different assignment operators: two of them have leftwards and rightwards forms. The operators <- and = assign into the environment in which they are evaluated. The operator <- can be used anywhere, whereas the operator = is only allowed at the top level (e.g., in the complete expression typed at the command prompt) or ...

  15. R: Assignment Operators

    Details. There are three different assignment operators: two of them have leftwards and rightwards forms. The operators <- and = assign into the environment in which they are evaluated. The operator <- can be used anywhere, whereas the operator = is only allowed at the top level (e.g., in the complete expression typed at the command prompt) or ...

  16. 6 Life-Altering RStudio Keyboard Shortcuts

    Let's speed up common activities with these 6 super-useful keyboard shortcuts. (Click image to play tutorial) 1: Commenting & Uncommenting Code ... My code has tons of assignment operators. This is a simple, time-saver that will make you more productive in building functions and assigning variables values. 4: Cursor-Select Multiple Lines ...

  17. Assignment & Evaluation · UC Business Analytics R Programming Guide

    The original assignment operator in R was <-and has continued to be the preferred among R users. The = assignment operator was added in 2001 primarily because it is the accepted assignment operator in many other languages and beginners to R coming from other languages were so prone to use it. However, R uses = to associate function arguments with values (i.e. f(x = 3) explicitly means to call ...

  18. Assignment operators in R: '<-' and '<<-'

    7. <- assigns an object to the environment in which it is evaluated (local scope). <<- assigns an object to the next highest environment that the name is found in or the global namespace if no name is found. See the documentation here. <<- is usually only used in functions, but be careful. <<- can be much harder to debug because it is harder to ...

  19. Question 31: Super assignment operator

    Answer. <<- is known as super assignment operator. In contrast to the regular assignment operator <- which does the assignment in current environment, <<- does the assignment in parent environment. If in case it doesn't find the variable, it will create the variable in global environment. See for example below:

  20. What is the R assignment operator := for?

    The development version of R now allows some assignments to be written C- or Java-style, using the = operator. This increases compatibility with S-Plus (as well as with C, Java, and many other languages). All the previously allowed assignment operators (<-, :=, _, and <<-) remain fully in effect. It seems the := function is no longer present ...