R Interview Questions
R interviews for data science and analytics roles typically probe four layers of knowledge: core language semantics (scoping, vectorization, NA handling), data structure fluency (vectors, lists, data frames, factors), practical data wrangling with the tidyverse, and enough debugging and performance awareness to show you've actually shipped R code beyond a classroom exercise. Interviewers care less about memorizing function signatures than about whether you can explain why R behaves the way it does — for example, why NA + 1 returns NA instead of erroring, or why a factor's levels can silently reorder a bar chart.
Cricket analogy: An R interview probing four knowledge layers is like a national selector assessing a batter's technique, temperament, fitness, and match awareness before picking them for a Test squad — no single skill is enough on its own.
Core Language Fundamentals
A recurring interview theme is R's treatment of missing values and vectorized comparisons: NA represents "unknown," so any arithmetic or comparison involving it (NA == NA, NA > 5, sum(c(1, NA, 3))) propagates NA rather than silently ignoring it, which is why sum() and mean() need na.rm = TRUE explicitly. Another common thread is lexical scoping and environments — R functions capture the environment in which they were defined (not where they're called from), which is what makes closures work, and interviewers often ask you to predict the output of a function that returns another function referencing a variable from its enclosing scope.
Cricket analogy: NA propagating through arithmetic instead of being silently ignored is like the Duckworth-Lewis method refusing to calculate a target when a crucial input like overs remaining is genuinely unknown — it flags the uncertainty rather than guessing a number.
# Common interview probe: NA propagation
x <- c(4, NA, 9, 2)
sum(x) # NA
sum(x, na.rm = TRUE) # 15
mean(x) # NA
mean(x, na.rm = TRUE) # 5
# Common interview probe: lexical scoping / closures
make_counter <- function() {
count <- 0
function() {
count <<- count + 1 # modifies the enclosing environment's count
count
}
}
counter <- make_counter()
counter() # 1
counter() # 2Data Structures and the Apply Family
Interviewers frequently test whether you understand R's core data structures well enough to pick the right one: an atomic vector holds one type only (numeric, character, logical), a list can hold mixed types and even other lists (making it R's closest analog to a nested JSON structure), and a data frame is technically a list of equal-length vectors, which explains why df[[1]] and df$col_name return a vector but df[1] returns a one-column data frame. The apply family — sapply(), lapply(), vapply(), mapply() — is a classic whiteboard topic because each function differs in return type predictability: lapply() always returns a list, sapply() tries to simplify to a vector or matrix unpredictably, and vapply() forces you to declare the expected output type up front, which is why production code should generally prefer vapply() over sapply().
Cricket analogy: A data frame being technically a list of equal-length vectors is like a scorecard being fundamentally a set of equal-length columns (overs, runs, wickets) bundled together — df$col_name pulling one column out is like extracting just the wickets column from that shared scorecard.
Data Wrangling and the Tidyverse
Tidyverse-focused interview questions usually center on the distinction between wide and long data formats and the pivot_longer()/pivot_wider() functions from tidyr that convert between them, since most real datasets arrive wide (one column per year or category) but most modeling and ggplot2 functions expect long, "tidy" data (one row per observation). Interviewers also commonly ask about the difference between left_join(), inner_join(), and full_join() from dplyr, and a classic gotcha question is what happens to row count when a join key has duplicate values on either side — the answer is that dplyr performs a full Cartesian match on the duplicated keys, which can silently multiply your row count if you didn't expect duplicates.
Cricket analogy: pivot_longer() reshaping wide data (one column per year) into long, tidy format is like converting a career batting-average table with one column per season into a single ball-by-ball log — the same information, restructured so it can actually be analyzed match by match.
A quick way to sanity-check a dplyr join before trusting it: compare nrow(joined_df) to nrow(left_table) immediately after the join. If the joined row count is noticeably larger than the left table's row count, the join key almost certainly has unexpected duplicates on the right-hand side.
Debugging, Performance, and Advanced Topics
Beyond syntax, senior R interviews probe debugging workflow — knowing traceback() to see the call stack after an error, browser() or debug() to step through a function interactively, and options(warn = 2) to convert warnings into errors so a silent NA-producing coercion doesn't slip through unnoticed — as well as performance literacy: recognizing when data.table or dtplyr will meaningfully outperform dplyr on large datasets (typically millions of rows), and knowing that R's copy-on-modify semantics mean that modifying a data frame inside a function returns a new copy rather than mutating the caller's original object. A well-prepared candidate can also explain the difference between S3, S4, and R6 object systems at a high level, since seeing all three in a single legacy codebase is common in R shops that have grown organically over a decade or more.
Cricket analogy: traceback() showing the call stack after an error is like a third umpire reviewing every stage of a run-out appeal in sequence — the throw, the collection, the bail dislodging — to pinpoint exactly where the decision went wrong.
sapply() is a common interview trap in production code: it silently changes its return type based on the input (a vector if results are uniform length, a list if they're not), which means code that works fine in testing can break unpredictably in production when one edge-case input produces a differently shaped result. Prefer vapply() with an explicit FUN.VALUE, or purrr::map_dbl()/map_chr(), for predictable, type-safe output.
- NA propagates through arithmetic and comparisons (NA + 1, NA > 5) rather than being ignored; use na.rm = TRUE explicitly in sum()/mean().
- R uses lexical scoping — closures capture the environment where a function was defined, not where it's called from.
- A data frame is technically a list of equal-length vectors; df[[1]] returns a vector, df[1] returns a one-column data frame.
- Prefer vapply() over sapply() in production code because vapply() enforces a predictable, declared return type.
- pivot_longer()/pivot_wider() convert between wide and long data; most modeling and ggplot2 functions expect long, tidy data.
- A dplyr join with duplicate keys on either side performs a full Cartesian match on those duplicates, which can silently multiply row counts.
- R's copy-on-modify semantics mean modifying a data frame inside a function returns a new copy rather than mutating the caller's original.
Practice what you learned
1. What does sum(c(1, NA, 3)) return in R by default?
2. What R scoping rule explains why a closure returned from make_counter() correctly remembers its own count variable across calls?
3. Why is vapply() generally preferred over sapply() in production R code?
4. What happens when you perform a dplyr join and the join key has duplicate values on one side?
5. What does R's copy-on-modify semantics mean for a function that modifies a data frame passed as an argument?
Was this page helpful?
You May Also Like
R vs Python for Data Science
A practical comparison of R and Python for data science work — language design, data wrangling syntax, statistical modeling, visualization, and how teams choose (or combine) both.
R Quick Reference
A condensed cheat sheet of essential R syntax: data types and structures, dplyr verbs and piping, string/date/regex helpers, and common base R and statistics functions.
The apply Family of Functions
How R's apply family — lapply, sapply, apply, vapply, and mapply — replaces explicit loops with predictable, functional-style iteration.
dplyr Basics
Learn the core dplyr verbs—filter, select, mutate, arrange, summarise, and group_by—for fast, readable data manipulation in R.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics