Skip to content

Biostatistician Interview Questions & Answers

40 questions for Biostatistician interviews — 18 medium, 22 hard.

About the Biostatistician role

A Biostatistician designs studies and analyses health and life-science data, including sample size planning, survival analysis, regression models and hypothesis testing. They interpret results for clinicians and regulators and write statistical analysis plans. The role usually needs a master's degree in statistics or biostatistics and strong R skills, and is found in pharma, CROs, hospitals and public health research. Clear writing for non-statisticians is a key part of the job.

Study design Survival analysis Regression modelling Power analysis Statistical reporting
Solved for this roleStart 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.
3 questions free Unlock the rest in seconds
01

What does a 95 percent confidence interval mean?

Save to ▾
Show answer
If we repeated the sampling many times and built an interval each time, about 95 percent of those intervals would contain the true population value. It does not mean there is a 95 percent probability that this specific interval contains it. A narrow interval shows precise estimation and a wide one shows uncertainty. For example, average order value of 850 INR with CI 820 to 880 is far more useful than CI 600 to 1100.
02

How do you check whether data is normally distributed in R?

Save to ▾
Show answer
Combine visual checks with formal tests, because tests become oversensitive with large samples. hist(x, breaks = 30) qqnorm(x); qqline(x, col = 'red') shapiro.test(x) # works for n between 3 and 5000 library(ggplot2) ggplot(df, aes(sample = x)) + stat_qq() + stat_qq_line() # for model residuals plot(lm_fit, which = 2)
03

Your function fails with 'the condition has length > 1' when given a vector. What is wrong?

Save to ▾
Show answer
An if statement can only check a single TRUE or FALSE value, and since R 4.2 a longer condition is an error. The function was written for one value but received a vector. I would replace if with a vectorized alternative such as ifelse(), dplyr::if_else() or case_when(). If the function is truly meant for single values, I validate the input length with stopifnot(length(x) == 1) or apply it over elements with purrr::map().
04

How do you run R code in parallel?

Save to ▾
Show answer
The parallel package ships with R: mclapply() forks processes on Linux and macOS, while parLapply() uses a socket cluster that also works on Windows. The future framework gives a cleaner interface where you choose a plan, such as plan(multisession), and then use future_map() from furrr or future_lapply(). Parallelism helps when each task is independent and heavy, such as model fitting per group, but adds overhead for small tasks and multiplies memory use.
05

What is the multiple testing problem and how do you correct for it?

Save to ▾
Show answer
When you run many hypothesis tests, the chance of at least one false positive rises sharply. With 20 independent tests at alpha 0.05, it is about 64 percent. Corrections adjust p-values: Bonferroni multiplies by the number of tests and is conservative, Holm is a less strict step-down version, and Benjamini-Hochberg controls the false discovery rate. In R, p.adjust(pvals, method = 'BH') applies them, which is common when testing many metrics or segments.
06

Tell me about a time your analysis did not support what a stakeholder hoped to see.

Save to ▾
Show answer
Situation: a marketing head expected a new campaign to have doubled sign-ups. Task: evaluate its impact. Action: my analysis in R using a pre-post comparison with a control region showed only a 6 percent lift, much of it seasonal. I presented the method and confidence interval openly, acknowledged what went well and suggested a cleaner randomized test. Result: the stakeholder accepted the finding, and the next campaign was run as a proper experiment.
07

What is tidy evaluation and why do you need {{ }} in functions?

Save to ▾
Show answer
dplyr uses data masking, so you can write column names without quotes. Inside your own function, a column passed as an argument is not found automatically because R looks for a variable with that name. Embracing the argument with {{ }} tells dplyr to use the column the caller supplied. For column names stored as strings, you use .data[[var]]. For new column names, use the glue syntax '{{var}}_mean' := with the walrus operator.
08

Explain Type I and Type II errors.

Save to ▾
Show answer
A Type I error is rejecting a true null hypothesis, a false positive, and its probability is the significance level alpha, usually 0.05. A Type II error is failing to reject a false null hypothesis, a false negative, with probability beta; power is 1 - beta. For example, launching a feature that does not actually help is a Type I error, while dropping a feature that really works is Type II. Larger samples reduce Type II errors.
09

How do you perform a t-test in R?

Save to ▾
Show answer
t.test() handles one-sample, two-sample and paired tests. Welch's test, which does not assume equal variances, is the default. # two independent groups t.test(score ~ batch, data = students) # paired: before vs after training t.test(after, before, paired = TRUE) # one-sample against a target of 30 minutes t.test(delivery_time, mu = 30, alternative = 'greater') res <- t.test(score ~ batch, data = students) res$p.value; res$conf.int
10

What are the differences between S3, S4 and R6 classes?

Save to ▾
Show answer
S3 is R's simplest object system: an object is a list with a class attribute, and generic functions like print() dispatch on it through UseMethod(). S4 adds formal class definitions with setClass(), typed slots and validity checks, and is common in Bioconductor. R6, from a package, provides reference semantics with mutable objects and encapsulated methods, similar to classes in Python or Java, and is used inside Shiny. Most analysis code needs only S3.
11

You are analysing a national survey where some groups were deliberately oversampled. How do you compute correct averages?

Save to ▾
Show answer
Simple averages would be biased toward oversampled groups, so I use the survey weights provided with the data. In R, the survey package defines the design with svydesign(ids = ~psu, strata = ~stratum, weights = ~wt, data = df), then svymean(), svytotal() and svyglm() give weighted estimates with correct standard errors that account for clustering and stratification. The srvyr package offers the same with dplyr-style syntax.
12

What is Rcpp and when would you use it?

Save to ▾
Show answer
Rcpp is a package that lets you write C++ functions and call them from R with almost no boilerplate, using cppFunction() for small snippets or sourceCpp() for files. It is used when R code has loops that cannot be vectorized, such as recursive calculations, simulations with dependence between steps or custom algorithms. Many popular packages, including dplyr and data.table internals, rely on compiled code. Use it only after profiling shows a real bottleneck.
13

How do you debug an R function?

Save to ▾
Show answer
After an error, traceback() shows the call stack so I can see which function failed. I insert browser() inside a function to pause execution and inspect variables interactively, stepping with n and continuing with c. debug(fn) or debugonce(fn) enters the browser on the next call without editing code. options(error = recover) lets me choose a frame to inspect after an error. RStudio breakpoints offer the same features visually.
14

How do you write unit tests for an R function?

Save to ▾
Show answer
testthat organises tests into files under tests/testthat/ using expectations. # tests/testthat/test-gst.R test_that('gst_price adds 18 percent by default', { expect_equal(gst_price(100), 118) expect_equal(gst_price(c(100, 200)), c(118, 236)) }) test_that('gst_price rejects text input', { expect_error(gst_price('abc'), 'numeric') }) # run all tests devtools::test()
15

How do you interpret the summary output of an lm() model?

Save to ▾
Show answer
Each coefficient is the expected change in the outcome for a one-unit increase in that predictor, holding others constant. The p-value tests whether the coefficient differs from zero. R-squared is the share of variance explained, and adjusted R-squared penalizes extra predictors. Residual standard error shows typical prediction error in outcome units. The F-statistic tests whether the model beats an intercept-only model. For factors, coefficients are differences from the reference level.
16

Describe a mistake you made in an analysis and how you handled it.

Save to ▾
Show answer
Situation: I shared a retention analysis showing improvement, but a week later I noticed I had excluded customers with missing signup dates, who were mostly churned users. Task: correct it before decisions were made. Action: I informed my manager immediately, reran the analysis with proper handling, explained the impact clearly and added a missing-value check to my template. Result: the corrected numbers were used, and my manager appreciated the transparency.
17

Tell me about a time a model you built did not perform as expected after deployment.

Save to ▾
Show answer
Situation: a demand model I built performed well in testing but its errors doubled two months after launch. Task: diagnose and fix it. Action: I compared input distributions and found a new product category and a pricing change that the training data never saw. I added drift monitoring, retrained with recent data and set up monthly retraining. Result: accuracy returned to target, and I learned to plan monitoring before deployment, not after.
18

Tell me about a time a production job failed and you had to fix it under pressure.

Save to ▾
Show answer
Situation: the scheduled R job feeding the morning leadership dashboard failed at 6 AM after a source table added a new column type. Task: restore the dashboard before the 9 AM meeting. Action: I checked the logs, identified the parsing error, patched the column specification, reran the job and informed stakeholders of the delay. Afterwards I added schema validation and a failure alert. Result: data was live by 8:30 AM, and similar changes are now caught early.
19

How do you use Reduce(), Filter() and Map() in base R?

Save to ▾
Show answer
These higher-order functions come from functional programming. nums <- 1:10 Filter(function(x) x %% 2 == 0, nums) # even numbers Map(function(a, b) a + b, 1:3, 4:6) # list: 5 7 9 Reduce(`+`, 1:5, accumulate = TRUE) # 1 3 6 10 15 # join many data frames on a common key dfs <- list(sales, targets, regions) Reduce(function(x, y) merge(x, y, by = 'region_id'), dfs) # purrr equivalents: keep(), map2(), reduce()
20

What is lazy evaluation in R?

Save to ▾
Show answer
R evaluates function arguments lazily, only when they are first used inside the function body. Arguments are stored as promises holding the expression and its environment. For example, f <- function(x, y) x * 2 works with f(5, stop('error')) because y is never touched. Lazy evaluation enables defaults that depend on other arguments and non-standard evaluation in dplyr, but it can surprise you when creating closures in a loop, which force() fixes.
21

Product wants to detect a lift in conversion from 5 to 5.5 percent. How many users do you need per group?

Save to ▾
Show answer
I use power analysis before the test starts. In R, power.prop.test(p1 = 0.05, p2 = 0.055, power = 0.8, sig.level = 0.05) gives roughly 31,000 users per group. Smaller lifts need far larger samples, since sample size scales with the inverse square of the effect. I would then convert the sample into test duration using daily traffic, round up to full weeks, and agree on the stopping rule in advance to avoid peeking bias.
22

A loop calls an external API 5,000 times and crashes at call 3,200. How do you make it robust?

Save to ▾
Show answer
I wrap each call with purrr::safely() or tryCatch() so one failure does not stop the loop, and add retries with exponential backoff, which httr2 supports through req_retry(). I respect rate limits with Sys.sleep() or req_throttle(). I save results incrementally, for example every 100 calls to an RDS file or database, so a restart resumes from the last checkpoint. Finally I log failed IDs and rerun only those.
23

How do you do feature engineering with recipes in tidymodels?

Save to ▾
Show answer
A recipe defines preprocessing steps that are learned on training data and applied identically to new data. rec <- recipe(price ~ ., data = train) %>% update_role(listing_id, new_role = 'id') %>% step_date(listed_on, features = c('month', 'dow')) %>% step_rm(listed_on) %>% step_log(area_sqft) %>% step_other(locality, threshold = 0.01) %>% step_dummy(all_nominal_predictors()) %>% step_normalize(all_numeric_predictors()) prep(rec) %>% bake(new_data = NULL) %>% glimpse()
24

Overall, treatment A has a higher success rate than B, but B is better in every hospital. How is that possible?

Save to ▾
Show answer
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.
25

How do you validate inputs inside an R function?

Save to ▾
Show answer
Fail early with clear messages using stopifnot(), stop() and match.arg(). summarise_kpi <- function(df, metric = c('revenue', 'orders', 'aov'), min_date = NULL) { stopifnot(is.data.frame(df), nrow(df) > 0) metric <- match.arg(metric) if (!is.null(min_date) && !inherits(min_date, 'Date')) { stop('min_date must be a Date', call. = FALSE) } required <- c('date', metric) missing <- setdiff(required, names(df)) if (length(missing)) stop('Missing columns: ', toString(missing)) # ... compute }
26

What is the difference between Pearson and Spearman correlation?

Save to ▾
Show answer
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.
27

What are closures and function factories in R?

Save to ▾
Show answer
A closure is a function that remembers the environment in which it was created. A function factory is a function that returns such functions. For example, power <- function(exp) function(x) x^exp; square <- power(2) creates a function that remembers exp = 2. They are useful for creating families of related functions, counters and memoised functions. Because of lazy evaluation, call force(exp) inside the factory so the value is captured immediately.
28

You are asked to forecast daily demand for the Diwali season. How do you approach it?

Save to ▾
Show answer
I gather at least two or three years of daily history, since festive peaks happen once a year and Diwali moves across October and November. I add holiday regressors for Diwali, Dussehra and sale events rather than relying on calendar seasonality alone. I compare models like ETS, ARIMA with regressors and prophet in fable, validating on past festive seasons. I report prediction intervals and review with business teams for planned promotions or stock limits.
29

What tools does R offer for time series forecasting?

Save to ▾
Show answer
The forecast package provides auto.arima(), ets() for exponential smoothing and easy plotting of forecasts with intervals. The newer tidyverts family, with tsibble for time-series data frames, fable for models and feasts for features and decomposition, works with tidy data and fits many series at once. prophet handles strong seasonality and holidays. Typical steps are visualizing, decomposing trend and seasonality, fitting candidates and comparing accuracy on a holdout period.
30

How do you calculate ROC AUC and plot an ROC curve in R?

Save to ▾
Show answer
yardstick in tidymodels or pROC compute AUC from predicted probabilities. library(yardstick) preds <- tibble(truth = factor(test$churned, levels = c('1', '0')), .pred_1 = test$prob) roc_auc(preds, truth, .pred_1) roc_curve(preds, truth, .pred_1) %>% autoplot() # with pROC library(pROC) r <- roc(test$churned, test$prob) auc(r); plot(r)
31

What is regularization and how do you apply it in R?

Save to ▾
Show answer
Regularization adds a penalty on coefficient size to reduce overfitting. Ridge (L2) shrinks all coefficients toward zero, useful with correlated predictors. Lasso (L1) can shrink some to exactly zero, performing feature selection. Elastic net mixes both. In R, glmnet fits these models, with alpha = 0 for ridge and alpha = 1 for lasso, and cv.glmnet() chooses the penalty lambda by cross-validation. Predictors should be standardized, which glmnet does by default.
32

How do you handle errors in R code?

Save to ▾
Show answer
tryCatch() catches errors and warnings so a script can recover or log them; finally runs cleanup. read_safe <- function(path) { tryCatch({ readr::read_csv(path, show_col_types = FALSE) }, warning = function(w) { message('Warning in ', path, ': ', conditionMessage(w)) NULL }, error = function(e) { message('Failed: ', path, ' - ', conditionMessage(e)) NULL }, finally = message('Done: ', path)) }
33

Write code using map2() and pmap() to iterate over multiple inputs.

Save to ▾
Show answer
map2() pairs two vectors and pmap() takes a list or data frame of arguments. library(purrr) prices <- c(100, 250, 400) qty <- c(2, 1, 5) map2_dbl(prices, qty, ~ .x * .y) # 200 250 2000 params <- tibble::tibble( region = c('North', 'South'), year = c(2025, 2026) ) pmap(params, function(region, year) { build_report(region = region, year = year) }) walk2(plots, paths, ~ ggsave(.y, .x))
34

What does the ... (dots) argument do in an R function?

Save to ▾
Show answer
The dots let a function accept any number of additional arguments and pass them on to another function. For example, my_plot <- function(df, ...) ggplot(df, aes(x, y)) + geom_point(...) lets callers pass colour or size without declaring them. You can capture dots with list(...) or count them with ...length(). The downside is that misspelled argument names are silently ignored, so document them and validate with rlang::check_dots_used() in packages.
35

What is bootstrapping and when would you use it?

Save to ▾
Show answer
Bootstrapping estimates the sampling distribution of a statistic by resampling the data with replacement many times and recomputing the statistic each time. The spread of those results gives standard errors and confidence intervals without assuming normality. It is useful for medians, ratios or complex metrics like revenue per user, where formulas are hard. In R, the boot package or a simple replicate() loop with sample(x, replace = TRUE) does it.
36

Your scatter plot of 200,000 points looks like a solid blob. How do you fix it?

Save to ▾
Show answer
This is overplotting. I reduce point size and add transparency with geom_point(alpha = 0.1, size = 0.5). Better options are geom_hex() or geom_bin2d(), which show density as colour, or geom_density_2d() contours. I could also plot a random sample for shape and a smoothed trend with geom_smooth() on the full data. If x values are discrete, geom_jitter() spreads points. The goal is to make density visible, not every point.
37

A simulation loop runs for 40 minutes. How would you speed it up?

Save to ▾
Show answer
First I profile with profvis to find the slow lines. Then I try vectorizing, for example generating all random numbers at once with rnorm(n * reps) and using matrix operations instead of nested loops. I preallocate outputs instead of growing them. If iterations are independent, I run them in parallel with future and furrr. If a tight loop cannot be vectorized, I rewrite it in C++ with Rcpp, which often gives a 10 to 100 times speed-up.
38

A manager says a test with p = 0.06 proves the change had no effect. How do you respond?

Save to ▾
Show answer
I explain that failing to reject the null is not proof of no effect. A p-value of 0.06 means the evidence is weak or the test was underpowered, and the 0.05 cut-off is a convention, not a law. I would show the confidence interval for the effect, which probably ranges from slightly negative to clearly positive, check whether the sample size met the planned power and suggest extending the test or weighing the decision on cost and risk.
39

Business users do not trust your gradient boosting model because it is a black box. What do you do?

Save to ▾
Show answer
I explain both global and individual behaviour. Globally, I show variable importance and partial dependence plots, using DALEX or the vip package, to show how key features affect predictions. For individual cases I use SHAP values, for example through the shapviz or fastshap packages, so a manager can see why a customer was flagged. I compare against a simple logistic model and walk users through real examples they know.
40

You ran an A/B test on a checkout page: 4.1 percent conversion versus 4.6 percent. How do you decide if the difference is real?

Save to ▾
Show answer
Conversion is a proportion, so I use a two-proportion test: prop.test(c(conv_a, conv_b), c(n_a, n_b)). I check the p-value and, more importantly, the confidence interval for the difference. Before trusting it, I confirm the sample size was planned in advance, the test ran for full weeks to cover weekday effects, users were randomized properly and nobody stopped the test early when results looked good. Then I judge business significance.

Frequently asked questions

What does a Biostatistician do?

A Biostatistician designs studies and analyses health and life-science data, including sample size planning, survival analysis, regression models and hypothesis testing. They interpret results for clinicians and regulators and write statistical analysis plans. The role usually needs a master's degree in statistics or biostatistics and strong R skills, and is found in pharma, CROs, hospitals and public health research. Clear writing for non-statisticians is a key part of the job.

Which skills are tested in a Biostatistician interview?

Expect questions on Study design, Survival analysis, Regression modelling, Power analysis, Statistical reporting. Most rounds also ask you to explain a project and solve one practical problem live.

How do I prepare for a Biostatistician interview?

Revise Statistics and Hypothesis Testing, Modelling and Machine Learning in R, Functions and Functional Programming first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.