I want to use dplyr's mutate()
to create multiple new columns in a data frame. The column names and their contents should be dynamically generated.
Example data from iris:
require(dplyr)
data(iris)
iris <- tbl_df(iris)
I've created a function to mutate my new columns from the Petal.Width
variable:
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
df <- mutate(df, varname = Petal.Width * n) ## problem arises here
df
}
Now I create a loop to build my columns:
for(i in 2:5) {
iris <- multipetal(df=iris, n=i)
}
However, since mutate thinks varname is a literal variable name, the loop only creates one new variable (called varname) instead of four (called petal.2 - petal.5).
How can I get mutate()
to use my dynamic name as variable name?
You may enjoy package
friendlyeval
which presents a simplified tidy eval API and documentation for newer/casualdplyr
users.You are creating strings that you wish
mutate
to treat as column names. So usingfriendlyeval
you could write:Which under the hood calls
rlang
functions that checkvarname
is legal as column name.friendlyeval
code can be converted to equivalent plain tidy eval code at any time with an RStudio addin.In the new release of
dplyr
(0.6.0
awaiting in April 2017), we can also do an assignment (:=
) and pass variables as column names by unquoting (!!
) to not evaluate itChecking the output based on @MrFlick's
multipetal
applied on 'iris1'Since you are dramatically building a variable name as a character value, it makes more sense to do assignment using standard data.frame indexing which allows for character values for column names. For example:
The
mutate
function makes it very easy to name new columns via named parameters. But that assumes you know the name when you type the command. If you want to dynamically specify the column name, then you need to also build the named argument.The latest version of dplyr (0.7) does this using by using
:=
to dynamically assign parameter names. You can write your function as:For more information, see the documentation available form
vignette("programming", "dplyr")
.Slightly earlier version of dplyr (>=0.3 <0.7), encouraged the use of "standard evaluation" alternatives to many of the functions. See the Non-standard evaluation vignette for more information (
vignette("nse")
).So here, the answer is to use
mutate_()
rather thanmutate()
and do:Older versions of dplyr
Note this is also possible in older versions of dplyr that existed when the question was originally posed. It requires careful use of
quote
andsetName
:I am also adding an answer that augments this a little bit because I came to this entry when searching for an answer, and this had almost what I needed, but I needed a bit more, which I got via @MrFlik 's answer and the R lazyeval vignettes.
I wanted to make a function that could take a dataframe and a vector of column names (as strings) that I want to be converted from a string to a Date object. I couldn't figure out how to make
as.Date()
take an argument that is a string and convert it to a column, so I did it as shown below.Below is how I did this via SE mutate (
mutate_()
) and the.dots
argument. Criticisms that make this better are welcome.While I enjoy using dplyr for interactive use, I find it extraordinarily tricky to do this using dplyr because you have to go through hoops to use lazyeval::interp(), setNames, etc. workarounds.
Here is a simpler version using base R, in which it seems more intuitive, to me at least, to put the loop inside the function, and which extends @MrFlicks's solution.
Here's another version, and it's arguably a bit simpler.