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
20: Joining Data Frames
I've seen this happen a dozen times with junior devs: they have two datasets that look like they belong together, so they just use cbind() to glue them side-by-side. They assume that because both tables have 100 rows and are sorted by an ID column, they are effectively the same records. It feels intuitive, but it's a dangerous habit that will eventually lead to "silent" data corruption—where your code runs perfectly, but your results are complete nonsense.
Stop trusting row order for alignment
Let's look at why cbind() is a trap. Imagine you have a list of employees and a list of their monthly bonuses. If one person leaves the company and is removed from the employee list, but stays in the bonus list, everything shifts up by one. Suddenly, every employee is credited with the bonus of the person who was below them in the spreadsheet.
# The dangerous way
employees <- data.frame(id = c(1, 2, 3), name = c("Alice", "Bob", "Charlie"))
bonuses <- data.frame(id = c(1, 3), amount = c(500, 700)) # Bob is missing!
# This looks okay at a glance, but it's wrong
wrong_data <- cbind(employees, bonuses)
print(wrong_data)
# Alice gets 500 (Correct)
# Bob gets 700 (WRONG - this was Charlie's bonus!)
# Charlie gets NA (WRONG)
In the example above, cbind() doesn't care about the id column; it only cares about the position of the row. You've just accidentally given Bob's bonus to Charlie and shifted the whole dataset. This is why we use joins.
Matching by keys, not by position
A join doesn't ask "What is in row 2?" instead, it asks "Where is the value '3' in both tables?" By using a common key, you ensure the data remains linked regardless of how the rows are sorted or if some records are missing. I highly recommend using the dplyr package for this; its syntax is much more readable than base R's merge().
The most common tool in your kit will be the left_join(). It keeps everything in your primary (left) table and brings in matching data from the secondary (right) table. If there's no match, it fills the gap with NA.
library(dplyr)
# The right way
correct_data <- left_join(employees, bonuses, by = "id")
print(correct_data)
# Alice 1 500
# Bob 2 NA (Correct - Bob has no bonus)
# Charlie 3 700 (Correct)
Choosing the right join for the job
Depending on what you're trying to achieve, a left join might not be enough. I usually think about joins in terms of "who is the priority?"
- inner_join(): "I only want records that exist in both tables." If an employee hasn't received a bonus, they vanish from the result entirely.
- full_join(): "I want everything." If there's a bonus record for an ID that isn't in the employee table (maybe a contractor?), it still shows up.
- right_join(): The mirror of a left join. I rarely use this in practice; I usually just swap the order of the tables in a
left_join()to keep my mental model consistent.
One quick tip: always check your row count after a join. If you expected 100 rows but ended up with 120, you probably have duplicate keys in one of your tables, causing a "Cartesian product" where R matches every instance of a key in table A to every instance in table B. It's a common headache, so keep an eye on it.
📋 Practical Task
Reconciling Product Categories with Sales Records
You have two data frames: sales_data and product_catalog. The sales data contains the transaction IDs and product IDs, but it doesn't tell you what the products actually are or how much they cost. The catalog contains the product details.
sales_data <- data.frame(
transaction_id = c(101, 102, 103, 104),
product_id = c("A1", "B2", "A1", "C3")
)
product_catalog <- data.frame(
product_id = c("A1", "B2", "C3", "D4"),
product_name = c("Laptop", "Mouse", "Keyboard", "Monitor"),
price = c(1200, 25, 75, 300)
)
Your Goal: Write the code to create a new data frame called final_report that contains all the columns from sales_data plus the product_name and price from the catalog. Ensure that if a sale happened for a product not in the catalog (though not the case here), the sale record is still preserved.
There are no comments for now.