-
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
41: Practice Exercise: Building a Custom Summary Statistics Function
When you start writing your own functions in R, it's easy to assume that because mean() or sd() work perfectly on a clean vector, they'll work perfectly inside your custom wrapper. But real-world data is messy. I've seen countless scripts crash or return useless results because of one tiny oversight in how R handles missing values.
# A typical first attempt at a summary function
get_stats <- function(data_vec) {
avg <- mean(data_vec)
med <- median(data_vec)
stdev <- sd(data_vec)
return(c(mean = avg, median = med, sd = stdev))
}
# Let's test it with some actual data
my_data <- c(12, 15, 14, NA, 18, 21)
get_stats(my_data)
# Output:
# mean median sd
# NA NA NA
The NA Propagation Trap
If you ran the code above, you probably noticed it didn't crash, but it gave you a result that's functionally useless: a vector of NAs. In R, NA (Not Available) is contagious. If a single value in your vector is missing, most mathematical functions will return NA by default to warn you that the calculation is incomplete. This is actually a safety feature, but when you're building a summary function, it's a nuisance.
The fix is simple, but essential: you have to explicitly tell the internal functions to ignore the missing values using the na.rm = TRUE argument. I usually prefer to do this inside the function so the person calling the function doesn't have to worry about it.
# The fixed version
get_stats <- function(data_vec) {
avg <- mean(data_vec, na.rm = TRUE)
med <- median(data_vec, na.rm = TRUE)
stdev <- sd(data_vec, na.rm = TRUE)
return(c(mean = avg, median = med, sd = stdev))
}
get_stats(my_data)
# Output:
# mean median sd
# 16.0000 15.0000 3.807887
Making the Function Robust and Flexible
Now, while the version above works, it's a bit "brittle." What happens if someone accidentally passes a character vector into get_stats? R will throw a confusing error. Also, some users might actually want to know if there are NAs present rather than just hiding them.
To make this professional-grade, we should add a type check and a parameter to control na.rm. I like to set na.rm = TRUE as the default, but leave the door open for the user to change it. I'll also wrap the logic in an if statement to ensure we're only dealing with numbers.
get_stats_pro <- function(data_vec, remove_na = TRUE) {
# First, make sure we are actually dealing with numbers
if (!is.numeric(data_vec)) {
stop("The input must be a numeric vector. You passed: ", class(data_vec))
}
# We use the remove_na variable directly inside the functions
avg <- mean(data_vec, na.rm = remove_na)
med <- median(data_vec, na.rm = remove_na)
stdev <- sd(data_vec, na.rm = remove_na)
# I'm returning a named list here instead of a vector.
# Lists are generally more flexible in R for returning mixed data.
list(
mean = avg,
median = med,
standard_deviation = stdev,
n_obs = length(data_vec),
n_missing = sum(is.na(data_vec))
)
}
# Now it's a tool, not just a snippet
get_stats_pro(my_data)
By adding the n_obs and n_missing counts, we've turned a basic calculation into a legitimate summary tool. You're no longer just blindly ignoring NAs; you're reporting exactly how many were ignored. That's the difference between a script that "just works" and a professional piece of software.
📋 Practical Task
Exercise: Building a Robust Environmental Sensor Summary Tool
Imagine you are analyzing data from a remote weather station. The station records temperatures, but occasionally the sensor glitches and records non-numeric strings (like "ERROR") or leaves gaps (NAs).
Your task is to create a function called summarize_sensor_data that does the following:
- Accepts a vector (which might contain numbers,
NAs, or strings). - First, coerces the input to numeric using
as.numeric(). (Note: R will turn non-numeric strings intoNAs during this process). - Calculates the mean, minimum, and maximum values, ignoring
NAs. - Calculates the range (Max minus Min).
- Returns a named list containing all four metrics, plus a count of how many
NAvalues were present after the numeric coercion.
Test your function with this vector:
sensor_readings <- c(22.5, 23.1, "ERROR", 21.8, NA, 24.2, "MALFUNCTION", 22.0)
There are no comments for now.