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
13: Working with NA and Missing Data
Imagine you're reviewing a stack of handwritten job applications. Most candidates filled out every field, but a few left the "Years of Experience" box completely blank. Now, if you were to calculate the average experience of all applicants, you couldn't just treat those blank boxes as zeros. If someone left it blank, it doesn't mean they have zero experience; it means the information is missing. If you treat a blank as a zero, you're lying to your data and dragging your average down unfairly.
In R, that blank box is NA (Not Available). It's a special logical constant that tells R, "I don't know what goes here." Here is how that maps to the code you'll be writing:
- The Blank Box: This is the
NAvalue. It can exist in numeric, character, or logical vectors. - Checking for Blanks: You can't just ask R "Is this equal to NA?" (I'll explain why in a second). Instead, you use a specific tool:
is.na(). - Dealing with the Gaps: When you run a calculation, you have to decide whether to throw away the "incomplete applications" (
na.rm = TRUE) or let the missing data signal that the result is also unknown.
The Trap of the Equality Operator
Here is the first thing that trips up almost everyone I've mentored. You'll be tempted to find missing values using my_vector == NA. Don't do it. It won't work.
In R, NA is contagious. If you ask "Is this value equal to something unknown?", the answer is... unknown. So R returns NA for every single element instead of a TRUE or FALSE. To actually find the holes in your data, you have to use is.na().
# This is the wrong way
ages <- c(25, 30, NA, 42)
ages == NA
# Result: NA NA NA NA (Useless!)
# This is the right way
is.na(ages)
# Result: FALSE FALSE TRUE FALSE
Stopping the Contagion in Calculations
I've seen plenty of developers pull their hair out because sum() or mean() suddenly started returning NA. This happens because R is being cautious. If one value in your set is unknown, R assumes the total sum must also be unknown.
If you've decided that it's acceptable to just ignore the missing entries and calculate the average of whatever is actually there, you use the na.rm (NA remove) argument. I personally find it's better to be explicit here so the next person reading your code knows you didn't just forget about the missing data.
# Let's say we have daily temperature readings, but the sensor died on Tuesday
temps <- c(72, 75, NA, 68, 71)
mean(temps)
# Result: NA (R is playing it safe)
mean(temps, na.rm = TRUE)
# Result: 71.5 (R ignores the NA and averages the rest)
Cleaning the Whole Table
Sometimes, you don't want to handle NAs column by column. If a row in your data frame is missing critical information, that whole record might be useless to you. In those cases, you can use na.omit(). This effectively tosses any row that contains at least one NA.
Just a word of caution: be careful with this. If your dataset is "messy" and every row has at least one random missing value in some unimportant column, na.omit() will wipe out your entire dataset before you can blink. Always check how many NAs you have before you start deleting rows.
# A small dataset of users
users <- data.frame(
name = c("Alice", "Bob", "Charlie"),
email = c("alice@email.com", NA, "charlie@email.com"),
age = c(25, 30, NA)
)
# This removes Bob (missing email) and Charlie (missing age)
clean_users <- na.omit(users)
# Result: Only Alice remains📋 Practical Task
Fixing the Broken Sensor Log
You have been handed a numeric vector representing the voltage readings from a power grid sensor over 10 minutes. However, the sensor flickered, and some readings were lost (recorded as NA). Your goal is to clean this data and find the average voltage.
Your Task:
- Create a vector called
voltage_readingswith the following values:12.1, 12.0, NA, 12.2, NA, 11.9, 12.1, NA, 12.0, 12.1. - Write a line of code that returns a logical vector identifying exactly which readings are missing.
- Calculate the mean voltage of the readings. Ensure your result is a number, not
NA. - Create a new vector called
filtered_readingsthat contains only the non-missing values (Hint: use the!operator withis.na()inside square brackets[]).
There are no comments for now.