R Programming Interview Questions for 2 Years Experience
40 questions with answers for candidates with 2 years of experience — 8 easy, 30 medium, 2 hard.
What interviewers expect: 2 years experience
At two years, interviewers expect ownership of analyses end to end. Questions cover writing reusable functions, purrr, tidy evaluation, A/B test design, regression and logistic models, and building Quarto or Shiny outputs. You should explain trade-offs, such as which test to use and why, and describe how you handled messy real-world data.
This set is weighted towards Data Wrangling with dplyr and tidyr (9), Modelling and Machine Learning in R (7), Data Visualization with ggplot2 (6), Data Structures in R (5).
Reusable functions and purrr A/B testing Regression modelling Quarto and Shiny
Solved at this levelStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
The gap indicates overfitting or data leakage. I first check for leakage, such as a feature computed using future information or the target itself, or duplicate records across train and test. Then I simplify the model, add regularization, reduce features or tune hyperparameters with cross-validation instead of a single split. If data is time-based, I make sure the split is chronological. I compare against a simple baseline to judge real improvement.
03
Your team wants to share a Shiny app with 200 internal users. What deployment options exist?
Options include shinyapps.io, a hosted service that is quickest to start but may not suit sensitive internal data, Posit Connect, a commercial on-premise or cloud server with authentication, scheduling and scaling, the free open-source Shiny Server on a Linux machine, and a Docker container behind a reverse proxy like Nginx, often with ShinyProxy for authentication. For internal data, I would choose Connect or containers inside the company network with single sign-on.
04
How do you reshape data between wide and long formats in R?
A random forest builds many decision trees, each on a bootstrap sample of rows and considering a random subset of features at each split, then averages their predictions for regression or takes a majority vote for classification. This reduces the variance of single trees and handles non-linear relationships well. In R, randomForest is the classic package and ranger is a much faster implementation. Key parameters are the number of trees, mtry and minimum node size.
Measure before optimizing. system.time() gives total time, bench::mark() compares alternatives, and profvis shows which lines consume time and memory.
system.time(result <- slow_function(df))
bench::mark(
loop = sum_loop(x),
vec = sum(x)
)
library(profvis)
profvis({
out <- clean_data(raw)
model <- fit_model(out)
})
08
Tell me about a time you automated a manual report.
Situation: my team spent two days every month building a sales report in Excel. Task: I was asked to reduce the effort. Action: I wrote an R script that pulled data from the database with DBI, cleaned it with dplyr, created charts with ggplot2 and rendered a Quarto report scheduled through cron. Result: the report ran in ten minutes, copy-paste errors disappeared and the team used the saved time for deeper analysis.
09
The same 40 lines of cleaning code are copied into ten scripts. What would you do?
I would extract the logic into well-named functions with arguments for the parts that differ, then move them into one shared file loaded with source() or, better, a small internal R package. A package gives documentation with roxygen2, tests with testthat and versioning, so every script uses the same tested logic. Fixing a bug then happens in one place. I would migrate the scripts gradually and compare outputs to confirm nothing changed.
10
What is the difference between coord_cartesian() limits and scale limits in ggplot2?
Setting limits with scale_y_continuous(limits = c(0, 100)) or ylim() removes data outside the range before statistics are calculated, which changes boxplots, smooth lines and bar totals and gives warnings about removed rows. coord_cartesian(ylim = c(0, 100)) only zooms the view, keeping all data in calculations. So for zooming into part of a chart without altering the summary, coord_cartesian() is the correct choice.
Window functions return one value per row while looking at other rows in the group. Common ones are lag() and lead() for previous or next values, row_number(), min_rank() and dense_rank() for ranking, cumsum() and cummean() for running totals, and ntile() for buckets. For example, group_by(product) %>% arrange(month) %>% mutate(prev = lag(sales)) gives the previous month's sales per product, which is the basis for growth calculations.
12
How do you explain technical results to non-technical managers?
I start with the business question and the answer in one sentence, then show one or two clear charts instead of tables of coefficients. For example, in a college project on customer churn, I said that customers with more than three complaints were twice as likely to leave, showed a simple bar chart and suggested a follow-up call process. I keep technical details in an appendix and invite questions so managers can decide confidently.
13
Overall, treatment A has a higher success rate than B, but B is better in every hospital. How is that possible?
This is Simpson's paradox. It happens when a confounding variable, here hospital or patient severity, is unevenly distributed between groups. If A was used mostly in hospitals with easier cases, its overall rate looks better even though B wins within each hospital. In R I would group by hospital and compare rates, then use a stratified analysis or regression with hospital as a covariate to estimate the adjusted effect.
14
Reading a 2 GB CSV file with read.csv() is very slow. What would you do?
read.csv() is single-threaded and guesses types slowly. I would switch to data.table::fread() or readr::read_csv(), which are many times faster and multi-threaded. I would read only the needed columns with the select argument and set column types explicitly. If the file is reused, I convert it once to a binary format like Parquet with the arrow package, which loads in seconds and supports reading just a subset of columns.
Extra rows after a join usually mean the key is duplicated in one or both tables, so each match multiplies rows. I check uniqueness with df %>% count(key) %>% filter(n > 1) on both sides. Then I decide whether to deduplicate, aggregate before joining or join on a composite key such as customer_id plus month. In dplyr 1.1 and later, the relationship argument, for example 'many-to-one', makes the join fail loudly when this assumption breaks.
The main assumptions are a linear relationship between predictors and the outcome, independent errors, constant variance of errors (homoscedasticity), normally distributed errors for valid inference and no severe multicollinearity. In R, plot(fit) shows residuals versus fitted values, a Q-Q plot and leverage, and car::vif() checks collinearity. Violations can be addressed with transformations like log, robust standard errors or different models such as GLMs.
Tidy data follows three rules: each variable is a column, each observation is a row and each type of observational unit forms a table. For example, a sales sheet with separate columns for Jan, Feb and Mar is not tidy, because month is a variable spread across columns. Reshaping it into month and sales columns makes it tidy. Tidy data works directly with dplyr, ggplot2 and modelling functions, reducing reshaping effort later.
20
What is a pure function and why prefer them in data pipelines?
A pure function always returns the same output for the same inputs and has no side effects, such as modifying global variables, writing files or printing. Pure functions are easy to test, reuse, cache and run in parallel, because they do not depend on hidden state. In data pipelines, I keep transformations pure, taking a data frame and returning a new one, and isolate side effects such as reading and writing in a few clearly named functions.
21
What is the difference between if, ifelse() and dplyr's if_else()?
if is a control-flow statement that checks a single TRUE or FALSE value and runs one branch, used for program logic. ifelse() is vectorized and returns a vector of the same length as the condition, used for transforming data. dplyr::if_else() is a stricter vectorized version that requires both outputs to have the same type, preserves classes like dates and has a missing argument for NA conditions, making it safer than ifelse().
22
How do you customise scales, labels and themes in ggplot2?
They are filtering joins: they keep rows of the left table based on matches in the right table but add no columns. semi_join() keeps rows that have a match, and anti_join() keeps rows that have no match. For example, anti_join(customers, orders, by = 'customer_id') lists customers who never ordered. They are also safer than inner joins for filtering, since they never duplicate rows when the right table has repeated keys.
25
You need month-over-month sales growth for each product. How do you calculate it in R?
I first aggregate to one row per product and month with group_by() and summarise(). Then I group by product, arrange by month and use mutate(growth = (sales - lag(sales)) / lag(sales) * 100). The first month for each product becomes NA, which is correct. I make sure months with zero sales exist using tidyr::complete(), otherwise lag() compares non-consecutive months. Finally I round and format growth for reporting.
26
Describe a disagreement with a teammate about a technical approach.
Situation: a teammate wanted to build our reporting pipeline in Excel macros, while I preferred R scripts. Task: agree on an approach quickly. Action: instead of arguing, we listed requirements together: refresh frequency, data size and who would maintain it. I built a small R prototype in a day to compare. Result: we chose R for the pipeline and kept an Excel output for users, and my teammate later helped improve the formatting.
apply() works over rows or columns of a matrix, lapply() applies a function to each element of a list and returns a list, sapply() simplifies the result to a vector or matrix when possible, vapply() is like sapply() but requires a declared output type, making it safer, and mapply() iterates over several vectors in parallel. tapply() applies a function by groups. For example, sapply(df, class) returns the class of each column.
28
Write ggplot2 code for a monthly sales line chart with points.
across() applies one or more functions to a set of columns selected with tidyselect helpers.
df %>%
mutate(across(where(is.character), str_trim)) %>%
mutate(across(starts_with('amt_'), ~ round(.x, 2)))
df %>%
group_by(region) %>%
summarise(across(c(sales, profit),
list(mean = mean, max = max),
.names = '{.col}_{.fn}'))
30
Write a tidymodels workflow to train and evaluate a random forest.
dplyr has a small set of verbs for data transformation: filter() keeps rows that meet conditions, select() picks columns, mutate() creates or changes columns, arrange() sorts rows, summarise() reduces groups to summary rows and group_by() defines groups for the next operations. Others include distinct(), rename(), relocate(), count() and slice(). Each takes a data frame as the first argument and returns a data frame, so verbs chain naturally with the pipe.
32
What is the difference between Pearson and Spearman correlation?
Pearson correlation measures the strength of a linear relationship between two numeric variables and is sensitive to outliers. Spearman correlation is computed on ranks, so it measures monotonic relationships and is robust to outliers and non-normal data. In R, cor(x, y, method = 'spearman') and cor.test() compute them. For example, ad spend and sales with diminishing returns may show a moderate Pearson value but a high Spearman value.
33
Why do we split data into training and test sets, and what is cross-validation?
A model must be evaluated on data it has not seen, otherwise performance looks better than reality. We train on one part, typically 70 to 80 percent, and evaluate on the held-out test set. Cross-validation, such as 5-fold or 10-fold, splits training data into folds, trains on all but one and validates on the remaining fold in turn, giving a more stable estimate for tuning. In tidymodels, initial_split() and vfold_cv() handle this.
34
Some stakeholders are colour-blind. How do you design charts for them?
I use colour-blind-safe palettes such as viridis with scale_colour_viridis_d() or the Okabe-Ito palette, and avoid red-green contrasts. I never rely on colour alone: I add direct labels, different shapes or line types, and facets. I limit categories to around six colours and highlight one key series while greying the rest. I also check charts with a colour-blindness simulator, such as the colorblindr package, before sharing.
35
Salary data has a few extreme values that distort averages. How do you handle them?
First I investigate whether they are data errors, such as an extra zero, or genuine high earners. I flag outliers with the IQR rule, values beyond Q1 - 1.5 IQR or Q3 + 1.5 IQR, or with percentiles. Errors I fix or remove with documentation. Genuine values I keep, but I report the median alongside the mean, or winsorize at the 1st and 99th percentiles for modelling. The choice depends on the business question.
36
A loop that adds rows to a data frame with rbind() takes very long. How do you speed it up?
Calling rbind() inside a loop copies the growing data frame each iteration, so time grows roughly quadratically. I instead store each piece in a preallocated list, results[[i]] <- piece, and combine once at the end with dplyr::bind_rows() or data.table::rbindlist(), which is the fastest. Better still is to vectorize the computation or use purrr::map() followed by list_rbind(). On 10,000 iterations this can cut runtime from minutes to seconds.
37
How do you calculate ROC AUC and plot an ROC curve in R?
Common problems are a truncated y-axis on bar charts, which exaggerates differences because bar length should start at zero, inconsistent scales across panels, 3D effects, cherry-picked time ranges, dual axes with arbitrary ranges and too many colours. Area or bubble charts can mislead when size is mapped to radius instead of area. Good practice is to start bars at zero, label units, cite the data source and show context such as targets or prior periods.
39
What is tidymodels and how does it compare with caret?
tidymodels is a collection of packages for modelling with tidyverse principles: rsample for splitting and resampling, recipes for preprocessing, parsnip for a unified model interface, workflows to bundle them, tune for hyperparameter tuning and yardstick for metrics. caret, by the same original author, is an older single package that does similar work with one train() function. caret is stable and still widely used, but new development happens in tidymodels.
geom_smooth() adds a fitted trend line with a confidence band, using loess for small data and a GAM for large data by default, or method = 'lm' for a straight line. stat_summary() computes summaries of y for each x, for example stat_summary(fun = mean, geom = 'point') to plot group means, or fun.data = mean_cl_normal for error bars. Both let you show patterns without precomputing summaries.
Frequently asked questions
What is asked in R Programming interviews for 2 years of experience?
At two years, interviewers expect ownership of analyses end to end. Questions cover writing reusable functions, purrr, tidy evaluation, A/B test design, regression and logistic models, and building Quarto or Shiny outputs. You should explain trade-offs, such as which test to use and why, and describe how you handled messy real-world data.
How many questions should I practise before a R Programming interview?
Work through all 40 questions here, then the company-wise sets for the employers you are targeting. Quality matters more than count: be able to explain why each answer is right.
Which topics matter most for 2 years of experience?
Focus first on Reusable functions and purrr, A/B testing, Regression modelling, Quarto and Shiny.