TL;DR
- Explain the shape and meaning of your data before choosing an R function. A vector, list and data frame behave differently when you subset them.
- Decide how missing values should affect the result. Dropping an unknown order value or treating it as zero changes the business interpretation.
- Practise with the complete base R function below: it validates order identity, reports excluded rows and produces deterministic regional summaries.
- Test empty inputs, missing values, duplicate IDs and joins that multiply rows. State which assumptions your tests cover and which require real pipeline evidence.
A useful way to practise R interview questions
Imagine an operations analyst receiving a table of orders with three fields: order_id, region and amount. The task is to summarize usable orders by region while reporting which rows cannot be included. Each row should represent one order, amounts use one currency, and the supplied values are already parsed into appropriate R types.
The business owner has made two decisions for this exercise. Missing amounts must remain visible as a data-quality issue, and duplicate order IDs must cause a failure rather than silently double-count revenue. A missing or blank region also makes a row unusable for this regional report.
These eight questions are original practice prompts. They are not a claimed question list from a particular employer. The function and its fixtures were executed in R through WebR; a production pipeline still needs tests against its real input files, package versions and data contracts.
1. How do vectors, lists and data frames differ?
An atomic vector holds values of a common atomic type. A list can hold elements of different types, including other lists. A data frame provides a tabular structure whose columns can have different types while representing the same rows.
For this exercise, orders["amount"] returns a one-column data frame, while orders[["amount"]] extracts that column. A computed column name belongs inside [[...]]; using $name literally asks for the element called name. R's extraction documentation explains these operators and their matching behavior.
Explain the consequence, not just the notation. A helper expecting a numeric vector may behave differently if you accidentally pass it a data frame. When a subset must retain its tabular shape, use an explicit drop = FALSE rather than depending on how many columns happen to be selected today.
2. What is the right way to handle missing amounts?
First ask what missing means. An unavailable amount is not evidence of a zero-value order. In this exercise, an unusable row is excluded from the numerical summary and returned in a separate list of row positions for investigation.
The aggregate methods have missing-value behavior that depends on the interface and grouping fields. The official aggregate reference describes omitted grouping values and the formula method's handling of missing observations. Make the filtering decision explicit when the business rule requires an auditable exclusion count.
A useful answer also describes the report label. The output represents revenue from usable rows, not necessarily complete regional revenue. If a region has ten orders with unknown amounts, a total calculated from its other orders should not be presented as a fully reconciled result. The consumer should see the data-quality exception alongside the summary.
3. Can you write a small, testable base R function?
Here is the full exercise implementation. It accepts a data frame with character IDs and regions and a plain integer or double amount column. Values may be missing, but non-missing amounts must be finite and between zero and one million. It trims surrounding region whitespace without changing the caller's input.
summarize_orders <- function(x) {
required <- c("order_id", "region", "amount")
if (!is.data.frame(x) || anyDuplicated(names(x)) ||
!all(required %in% names(x))) {
stop("Expected unique columns: order_id, region, amount")
}
if (!is.character(x$order_id) || !is.character(x$region) ||
!(is.integer(x$amount) || is.double(x$amount)) || is.object(x$amount) ||
!is.null(dim(x$amount))) {
stop("Invalid column types")
}
if (anyNA(x$order_id) || any(!nzchar(trimws(x$order_id))) ||
anyDuplicated(x$order_id)) {
stop("Order IDs must be present and unique")
}
if (any(!is.na(x$amount) &
(!is.finite(x$amount) | x$amount < 0 | x$amount > 1000000))) {
stop("Non-missing amounts must be finite and within range")
}
region <- trimws(x$region)
usable <- !is.na(region) & nzchar(region) & is.finite(x$amount)
empty <- data.frame(region = character(), revenue = double(),
orders = integer())
if (!any(usable)) {
return(list(summary = empty, excluded_rows = which(!usable)))
}
out <- aggregate(
list(revenue = x$amount[usable], orders = rep(1L, sum(usable))),
by = list(region = region[usable]), FUN = sum
)
out <- out[order(out$region), , drop = FALSE]
rownames(out) <- NULL
list(summary = out, excluded_rows = which(!usable))
}This is a reporting helper, not a currency ledger or a complete file-ingestion service. R doubles use floating-point arithmetic. For a financial system, choose a representation and reconciliation policy appropriate to its accuracy requirements; do not assume this small example establishes that policy.
The identity check is intentionally strict. If duplicate rows are legitimate updates, the specification needs an update-order rule and a deduplication step. Simply removing duplicates by whichever row appears first would invent a business rule that the function was never given. Data engineering interviews reward this restraint too, and a data engineer interview guide points out that candidates who ask what a duplicate means score above those who guess.
4. What should the fixture output be?
Use the following fictional data. Amounts are shown as ordinary numbers for readability, and every row has a distinct order ID.
| Row | Region | Amount | Treatment |
|---|---|---|---|
| 1 | North | 10 | Included |
| 2 | North | Missing | Excluded and reported |
| 3 | North | 20 | Included |
| 4 | South | 5 | Included |
| 5 | Blank | 7 | Excluded and reported |
| 6 | South | 0 | Included as a genuine zero-value order |
The result should contain North with revenue 30 and two usable orders, and South with revenue 5 and two usable orders. The excluded row positions are 2 and 5. Keeping the zero-value order distinguishes a known zero from a missing amount.
Then vary the data. A zero-row data frame should return a correctly shaped empty summary. An all-missing amount column should exclude every row without fabricating regional totals. A negative amount, infinity or repeated order ID should fail under this contract. If the business supports refunds as negative values, change the contract and tests together instead of quietly relaxing validation.
5. Why can a join inflate the answer?
Suppose a region lookup has two rows for North. Joining two North orders to that lookup can produce four rows, doubling the revenue if the result is summed without understanding the relationship. Base R's merge documentation states that multiple matches contribute all matching combinations.
Before joining, define the expected relationship: one lookup row per region, one row per region and effective date, or something else. If the lookup is historical, choose the row applicable to the order date. Do not use an arbitrary duplicate-removal step to conceal a many-to-many relationship.
Test the join with a deliberately duplicated lookup key and compare both row counts and totals. Also include an order whose region has no lookup entry. Decide whether it should remain in the result, be excluded with a reason, or fail the pipeline. The join type is a business decision as well as an R argument. If your role also uses SQL, practise spotting the same duplicated-key problem with SQL interview questions on joins and cardinality.
6. When would you use vectorization, lapply or a loop?
Start with a clear operation and an expected output shape. In this function, the usability mask applies the same checks across a vector of rows. Aggregation then summarizes each group. There is no need to build the output by repeatedly appending one row at a time.
For a list of independently loaded files, lapply can express the same transformation for each element. A loop can be clearer when later steps depend on an earlier result, when a controlled retry is needed or when you need explicit progress handling. Avoid claiming that every loop is inherently wrong or that a more compact expression must be faster.
If performance matters, profile a representative input and compare equivalent outputs. Record the input size, runtime and memory context. Rewriting a small readable loop without measuring the real bottleneck can add complexity while leaving file I/O or database latency unchanged.
7. How would you make the analysis reproducible?
Keep the raw input separate from cleaned data, record the function and dependency versions, and retain the assumptions that produced the report. A deterministic transformation should return the same summary when rows are reordered, while its excluded row positions appropriately refer to the new input order.
Random sampling adds another decision. Set and record the seed when a reproducible sample is required, and record the sampling method and eligible population as well. A seed alone does not document how the sample was constructed. R's random-number generation documentation describes the available generator controls.
For this guide, the fixture checks include reordering rows, validating the empty output and verifying that the input object remains unchanged. Those checks demonstrate the helper's local behavior. They do not show that a real export was complete or that an upstream system supplied accurate amounts.
8. How would you explain an unexpected result to a stakeholder?
Begin with what the report actually measures. An example explanation is: “North has 30 units of recorded revenue across two usable orders. Another North order has no amount, so this is not yet a reconciled total. I have retained the excluded row for correction.”
That explanation is more useful than saying that R dropped an NA. It identifies the scope, the uncertainty and the next action. If a join doubled the result, show the smallest fixture that reproduces the duplication and explain the relationship that must be corrected. Interviewers look for clear explanations even in technical rounds; a data scientist interview questions guide observes that the SQL round of a data science loop also probes how you would communicate the result.
For interview practice, ask a partner to introduce one change: regions become factors, order IDs are only unique within a store, or a file contains refunds. Explain the necessary contract change before modifying the function. Use PhantomCodeAI alongside hands-on R practice to rehearse those decisions and their tests in your own words.