R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
128: Quasiquotation with rlang
If you've spent any time writing functions that wrap around dplyr or ggplot2, you've probably hit a wall where R seems to be ignoring your variables. You pass a column name into a function, but instead of filtering by that column, R starts looking for a column actually named "column_name". It's a frustrating moment because you know the value is right there in the argument, but the function is treating it as a literal symbol.
The "Where did my column go?" problem
Let's look at how most of us try to solve this the first time. Imagine you want a helper function that filters a data frame based on a column and a value. You'd probably write something like this:
my_filter <- function(data, col_name, value) {
data %>% filter(col_name == value)
}
# Trying to filter mtcars for cylinders == 6
my_filter(mtcars, cyl, 6)
This will fail. Why? Because filter() uses non-standard evaluation (NSE). It doesn't look for a variable called col_name in your environment; it looks for a column literally named col_name inside the data frame. Since your data frame doesn't have a column called "col_name", it throws an error. I've seen a lot of developers try to "hack" their way out of this using get() or eval(parse(text = ...)). Please, don't do that. Parsing strings into code is slow, hard to debug, and opens you up to some nasty security risks if you're handling user input.
Bridging the gap with Bang-Bang
This is where quasiquotation comes in. The term sounds academic, but the concept is simple: we want to "quote" an expression (tell R "don't evaluate this yet") and then "unquote" a specific part of it (tell R "actually, put this value right here"). In the rlang ecosystem, the tool for unquoting is !!, which we call "bang-bang".
To make this work, we first need to make sure the column name is a symbol, not a string or a raw value. We use ensym() for this because it's flexible—it handles both quoted strings ("cyl") and unquoted symbols (cyl) gracefully. Here is how I'd actually write that filter function:
library(rlang)
library(dplyr)
my_filter <- function(data, col_name, value) {
# Convert the input to a symbol
col_sym <- ensym(col_name)
# Use !! to "splice" the symbol into the expression
data %>% filter(!!col_sym == value)
}
# Now both of these work perfectly
my_filter(mtcars, cyl, 6)
my_filter(mtcars, "cyl", 6)
The trade-off: Complexity vs. Control
You might be wondering why we don't just use .data[[col_name]], which is another common way to handle dynamic columns in tidyverse. For a simple filter, .data[[col_name]] is actually cleaner. But as soon as you start building complex macros or functions that need to manipulate the actual expression tree—like building a dynamic ggplot layer or a complex summarise call—the rlang approach is the only one that gives you full control.
The "cost" here is a steeper learning curve. You're no longer just writing R code; you're writing code that generates R code. It feels like a meta-layer of programming. But once you get comfortable with ensym() and !!, you stop fighting the Tidyverse's NSE and start using it to your advantage. You're essentially telling R: "I know you usually handle these names automatically, but for this one specific spot, I want you to use this exact symbol I've captured."
📋 Practical Task
Build a Dynamic Column Summarizer
Your task is to create a function called summarize_column that takes a data frame and a column name (either as a symbol or a string) and returns the mean and standard deviation of that column. You must use rlang::ensym() and the !! operator to inject the column name into a dplyr::summarise call.
Requirements:
- The function should accept
(data, col_name). - It should work whether the user passes the column as a symbol (e.g.,
mpg) or a string (e.g.,"mpg"). - The resulting data frame should have columns named
avgandstd_dev. - Test your function using the
mtcarsdataset.
There are no comments for now.