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
15: Subsetting and Indexing
A few years ago, I was reviewing a PR for a colleague who was analyzing a dataset of 50,000 sensor readings. They were trying to isolate all the "error" readings where the voltage spiked above a certain threshold. The code looked correct at a glance, but when they tried to run a summary function on the result, R threw a cryptic error saying the object wasn't a data frame anymore. It turned out they had subsetted the data down to a single column, and R—in its infinite, helpful wisdom—had automatically "simplified" that data frame into a plain vector. They spent half their afternoon chasing a bug that was actually just a quirk of how R handles indexing. It's a rite of passage for R developers, and it's exactly why we need to be precise about how we grab our data.
The Magic of Square Brackets
At its core, subsetting is just telling R, "I don't want the whole thing; I just want these specific pieces." For vectors, we use the square brackets []. If you pass an integer, you get the element at that position. But here is the first thing to remember: R is 1-indexed, not 0-indexed. If you're coming from Python or Java, this will feel wrong for a while. I still occasionally catch myself trying to start at 0.
# Imagine we have a vector of daily temperatures
temps <- c(72, 75, 68, 82, 90, 71)
# Grab the first and third days
temps[c(1, 3)]
# Grab everything EXCEPT the fifth day using a negative index
temps[-5]
Negative indexing is a shorthand I use constantly. Instead of figuring out which indices you do want, it's often easier to just tell R which one to throw away.
Filtering with Logical Vectors
Hard-coding index numbers is rarely useful in the real world because your data changes. You don't want the "5th element"; you want the "elements where the value is greater than 80." This is where logical subsetting comes in. When you put a logical comparison inside the brackets, R evaluates it for every element and only returns the TRUE ones.
# Find all days where it was hotter than 80 degrees
hot_days <- temps[temps > 80]
# Result: 82, 90
This is incredibly powerful because you can combine conditions. Using & (AND) and | (OR), you can carve out very specific slices of your data. Just be careful with your parentheses; R's order of operations can sometimes lead to surprising results if you're mixing logical operators.
Navigating Data Frames and the Single-Column Trap
Data frames are just lists of vectors, which means they are two-dimensional. Consequently, the square brackets now expect two arguments: [row, column]. If you leave one blank, R assumes you want everything in that dimension.
# Let's use a small sales data frame
sales <- data.frame(
item = c("Latte", "Mocha", "Espresso", "Tea"),
price = c(4.50, 5.00, 3.00, 3.50),
qty = c(10, 12, 20, 15)
)
# Get the 2nd row, all columns
sales[2, ]
# Get all rows, but only the 'price' column (column 2)
sales[, 2]
Now, here is that "trap" I mentioned earlier. When you subset a data frame to a single column using sales[, 2], R converts the result into a vector. This is called "dropping." If your subsequent code expects a data frame, it will crash. To prevent this, use drop = FALSE. It forces R to keep the result as a data frame, regardless of how small the subset is. It's a small addition to your code that saves hours of debugging.
# Keep it as a data frame, even though it's only one column
price_column <- sales[, 2, drop = FALSE]
📋 Practical Task
Filtering High-Value Coffee Orders
You have been handed a data frame containing a morning's worth of coffee shop transactions. Your goal is to extract a specific subset of this data for a manager's report.
# The Dataset
orders <- data.frame(
order_id = 1:6,
drink = c("Latte", "Cappuccino", "Flat White", "Latte", "Espresso", "Mocha"),
amount = c(5.50, 4.75, 5.00, 5.50, 3.00, 6.25),
customer_type = c("Member", "Guest", "Member", "Guest", "Member", "Member")
)
Your task: Create a new data frame called premium_members that contains only the rows where the customer_type is "Member" AND the amount is greater than 5.00. Ensure that your resulting object is still a data frame.
There are no comments for now.