Tidyverse Expert - Comprehensive Data Manipulation Skill
Master data manipulation in R using the tidyverse's coherent ecosystem of packages. This skill provides expert guidance on transforming, cleaning, and reshaping data with complete control over all transformation operations.
The Tidyverse Philosophy
The tidyverse is built on a shared design philosophy:
- Tidy data - Each variable is a column, each observation is a row, each value is a cell
- Composable functions - First argument is always data, enabling pipe workflows
- Type stability - Functions return predictable output types
- Human-centered API - Intuitive, readable code that mirrors analytical thinking
Philosophy: Build data pipelines incrementally by composing focused functions, not by using monolithic operations. This enables infinite flexibility while maintaining code clarity.
Core Packages
Data Transformation Packages
- dplyr - Data manipulation grammar (filter, select, mutate, summarize, join)
- tidyr - Data tidying and reshaping (pivot, nest, separate, complete)
- purrr - Functional programming tools (map, walk, reduce, safely)
Specialized Manipulation Packages
- stringr - Consistent string manipulation with regex support
- forcats - Factor (categorical variable) handling and reordering
- lubridate - Date-time parsing, manipulation, and arithmetic
Package-Specific Guidance
dplyr: Data Manipulation Grammar
See references/dplyr-reference.md for complete documentation.
Five Core Verbs:
filter() - Keep rows matching conditions
select() - Keep or drop columns
mutate() - Create or modify columns
summarize() - Aggregate data to summary statistics
arrange() - Order rows by column values
Key Advanced Features:
across() - Apply functions to multiple columns
rowwise() - Row-by-row operations
- Window functions -
lag(), lead(), cumsum(), rank()
- Multiple table operations - joins, set operations, binding
Common Patterns:
# Column selection helpers
select(starts_with("x"), ends_with("_id"), contains("temp"))
# Conditional mutation
mutate(category = case_when(
value < 10 ~ "low",
value < 50 ~ "medium",
TRUE ~ "high"
))
# Grouped summaries
summarize(
across(where(is.numeric), list(mean = mean, sd = sd)),
.by = group_var
)
tidyr: Data Tidying and Reshaping
See references/tidyr-reference.md for complete documentation.
Core Operations:
- Pivoting -
pivot_longer(), pivot_wider() for reshaping
- Nesting -
nest(), unnest() for list-columns
- Rectangling -
unnest_wider(), unnest_longer() for JSON/hierarchical data
- Missing values -
complete(), fill(), drop_na(), replace_na()
- Column splitting -
separate(), separate_wider_*(), unite()
Key Concepts:
# Pivot longer - wide to long format
pivot_longer(cols = -id, names_to = "variable", values_to = "value")
# Pivot wider - long to wide format
pivot_wider(names_from = category, values_from = measurement)
# Nesting for grouped operations
nest(.by = group_var) |>
mutate(model = map(data, ~lm(y ~ x, data = .x))) |>
mutate(predictions = map2(model, data, predict))
purrr: Functional Programming
See references/purrr-reference.md for complete documentation.
Map Family - Apply functions to lists/vectors:
map() - Returns list
map_dbl(), map_int(), map_chr(), map_lgl() - Type-specific output
map2(), pmap() - Iterate over multiple inputs
imap() - Iterate with indices/names
walk() - For side effects (no return value)
Error Handling:
safely() - Capture errors without stopping
possibly() - Return default value on error
quietly() - Capture messages, warnings, output
Predicates & Logic:
keep(), discard() - Filter by predicate
some(), every(), none() - Test conditions
detect(), detect_index() - Find first match
Common Patterns:
# Read multiple files
files |> map(read_csv) |> list_rbind()
# Safe operations
results <- data |> map(safely(risky_function))
errors <- results |> map("error") |> discard(is.null)
successes <- results |> map("result") |> discard(is.null)
# Nested iterations
params |> pmap(\(x, y, z) run_model(x, y, z))
stringr: String Manipulation
See references/stringr-reference.md for complete documentation.
Core Functions (all start with str_*):
- Detection -
str_detect(), str_starts(), str_ends(), str_which()
- Extraction -
str_extract(), str_extract_all(), str_match(), str_sub()
- Replacement -
str_replace(), str_replace_all(), str_remove(), str_remove_all()
- Transformation -
str_to_lower(), str_to_upper(), str_to_title(), str_trim()
- Splitting -
str_split(), str_split_fixed(), str_split_i()
Pattern Matching:
# Regex patterns
str_detect(text, "\\d{3}-\\d{4}") # Phone pattern
str_extract_all(text, "[A-Z]\\w+") # Capital words
# String cleaning
str_trim() |> str_squish() |> str_to_lower()
forcats: Factor Handling
See references/forcats-reference.md for complete documentation.
Key Operations:
- Reordering -
fct_reorder(), fct_infreq(), fct_inorder()
- Recoding -
fct_recode(), fct_collapse(), fct_other()
- Level manipulation -
fct_relevel(), fct_rev(), fct_shift()
- Missing values -
fct_explicit_na(), fct_drop()
Common Use Cases:
# Reorder for plotting
mutate(country = fct_reorder(country, value))
# Collapse rare levels
mutate(category = fct_lump_min(category, min = 100))
# Order by frequency
mutate(item = fct_infreq(item))
lubridate: Date-Time Manipulation
See references/lubridate-reference.md for complete documentation.
Parsing Functions:
ymd(), mdy(), dmy() - Parse dates
ymd_hms(), mdy_hm() - Parse date-times
parse_date_time() - Flexible parsing
Extraction:
year(), month(), day(), wday()
hour(), minute(), second()
quarter(), week()
Arithmetic:
- Durations -
ddays(), dhours(), dminutes() (exact)
- Periods -
days(), months(), years() (human-friendly)
- Intervals -
%--% operator, int_length(), int_overlaps()
Common Patterns:
# Parse dates
dates <- mdy(c("12/31/2023", "01/01/2024"))
# Date arithmetic
today() + days(7)
floor_date(now(), "month")
# Extract components
wday(date, label = TRUE) # "Mon", "Tue", ...
month(date, label = TRUE) # "Jan", "Feb", ...
Common Workflow Patterns
Pattern 1: Data Import and Initial Cleaning
raw_data <- read_csv("data.csv") |>
janitor::clean_names() |> # Standardize column names
mutate(
date = mdy(date_col), # Parse dates
category = str_trim(str_to_lower(category)), # Clean strings
value = as.numeric(str_remove(value, "\\$")) # Clean currency
) |>
filter(!is.na(key_column)) |> # Remove invalid rows
distinct() # Remove duplicates
Pattern 2: Complex Grouping and Summarization
summary <- data |>
group_by(category, year = year(date)) |>
summarize(
across(where(is.numeric), list(
mean = \(x) mean(x, na.rm = TRUE),
median = \(x) median(x, na.rm = TRUE),
sd = \(x) sd(x, na.rm = TRUE)
)),
n = n(),
.groups = "drop"
)
Pattern 3: Pivoting and Reshaping
# Wide to long
long_data <- wide_data |>
pivot_longer(
cols = matches("\\d{4}"), # Year columns
names_to = "year",
values_to = "value",
names_transform = list(year = as.integer)
)
# Long to wide with multiple values
wide_data <- long_data |>
pivot_wider(
names_from = metric,
values_from = c(value, error),
names_glue = "{metric}_{.value}"
)
Pattern 4: Nested Data and Models
nested_models <- data |>
nest(.by = group) |>
mutate(
model = map(data, \(df) lm(y ~ x, data = df)),
tidy = map(model, broom::tidy),
glance = map(model, broom::glance),
augment = map2(model, data, broom::augment)
) |>
unnest(glance) |>
arrange(desc(r.squared))
Pattern 5: Multiple Joins
combined <- sales |>
left_join(customers, by = "customer_id") |>
left_join(products, by = "product_id") |>
left_join(regions, by = c("state" = "region_code")) |>
mutate(
revenue = quantity * unit_price,
margin = revenue - (quantity * cost)
)
Best Practices
Pipe Workflows
✅ DO:
- Use native pipe
|> (R ≥ 4.1) preferred over magrittr %>%
- Break long pipes into intermediate objects for debugging
- Put each step on its own line
- Use
.by argument in dplyr 1.1+ instead of group_by() |> ... |> ungroup()
❌ DON'T:
- Create pipes longer than 10 steps without breaking
- Mix pipe and base R assignment in confusing ways
- Forget to ungroup after group operations (if not using
.by)
Column Selection
✅ DO:
- Use tidy-select helpers:
starts_with(), ends_with(), contains(), matches(), where()
- Use
across() for multi-column operations
- Use
: for column ranges: select(id:name)
❌ DON'T:
- Use
df$column inside dplyr verbs (breaks data masking)
- Repeat similar operations instead of using
across()
Type Conversion
✅ DO:
- Use
readr::parse_*() functions for robust parsing
- Use
lubridate for dates, not base R
- Use
forcats for factors, not base R
❌ DON'T:
- Use
as.Date() with ambiguous formats
- Create factors with
factor() when you need ordered levels
- Ignore parsing warnings from
read_csv()
Missing Values
✅ DO:
- Use
tidyr::complete() to make implicit missing values explicit
- Use
tidyr::fill() to carry forward/backward
- Use
coalesce() for value replacement
- Specify
na.rm = TRUE in summary functions
❌ DON'T:
- Forget that
filter() drops NAs by default
- Use
na.omit() carelessly (drops entire rows)
Performance Tips
- Use data.table for large data: tidyverse is optimized for readability, data.table for speed
- Filter early: Reduce data size before expensive operations
- Avoid row-wise operations: Vectorize when possible; use
rowwise() only when necessary
- Use
where() instead of across(everything()): More targeted selections
- Pre-allocate with joins: Use
left_join() instead of rbind() in loops
Debugging Strategies
- Break pipes: Assign intermediate results to inspect
- Use
glimpse(): Quick data structure check
- Use
count(): Verify grouping and filtering
- Use
slice_sample(): Test on small subset first
- Check joins: Use
anti_join() to find non-matches
Common Pitfalls
Pitfall 1: Grouped Data Propagation
# ❌ Group persists unexpectedly
df |>
group_by(category) |>
summarize(mean_val = mean(value)) |>
mutate(diff = mean_val - lag(mean_val)) # Still grouped!
# ✅ Use .by or ungroup()
df |>
summarize(mean_val = mean(value), .by = category) |>
mutate(diff = mean_val - lag(mean_val))
Pitfall 2: Implicit NA Behavior
# ❌ filter() drops NAs silently
filter(value > 10) # Loses NA rows
# ✅ Be explicit
filter(value > 10 | is.na(value))
Pitfall 3: Factor Ordering
# ❌ Alphabetical ordering
ggplot(aes(x = category, y = value)) + geom_col()
# ✅ Meaningful ordering
mutate(category = fct_reorder(category, value)) |>
ggplot(aes(x = category, y = value)) + geom_col()
Supporting Resources
Complete Reference Documentation
- dplyr - references/dplyr-reference.md
- tidyr - references/tidyr-reference.md
- purrr - references/purrr-reference.md
- stringr - references/stringr-reference.md
- forcats - references/forcats-reference.md
- lubridate - references/lubridate-reference.md
Practical Examples
- Real-world workflows: examples/workflow-examples.md
- Complete case studies: examples/case-studies.md
Reusable Templates
- Common patterns: templates/data-wrangling-templates.md
Integration with Other Skills
- Use ggplot2 skill for visualization after data preparation
- Use r-tidymodels skill for machine learning workflows
- Use r-datascience skill for complete analysis guidance
- Use tidyverse-patterns skill for modern syntax updates
Quick Reference: Function Lookup
| Task |
Package |
Function |
| Filter rows |
dplyr |
filter() |
| Select columns |
dplyr |
select() |
| Create columns |
dplyr |
mutate() |
| Aggregate data |
dplyr |
summarize() |
| Sort rows |
dplyr |
arrange() |
| Join tables |
dplyr |
left_join(), inner_join(), etc. |
| Wide to long |
tidyr |
pivot_longer() |
| Long to wide |
tidyr |
pivot_wider() |
| Nest data |
tidyr |
nest() |
| Fill missing |
tidyr |
fill(), complete() |
| Apply to list |
purrr |
map(), map_dbl(), etc. |
| Safe operations |
purrr |
safely(), possibly() |
| Match pattern |
stringr |
str_detect(), str_match() |
| Extract text |
stringr |
str_extract(), str_sub() |
| Replace text |
stringr |
str_replace(), str_remove() |
| Reorder factor |
forcats |
fct_reorder(), fct_infreq() |
| Collapse levels |
forcats |
fct_collapse(), fct_lump() |
| Parse date |
lubridate |
ymd(), mdy(), dmy() |
| Extract component |
lubridate |
year(), month(), day() |
| Date arithmetic |
lubridate |
days(), months(), years() |
1---2name: tidyverse-expert3description: Expert R data manipulation with tidyverse - dplyr, tidyr, purrr, stringr, forcats, lubridate. Use when working with tidyverse, mentions "filter", "select", "mutate", "summarize", "summarise", "arrange", "group_by", "join", "joins", "dplyr verbs", "data wrangling", "manipulação de dados", "data manipulation", "tidyr pivoting", "pivot_longer", "pivot_wider", "pivot", "purrr map", "map", "map_dbl", "purrr", "string manipulation", "manipulação de strings", "stringr", "str_detect", "str_replace", "regex", "factors", "forcats", "fct_reorder", "fct_lump", "fct_collapse", "fct_infreq", "fct_recode", "reorder factors", "reordenar fatores", "reordenar níveis", "factor levels", "níveis de fator", "collapse levels", "combinar níveis", "agrupar níveis", "dates in R", "datas em R", "lubridate", "ymd", "mdy", "dmy", "parse dates", "parsear datas", "parse date", "parsing dates", "year()", "month()", "day()", "hour()", "minute()", "date arithmetic", "aritmética de datas", "date math", "floor_date", "ceiling_date", "round_date4---56# Tidyverse Expert - Comprehensive Data Manipulation Skill78Master data manipulation in R using the tidyverse's coherent ecosystem of packages. This skill provides expert guidance on transforming, cleaning, and reshaping data with complete control over all transformation operations.910## The Tidyverse Philosophy1112The tidyverse is built on a shared design philosophy:13141. **Tidy data** - Each variable is a column, each observation is a row, each value is a cell152. **Composable functions** - First argument is always data, enabling pipe workflows163. **Type stability** - Functions return predictable output types174. **Human-centered API** - Intuitive, readable code that mirrors analytical thinking1819**Philosophy**: Build data pipelines incrementally by composing focused functions, not by using monolithic operations. This enables infinite flexibility while maintaining code clarity.2021## Core Packages2223### Data Transformation Packages24- **dplyr** - Data manipulation grammar (filter, select, mutate, summarize, join)25- **tidyr** - Data tidying and reshaping (pivot, nest, separate, complete)26- **purrr** - Functional programming tools (map, walk, reduce, safely)2728### Specialized Manipulation Packages29- **stringr** - Consistent string manipulation with regex support30- **forcats** - Factor (categorical variable) handling and reordering31- **lubridate** - Date-time parsing, manipulation, and arithmetic3233## Package-Specific Guidance3435### dplyr: Data Manipulation Grammar3637See [references/dplyr-reference.md](references/dplyr-reference.md) for complete documentation.3839**Five Core Verbs**:401. **`filter()`** - Keep rows matching conditions412. **`select()`** - Keep or drop columns423. **`mutate()`** - Create or modify columns434. **`summarize()`** - Aggregate data to summary statistics445. **`arrange()`** - Order rows by column values4546**Key Advanced Features**:47- `across()` - Apply functions to multiple columns48- `rowwise()` - Row-by-row operations49- Window functions - `lag()`, `lead()`, `cumsum()`, `rank()`50- Multiple table operations - joins, set operations, binding5152**Common Patterns**:53```r54# Column selection helpers55select(starts_with("x"), ends_with("_id"), contains("temp"))5657# Conditional mutation58mutate(category = case_when(59 value < 10 ~ "low",60 value < 50 ~ "medium",61 TRUE ~ "high"62))6364# Grouped summaries65summarize(66 across(where(is.numeric), list(mean = mean, sd = sd)),67 .by = group_var68)69```7071### tidyr: Data Tidying and Reshaping7273See [references/tidyr-reference.md](references/tidyr-reference.md) for complete documentation.7475**Core Operations**:76- **Pivoting** - `pivot_longer()`, `pivot_wider()` for reshaping77- **Nesting** - `nest()`, `unnest()` for list-columns78- **Rectangling** - `unnest_wider()`, `unnest_longer()` for JSON/hierarchical data79- **Missing values** - `complete()`, `fill()`, `drop_na()`, `replace_na()`80- **Column splitting** - `separate()`, `separate_wider_*()`, `unite()`8182**Key Concepts**:83```r84# Pivot longer - wide to long format85pivot_longer(cols = -id, names_to = "variable", values_to = "value")8687# Pivot wider - long to wide format88pivot_wider(names_from = category, values_from = measurement)8990# Nesting for grouped operations91nest(.by = group_var) |>92 mutate(model = map(data, ~lm(y ~ x, data = .x))) |>93 mutate(predictions = map2(model, data, predict))94```9596### purrr: Functional Programming9798See [references/purrr-reference.md](references/purrr-reference.md) for complete documentation.99100**Map Family** - Apply functions to lists/vectors:101- `map()` - Returns list102- `map_dbl()`, `map_int()`, `map_chr()`, `map_lgl()` - Type-specific output103- `map2()`, `pmap()` - Iterate over multiple inputs104- `imap()` - Iterate with indices/names105- `walk()` - For side effects (no return value)106107**Error Handling**:108- `safely()` - Capture errors without stopping109- `possibly()` - Return default value on error110- `quietly()` - Capture messages, warnings, output111112**Predicates & Logic**:113- `keep()`, `discard()` - Filter by predicate114- `some()`, `every()`, `none()` - Test conditions115- `detect()`, `detect_index()` - Find first match116117**Common Patterns**:118```r119# Read multiple files120files |> map(read_csv) |> list_rbind()121122# Safe operations123results <- data |> map(safely(risky_function))124errors <- results |> map("error") |> discard(is.null)125successes <- results |> map("result") |> discard(is.null)126127# Nested iterations128params |> pmap(\(x, y, z) run_model(x, y, z))129```130131### stringr: String Manipulation132133See [references/stringr-reference.md](references/stringr-reference.md) for complete documentation.134135**Core Functions** (all start with `str_*`):136- **Detection** - `str_detect()`, `str_starts()`, `str_ends()`, `str_which()`137- **Extraction** - `str_extract()`, `str_extract_all()`, `str_match()`, `str_sub()`138- **Replacement** - `str_replace()`, `str_replace_all()`, `str_remove()`, `str_remove_all()`139- **Transformation** - `str_to_lower()`, `str_to_upper()`, `str_to_title()`, `str_trim()`140- **Splitting** - `str_split()`, `str_split_fixed()`, `str_split_i()`141142**Pattern Matching**:143```r144# Regex patterns145str_detect(text, "\\d{3}-\\d{4}") # Phone pattern146str_extract_all(text, "[A-Z]\\w+") # Capital words147148# String cleaning149str_trim() |> str_squish() |> str_to_lower()150```151152### forcats: Factor Handling153154See [references/forcats-reference.md](references/forcats-reference.md) for complete documentation.155156**Key Operations**:157- **Reordering** - `fct_reorder()`, `fct_infreq()`, `fct_inorder()`158- **Recoding** - `fct_recode()`, `fct_collapse()`, `fct_other()`159- **Level manipulation** - `fct_relevel()`, `fct_rev()`, `fct_shift()`160- **Missing values** - `fct_explicit_na()`, `fct_drop()`161162**Common Use Cases**:163```r164# Reorder for plotting165mutate(country = fct_reorder(country, value))166167# Collapse rare levels168mutate(category = fct_lump_min(category, min = 100))169170# Order by frequency171mutate(item = fct_infreq(item))172```173174### lubridate: Date-Time Manipulation175176See [references/lubridate-reference.md](references/lubridate-reference.md) for complete documentation.177178**Parsing Functions**:179- `ymd()`, `mdy()`, `dmy()` - Parse dates180- `ymd_hms()`, `mdy_hm()` - Parse date-times181- `parse_date_time()` - Flexible parsing182183**Extraction**:184- `year()`, `month()`, `day()`, `wday()`185- `hour()`, `minute()`, `second()`186- `quarter()`, `week()`187188**Arithmetic**:189- Durations - `ddays()`, `dhours()`, `dminutes()` (exact)190- Periods - `days()`, `months()`, `years()` (human-friendly)191- Intervals - `%--%` operator, `int_length()`, `int_overlaps()`192193**Common Patterns**:194```r195# Parse dates196dates <- mdy(c("12/31/2023", "01/01/2024"))197198# Date arithmetic199today() + days(7)200floor_date(now(), "month")201202# Extract components203wday(date, label = TRUE) # "Mon", "Tue", ...204month(date, label = TRUE) # "Jan", "Feb", ...205```206207## Common Workflow Patterns208209### Pattern 1: Data Import and Initial Cleaning210```r211raw_data <- read_csv("data.csv") |>212 janitor::clean_names() |> # Standardize column names213 mutate(214 date = mdy(date_col), # Parse dates215 category = str_trim(str_to_lower(category)), # Clean strings216 value = as.numeric(str_remove(value, "\\$")) # Clean currency217 ) |>218 filter(!is.na(key_column)) |> # Remove invalid rows219 distinct() # Remove duplicates220```221222### Pattern 2: Complex Grouping and Summarization223```r224summary <- data |>225 group_by(category, year = year(date)) |>226 summarize(227 across(where(is.numeric), list(228 mean = \(x) mean(x, na.rm = TRUE),229 median = \(x) median(x, na.rm = TRUE),230 sd = \(x) sd(x, na.rm = TRUE)231 )),232 n = n(),233 .groups = "drop"234 )235```236237### Pattern 3: Pivoting and Reshaping238```r239# Wide to long240long_data <- wide_data |>241 pivot_longer(242 cols = matches("\\d{4}"), # Year columns243 names_to = "year",244 values_to = "value",245 names_transform = list(year = as.integer)246 )247248# Long to wide with multiple values249wide_data <- long_data |>250 pivot_wider(251 names_from = metric,252 values_from = c(value, error),253 names_glue = "{metric}_{.value}"254 )255```256257### Pattern 4: Nested Data and Models258```r259nested_models <- data |>260 nest(.by = group) |>261 mutate(262 model = map(data, \(df) lm(y ~ x, data = df)),263 tidy = map(model, broom::tidy),264 glance = map(model, broom::glance),265 augment = map2(model, data, broom::augment)266 ) |>267 unnest(glance) |>268 arrange(desc(r.squared))269```270271### Pattern 5: Multiple Joins272```r273combined <- sales |>274 left_join(customers, by = "customer_id") |>275 left_join(products, by = "product_id") |>276 left_join(regions, by = c("state" = "region_code")) |>277 mutate(278 revenue = quantity * unit_price,279 margin = revenue - (quantity * cost)280 )281```282283## Best Practices284285### Pipe Workflows286✅ **DO**:287- Use native pipe `|>` (R ≥ 4.1) preferred over magrittr `%>%`288- Break long pipes into intermediate objects for debugging289- Put each step on its own line290- Use `.by` argument in dplyr 1.1+ instead of `group_by() |> ... |> ungroup()`291292❌ **DON'T**:293- Create pipes longer than 10 steps without breaking294- Mix pipe and base R assignment in confusing ways295- Forget to ungroup after group operations (if not using `.by`)296297### Column Selection298✅ **DO**:299- Use tidy-select helpers: `starts_with()`, `ends_with()`, `contains()`, `matches()`, `where()`300- Use `across()` for multi-column operations301- Use `:` for column ranges: `select(id:name)`302303❌ **DON'T**:304- Use `df$column` inside dplyr verbs (breaks data masking)305- Repeat similar operations instead of using `across()`306307### Type Conversion308✅ **DO**:309- Use `readr::parse_*()` functions for robust parsing310- Use `lubridate` for dates, not base R311- Use `forcats` for factors, not base R312313❌ **DON'T**:314- Use `as.Date()` with ambiguous formats315- Create factors with `factor()` when you need ordered levels316- Ignore parsing warnings from `read_csv()`317318### Missing Values319✅ **DO**:320- Use `tidyr::complete()` to make implicit missing values explicit321- Use `tidyr::fill()` to carry forward/backward322- Use `coalesce()` for value replacement323- Specify `na.rm = TRUE` in summary functions324325❌ **DON'T**:326- Forget that `filter()` drops NAs by default327- Use `na.omit()` carelessly (drops entire rows)328329## Performance Tips3303311. **Use data.table for large data**: tidyverse is optimized for readability, data.table for speed3322. **Filter early**: Reduce data size before expensive operations3333. **Avoid row-wise operations**: Vectorize when possible; use `rowwise()` only when necessary3344. **Use `where()` instead of `across(everything())`**: More targeted selections3355. **Pre-allocate with joins**: Use `left_join()` instead of `rbind()` in loops336337## Debugging Strategies3383391. **Break pipes**: Assign intermediate results to inspect3402. **Use `glimpse()`**: Quick data structure check3413. **Use `count()`**: Verify grouping and filtering3424. **Use `slice_sample()`**: Test on small subset first3435. **Check joins**: Use `anti_join()` to find non-matches344345## Common Pitfalls346347### Pitfall 1: Grouped Data Propagation348```r349# ❌ Group persists unexpectedly350df |>351 group_by(category) |>352 summarize(mean_val = mean(value)) |>353 mutate(diff = mean_val - lag(mean_val)) # Still grouped!354355# ✅ Use .by or ungroup()356df |>357 summarize(mean_val = mean(value), .by = category) |>358 mutate(diff = mean_val - lag(mean_val))359```360361### Pitfall 2: Implicit NA Behavior362```r363# ❌ filter() drops NAs silently364filter(value > 10) # Loses NA rows365366# ✅ Be explicit367filter(value > 10 | is.na(value))368```369370### Pitfall 3: Factor Ordering371```r372# ❌ Alphabetical ordering373ggplot(aes(x = category, y = value)) + geom_col()374375# ✅ Meaningful ordering376mutate(category = fct_reorder(category, value)) |>377 ggplot(aes(x = category, y = value)) + geom_col()378```379380## Supporting Resources381382### Complete Reference Documentation383- **dplyr** - [references/dplyr-reference.md](references/dplyr-reference.md)384- **tidyr** - [references/tidyr-reference.md](references/tidyr-reference.md)385- **purrr** - [references/purrr-reference.md](references/purrr-reference.md)386- **stringr** - [references/stringr-reference.md](references/stringr-reference.md)387- **forcats** - [references/forcats-reference.md](references/forcats-reference.md)388- **lubridate** - [references/lubridate-reference.md](references/lubridate-reference.md)389390### Practical Examples391- Real-world workflows: [examples/workflow-examples.md](examples/workflow-examples.md)392- Complete case studies: [examples/case-studies.md](examples/case-studies.md)393394### Reusable Templates395- Common patterns: [templates/data-wrangling-templates.md](templates/data-wrangling-templates.md)396397## Integration with Other Skills398399- Use **ggplot2** skill for visualization after data preparation400- Use **r-tidymodels** skill for machine learning workflows401- Use **r-datascience** skill for complete analysis guidance402- Use **tidyverse-patterns** skill for modern syntax updates403404## Quick Reference: Function Lookup405406| Task | Package | Function |407|------|---------|----------|408| Filter rows | dplyr | `filter()` |409| Select columns | dplyr | `select()` |410| Create columns | dplyr | `mutate()` |411| Aggregate data | dplyr | `summarize()` |412| Sort rows | dplyr | `arrange()` |413| Join tables | dplyr | `left_join()`, `inner_join()`, etc. |414| Wide to long | tidyr | `pivot_longer()` |415| Long to wide | tidyr | `pivot_wider()` |416| Nest data | tidyr | `nest()` |417| Fill missing | tidyr | `fill()`, `complete()` |418| Apply to list | purrr | `map()`, `map_dbl()`, etc. |419| Safe operations | purrr | `safely()`, `possibly()` |420| Match pattern | stringr | `str_detect()`, `str_match()` |421| Extract text | stringr | `str_extract()`, `str_sub()` |422| Replace text | stringr | `str_replace()`, `str_remove()` |423| Reorder factor | forcats | `fct_reorder()`, `fct_infreq()` |424| Collapse levels | forcats | `fct_collapse()`, `fct_lump()` |425| Parse date | lubridate | `ymd()`, `mdy()`, `dmy()` |426| Extract component | lubridate | `year()`, `month()`, `day()` |427| Date arithmetic | lubridate | `days()`, `months()`, `years()` |