Curly-Curly, the successor of Bang-Bang

(This article was first published on Econometrics and Free Software, and kindly contributed to R-bloggers)


Writing functions that take data frame columns as arguments is a problem that most R users have been
confronted with at some point. There are different ways to tackle this issue, and this blog post will
focus on the solution provided by the latest release of the {rlang} package. You can read the
announcement here, which explains really
well what was wrong with the old syntax, and how the new syntax works now.

I have written about the problem of writing functions that use data frame columns as arguments
three years ago
and two year ago too.
Last year, I wrote a
blog post that showed how to map a list of functions to a list of datasets with a list of columns
as arguments that used the !!quo(column_name) syntax (the !! is pronounced bang-bang).
Now, there is a new sheriff in town, {{}}, introduced in {rlang} version 0.4.0 that makes
things even easier. The suggested pronunciation of {{}} is curly-curly, but there is no
consensus yet.

First, let’s load the {tidyverse}:

library(tidyverse)

Let’s suppose that I need to write a function that takes a data frame, as well as a column from
this data frame as arguments:

how_many_na <- function(dataframe, column_name){
  dataframe %>%
    filter(is.na(column_name)) %>%
    count()
}

Let’s try this function out on the starwars data:

data(starwars)

head(starwars)
## # A tibble: 6 x 13
##   name  height  mass hair_color skin_color eye_color birth_year gender
##                               
## 1 Luke…    172    77 blond      fair       blue            19   male  
## 2 C-3PO    167    75        gold       yellow         112     
## 3 R2-D2     96    32        white, bl… red             33     
## 4 Dart…    202   136 none       white      yellow          41.9 male  
## 5 Leia…    150    49 brown      light      brown           19   female
## 6 Owen…    178   120 brown, gr… light      blue            52   male  
## # … with 5 more variables: homeworld , species , films ,
## #   vehicles , starships 

As you can see, there are missing values in the hair_color column. Let’s try to count how many
missing values are in this column:

how_many_na(starwars, hair_color)
Error: object 'hair_color' not found

R cannot find the hair_color column, and yet it is in the data! Well, this is actually exactly
the issue. The issue is that the column is inside the dataframe, but when calling the function
with hair_color as the second argument, R is looking for a variable called hair_color that
does not exist. What about trying with "hair_color"?

how_many_na(starwars, "hair_color")
## # A tibble: 1 x 1
##       n
##   
## 1     0

Now we get something, but something wrong!

One way to solve this issue, is to not use the filter() function, and instead rely on base R:

how_many_na_base <- function(dataframe, column_name){
  na_index <- is.na(dataframe[, column_name])
  nrow(dataframe[na_index, column_name])
}

how_many_na_base(starwars, "hair_color")
## [1] 5

This works, but not using the {tidyverse} at all is not an option, at least for me. For instance,
the next function, which uses a grouping variable, would be difficult to implement without the
{tidyverse}:

summarise_groups <- function(dataframe, grouping_var, column_name){
  dataframe %>%
    group_by(grouping_var) %>%  
    summarise(mean(column_name, na.rm = TRUE))
}

Calling this function results in the following error message:

Error: Column `grouping_var` is unknown

Before the release of {rlang} 0.4.0 this is was the solution:

summarise_groups <- function(dataframe, grouping_var, column_name){

  grouping_var <- enquo(grouping_var)
  column_name <- enquo(column_name)
  mean_name <- paste0("mean_", quo_name(column_name))

  dataframe %>%
    group_by(!!grouping_var) %>%  
    summarise(!!(mean_name) := mean(!!column_name, na.rm = TRUE))
}

The core of the function remained very similar to the version from before, but now one has to
use the enquo()!! syntax. While not overly difficult to use, it is cumbersome.

Now this can be simplified using the new {{}} syntax:

summarise_groups <- function(dataframe, grouping_var, column_name){

  dataframe %>%
    group_by({{grouping_var}}) %>%  
    summarise({{column_name}} := mean({{column_name}}, na.rm = TRUE))
}

Much easier and cleaner! You still have to use the := operator instead of = for the column name
however. Also, from my understanding, if you want to modify the column names, for instance in this
case return "mean_height" instead of height you have to keep using the enquo()!! syntax.

Hope you enjoyed! If you found this blog post useful, you might want to follow
me on twitter for blog post updates and
buy me an espresso or paypal.me.

Buy me an EspressoBuy me an Espresso

To leave a comment for the author, please follow the link and comment on their blog: Econometrics and Free Software.

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: Data science, Big Data, R jobs, visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...



If you got this far, why not subscribe for updates from the site? Choose your flavor: e-mail, twitter, RSS, or facebook...

Comments are closed.

Search R-bloggers

Sponsors

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

Click here to close (This popup will not appear again)