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
32: Functional Programming Concepts in R
Imagine you're running a high-end commercial kitchen. You have a set of standard recipes—these are your functions. But as the head chef, you don't just follow recipes; you create systems. You might have a "Prep Station" whose only job is to take any recipe and apply it to every single ingredient in a crate. The Prep Station doesn't care if the recipe is "dice," "peel," or "sear"; it just knows how to repeat the process across a collection of items. Then, you might have a "Customizer" who takes a base recipe (like a vinaigrette) and, based on a customer's request for "extra spicy," hands back a modified version of that recipe for the line cooks to use.
In R, this is the heart of functional programming. We stop thinking of functions as just "tasks to perform" and start treating them as first-class citizens. This means you can pass a function into another function as an argument, or even have a function return another function as its result. It sounds meta, and it is, but it's how you move from writing repetitive scripts to building truly flexible software.
The Power of the Prep Station (Higher-Order Functions)
You've probably used lapply or sapply before, but I want you to look at them through this lens. They are Higher-Order Functions (HOFs) because they take another function as an input. I personally find that once you embrace this, you'll almost never write a for loop for data transformation again.
Let's look at a real scenario. Suppose you have a list of numeric vectors representing stock prices over a week, and you want to apply different types of summaries to them.
# A list of weekly prices for three stocks
stocks <- list(
AAPL = c(150, 152, 149, 155, 153),
GOOG = c(2800, 2810, 2790, 2820, 2815),
MSFT = c(300, 305, 302, 308, 307)
)
# Instead of writing a loop, we pass the 'mean' function directly into lapply
avg_prices <- lapply(stocks, mean)
print(avg_prices)
In this snippet, mean isn't being called (notice there are no parentheses like mean()); it's being passed as an object. The lapply function acts as our Prep Station, taking the "mean recipe" and applying it to every element in the stocks list.
Building a Function Factory (Closures)
This is where things get interesting. A "closure" happens when a function returns another function, and that inner function remembers the environment in which it was created. I like to call these "Function Factories."
Say you need to scale your data by different factors depending on the currency exchange rate. Instead of writing five different scaling functions, you write one factory that creates them for you.
# The Factory: it takes a multiplier and returns a NEW function
make_scaler <- function(multiplier) {
function(x) {
return(x * multiplier)
}
}
# Create specific tools using the factory
usd_to_eur <- make_scaler(0.92)
usd_to_gbp <- make_scaler(0.79)
# Now use the tools
price_usd <- 100
print(usd_to_eur(price_usd)) # 92
print(usd_to_gbp(price_usd)) # 79
Notice that usd_to_eur is now a function itself. It "remembered" that the multiplier was 0.92 even after make_scaler finished executing. This is an incredibly powerful way to encapsulate logic and avoid cluttering your global environment with dozens of nearly identical functions.
Staying Pure to Avoid Headaches
To really master this style, you have to strive for "Pure Functions." A function is pure if it always produces the same output for the same input and has no side effects. A side effect is anything that changes the state of the world outside the function—like modifying a global variable or printing to the console.
I've seen too many R scripts crash because a function deep in the code modified a variable in the global environment (the .GlobalEnv), and the programmer spent three hours wondering why their data changed. If you keep your functions pure, your code becomes predictable, easier to test, and much simpler to debug.
📋 Practical Task
Build a Dynamic Data Normalizer
Your task is to create a flexible system for normalizing numeric data. Instead of writing a single function with a long list of if/else statements, you will use the functional concepts learned in this lesson.
Requirements:
- Create a "factory" function called
make_normalizer. This factory should take a string argumentmethod(either"minmax"or"zscore"). - Based on the
method, the factory should return a new function.- The
"minmax"function should scale a vector to a range between 0 and 1:(x - min(x)) / (max(x) - min(x)). - The
"zscore"function should scale a vector by subtracting the mean and dividing by the standard deviation:(x - mean(x)) / sd(x).
- The
- Create a list of three different numeric vectors (e.g.,
list(a = c(1, 2, 3), b = c(10, 20, 30), c = c(5, 15, 25))). - Use a higher-order function (like
lapply) to apply a"zscore"normalizer (created by your factory) to every vector in that list.
Expected Output: A list where every numeric vector has been transformed into its Z-score equivalent.
There are no comments for now.