What this quiz covers
This quiz focuses on Vectorized Operations, giving you a quick way to practice the rules, question types, and explanations that matter most for R Programming.
A programmer wants to replace only values outside the inclusive range from zero through three with NA:
x <- c(-2, 1, 4)
Which statement performs the intended elementwise replacement?
x[(x < 0) | (x > 3)] <- NAx[(x < 0) & (x > 3)] <- NAx[any((x < 0) | (x > 3))] <- NAx[which((x >= 0) & (x <= 3))] <- NAR Programming Quiz
Practice Vectorized Operations in R Programming with focused quiz questions that help you check what you know, review explanations, and build confidence with test-style prompts.
This quiz focuses on Vectorized Operations, giving you a quick way to practice the rules, question types, and explanations that matter most for R Programming.
Try each quiz question before looking at the correct answer. Use the explanations to review missed ideas, then come back to similar questions until the pattern feels familiar.
A programmer wants to replace only values outside the inclusive range from zero through three with NA:
x <- c(-2, 1, 4)
Which statement performs the intended elementwise replacement?
x[(x < 0) | (x > 3)] <- NA (correct answer)x[(x < 0) & (x > 3)] <- NAx[any((x < 0) | (x > 3))] <- NAx[which((x >= 0) & (x <= 3))] <- NAx <- c(-2, 1, 4), you want to flag elements where x < 0 or x > 3 — either condition alone is enough to disqualify a value. That means the pipe operator | (elementwise OR) is correct. x[(x < 0) | (x > 3)] <- NA evaluates both conditions across every element and returns TRUE for -2 (too low) and 4 (too high), replacing them with NA while leaving 1 untouched. This makes A the right answer.
B fails because it uses & (AND), which requires both conditions to be true simultaneously. No number can be both less than 0 and greater than 3 at the same time, so this logical expression is always FALSE — nothing gets replaced.
C uses any(...), which collapses the entire logical vector into a single TRUE or FALSE. If any out-of-range element exists, any(...) returns TRUE, and R interprets that as index 1, replacing only x[1] — not the intended elements.
D inverts the logic entirely. (x >= 0) & (x <= 3) selects values inside the valid range, so this replaces the good value (1) with NA instead of the outliers.
As a study tip: whenever a question asks you to flag values outside a range, reach for | with strict inequalities, and remember that any() and all() reduce vectors to scalars — they don't belong inside element-targeting brackets.A programmer uses this loop:
out <- numeric(length(x))
for (i in seq_along(x)) out[i] <- log(x[i])
Assume every element of x is positive. Which statement about replacing the loop is accurate?
log(sum(x)) returns the same elements because logarithms distribute over vector additionlapply(x, log) returns the same numeric vector and is guaranteed to outperform log(x)log(x) returns the same numeric elements and usually avoids interpreted element-by-element iteration (correct answer)sum(log(x)) returns the same vector but performs the reduction after transformationlog() operate on entire vectors internally using compiled C code, bypassing the slow interpreted loop that processes one element at a time.
The loop in the question applies log() to each element individually and stores results in out. Option C is correct because writing log(x) does exactly the same thing — it returns a numeric vector of the same length with log applied element-wise — but it delegates that iteration to optimized compiled code rather than R's interpreter. This is almost always faster and is idiomatic R style.
Option A is mathematically wrong. Logarithms do not distribute over addition — log(a + b) ≠ log(a) + log(b). So log(sum(x)) collapses the vector to a single scalar first, giving you an entirely different result.
Option B contains two errors. First, lapply(x, log) returns a list, not a numeric vector, so it doesn't match the loop's output type without an additional unlist(). Second, claiming it is guaranteed to outperform log(x) is false — lapply still iterates element-by-element in R's interpreter and is generally slower than fully vectorized functions.
Option D is wrong because sum(log(x)) applies log correctly but then reduces everything to a single number (the sum), not the full vector the loop produces.
As a study tip, always distinguish between functions that transform vectors element-wise (log(x), sqrt(x)) and those that reduce them to a scalar (sum(), mean()). Mixing them up is a frequent trap in R exam questions.A nested loop creates z so that each entry is the sum of one element from each vector:
x <- 1:2
y <- c(10, 20, 30)
z <- matrix(0, nrow = length(x), ncol = length(y))
for (i in seq_along(x)) for (j in seq_along(y)) z[i, j] <- x[i] + y[j]
Which expression directly constructs the same matrix?
x + y, relying on recycling to generate all pairwise combinationsx + t(y), using transposition to force every pairwise combinationcbind(x, y), binding corresponding values before applying additionouter(x, y, "+"), applying addition to every cross-vector pair (correct answer)outer(x, y, "+") does exactly this: it applies the "+" function to every possible (x[i], y[j]) pair, producing a matrix where row i and column j holds x[i] + y[j]. With x = 1:2 and y = c(10, 20, 30), the result is a 2×3 matrix matching the loop output precisely. D is correct.
A is tempting but wrong. x + y uses R's recycling rules, which cycle the shorter vector element-by-element through the longer one — it produces a single vector of length 3, not all pairwise combinations. Recycling fills gaps sequentially, not exhaustively.
B misleads with transposition. t(y) converts y into a 1×3 matrix, but adding a length-2 vector to a 1×3 matrix still triggers recycling, not a true 2×3 outer product. The result won't reliably match the loop.
C is unrelated. cbind(x, y) binds x and y as columns of a matrix — it doesn't perform addition at all, just combines the raw values side by side.
Study tip: Whenever a nested loop fills a matrix by combining every element of one vector with every element of another, think outer() immediately. It's R's dedicated tool for pairwise operations across two vectors, and it's far more readable than a double loop.The following loop stores a running total that begins at five:
x <- c(2, -1, 4)
total <- 5
out <- numeric(length(x))
for (i in seq_along(x)) { total <- total + x[i]; out[i] <- total }
Which vectorized expression produces the same out?
cumsum(5 + x), which returns c(7, 11, 20)5 + sum(x), which returns the single value 10cumsum(c(5, x)), which returns c(5, 7, 6, 10)5 + cumsum(x), which returns c(7, 6, 10) (correct answer)x, the cumulative result grows step by step — that's the signature of cumsum().
The correct answer is D. cumsum(x) computes c(2, 1, 5) (the running totals of x alone), and adding 5 shifts every element up by the starting value: c(2+5, 1+5, 5+5) = c(7, 6, 10). This exactly mirrors what the loop produces at each iteration.
Here's why the other options miss the mark. A (cumsum(5 + x)) adds 5 to each element of x first, turning x into c(7, 4, 9), then accumulates those inflated values — producing c(7, 11, 20), which compounds the 5 repeatedly rather than applying it once. B (5 + sum(x)) collapses the entire vector into a single scalar 10, losing all intermediate steps entirely — it answers "what is the final total?" not "what are the running totals?" C (cumsum(c(5, x))) prepends 5 to x before accumulating, returning a four-element vector c(5, 7, 6, 10) — one element too long, and it includes the starting value itself as an output, which the loop does not.
A useful rule of thumb: when a loop builds up results at each step using a growing total, reach for cumsum(). The key question is where the offset is applied — outside cumsum() means it shifts the output once; inside means it compounds.The following loop updates x sequentially:
x <- c(1, 2, 3, 4)
for (i in 2:length(x)) x[i] <- x[i] + x[i - 1]
A programmer instead runs:
x <- c(1, 2, 3, 4)
x[-1] <- x[-1] + x[-length(x)]
How do the final vectors compare?
c(1, 3, 6, 10) because assignment proceeds element by elementc(1, 3, 6, 10), while the vectorized assignment produces c(1, 3, 5, 7) (correct answer)c(1, 3, 5, 7), while the vectorized assignment produces c(1, 3, 6, 10)c(1, 3, 5, 7) because each uses adjacent original elementsx <- c(1, 2, 3, 4):
i=2: x[2] = 2 + x[1] = 2 + 1 = 3 → x is now c(1, 3, 3, 4)i=3: x[3] = 3 + x[2] = 3 + 3 = 6 → x is now c(1, 3, 6, 4)i=4: x[4] = 4 + x[3] = 4 + 6 = 10 → final: c(1, 3, 6, 10)x[-1] <- x[-1] + x[-length(x)], R evaluates the entire right-hand side before any assignment occurs. So x[-1] + x[-length(x)] is computed as c(2,3,4) + c(1,2,3) = c(3,5,7) using the original values throughout. The result is c(1, 3, 5, 7) — making B the correct answer.
A is wrong because vectorized assignment does not proceed element by element; the right side is fully evaluated first. C swaps the two results — the loop gives the cumulative sum, not the vectorized version. D is wrong because while the vectorized version does use original adjacent elements, the loop does not.
Study tip: In R, vectorized operations always snapshot the right-hand side before writing results — sequential dependency requires an explicit loop.Suppose the following vectors are defined:
a <- c(1, 5, 3)
b <- c(2, 4, 6)
A loop sets each output element to max(a[i], b[i]). Which expression reproduces that elementwise result?
max(a, b), producing the vector c(2, 5, 6)pmax(a, b), producing the vector c(2, 5, 6) (correct answer)pmin(a, b), producing the vector c(1, 4, 3)max(cbind(a, b)), producing the vector c(2, 5, 6)pmax() and pmin() are the parallel versions of max() and min(). For each index i, pmax(a, b) returns whichever value is larger between a[i] and b[i]. With a <- c(1, 5, 3) and b <- c(2, 4, 6), this compares position by position: max(1,2)=2, max(5,4)=5, max(3,6)=6, yielding c(2, 5, 6). This exactly mirrors what the loop does, making B the correct answer.
Choice A uses max(a, b), which treats both vectors as a single pool of values and returns one scalar — the global maximum, which would be 6, not a three-element vector. This is the most common trap on questions like this.
Choice C uses pmin(a, b), which is parallel but returns the minimum at each position — c(1, 4, 3) — the opposite of what's needed.
Choice D uses cbind(a, b) to form a matrix, then passes it to max(), which again collapses everything to a single scalar (6). The framing makes it look like a clever workaround, but max() never returns a vector regardless of its input structure.
A useful memory trick: the "p" prefix means "parallel" — pmax and pmin process vectors side-by-side. Whenever a question describes a loop comparing two vectors elementwise, reach for pmax or pmin first.Consider the following matrix operation:
m <- matrix(1:6, nrow = 2)
result <- m * c(10, 100)
Which expression represents result?
matrix(c(10, 20, 30, 40, 50, 60), nrow = 2)matrix(c(10, 20, 300, 400, 50, 60), nrow = 2)matrix(c(10, 200, 30, 400, 50, 600), nrow = 2) (correct answer)matrix(c(10, 30, 50, 200, 400, 600), nrow = 2)m <- matrix(1:6, nrow = 2), the matrix is stored internally as [1, 2, 3, 4, 5, 6] (filled column by column), giving you a 2×3 matrix where column 1 is [1,2], column 2 is [3,4], and column 3 is [5,6]. When you multiply by c(10, 100), R recycles that two-element vector across all six positions: positions 1,3,5 get multiplied by 10, and positions 2,4,6 get multiplied by 100. That yields [10, 200, 30, 400, 50, 600], which reshaped into a 2×3 matrix is exactly answer C.
Answer A is wrong because it implies multiplying every element by 10 — it ignores the second value (100) in the vector entirely. Answer B suggests the vector was applied column-by-column (first column ×10, second column ×100, third column left as-is), which isn't how R handles this. Answer D rearranges the correct values but places them in the wrong positions — perhaps from assuming row-major rather than column-major order.
The key study tip: whenever you see matrix arithmetic with a short vector in R, always think column-major recycling. Unroll the matrix top-to-bottom, left-to-right by columns, apply the repeating vector, then re-roll it back.Consider the following R expression:
c(2, 4, 6, 8, 10) + c(1, 3)
Which result is produced?
c(3, 7, 7, 11, 11), together with a warning about incompatible lengths (correct answer)c(3, 7, 7, 11, 11), with no warning because recycling is always silentc(3, 7, 9, 11, 13), together with a warning about incompatible lengthsc(1, 3) gets recycled to match length 5, becoming c(1, 3, 1, 3, 1). The addition then proceeds element-wise:
2+1, 4+3, 6+1, 8+3, 10+1=3,7,7,11,11
Crucially, because 5 is not a multiple of 2, R cannot recycle cleanly — and it issues a warning: "longer object length is not a multiple of shorter object length." That makes A correct.
B is wrong because recycling is not always silent. R only suppresses the warning when the longer vector's length is an exact multiple of the shorter one (e.g., length 4 recycling length 2). A non-multiple length always triggers the warning.
C describes a sequential addition — as if R simply added 1 to every element — which is not how recycling works. The result 3, 7, 9, 11, 13 would only appear if c(1, 3) were treated as c(1, 1, 1, 1, 1) or similar, which it isn't.
D is wrong because R does not throw an error for length mismatches between vectors — it recycles and warns, but always produces a result.
Study tip: Remember the recycling rule in two parts: (1) shorter vector repeats from position 1, and (2) a warning fires only when lengths are non-multiples. Both parts are frequently tested together.Given the following code, what is assigned to result?
x <- c(-2, 0, NA, 3)
result <- ifelse(x > 0, $x^2$, -x)
c(2, 0, NA, 9), because an unknown test produces an unknown result (correct answer)c(2, 0, 0, 9), because the false branch is used for an unknown testc(-4, 0, NA, 6), because both branches are combined element by elementc(4, 0, NA, 9), because both negative and positive values are squaredifelse() in R, the key concept to understand is that it operates element-wise across a vector, applying the condition, true branch, and false branch to each element independently — including how it handles NA values.
For x <- c(-2, 0, NA, 3), the condition x > 0 evaluates to c(FALSE, FALSE, NA, TRUE). Here's the element-wise breakdown: -2 > 0 is FALSE, so -(-2) = 2; 0 > 0 is FALSE, so -(0) = 0; NA > 0 is NA (unknown); and 3 > 0 is TRUE, so 3^2 = 9. This gives c(2, 0, NA, 9), confirming that A is correct — an unknown test (NA) produces an unknown result.
Choice B is tempting but wrong. It assumes NA in the condition defaults to the false branch, returning -NA = 0. R doesn't make that assumption; it preserves uncertainty. Choice C reflects a fundamental misunderstanding — ifelse() does not combine both branches mathematically. Each element gets either the true result or the false result, never both. Choice D incorrectly applies the true branch (‘x2) to negative values, squaring -2to get4, which would only happen if the condition for -2` were TRUE — it isn't.
A useful rule of thumb: NA is contagious in logical tests. Whenever a condition can't be evaluated, ifelse() returns NA for that position, regardless of what either branch would return.Consider these named vectors:
x <- c(a = 10, b = 20)
y <- c(b = 1, a = 2)
What does x + y return?
c(a = 12, b = 21), because matching names determine which elements are addedc(a = 11, b = 22), because elements are added by position and names come from x (correct answer)c(b = 11, a = 22), because elements are added by position and names come from yx + y: R adds element 1 of x (which is a = 10) to element 1 of y (which is b = 1), giving 11. Then it adds element 2 of x (b = 20) to element 2 of y (a = 2), giving 22. The result takes its names from x, producing c(a = 11, b = 22) — confirming that B is correct.
Choice A describes name-based matching, which would pair a with a and b with b, yielding c(a = 12, b = 21). This is a natural assumption, but it's not how vectorized arithmetic works in R — name matching behavior belongs to operations like merging or indexing, not +. Choice C gets the positional logic right but incorrectly assumes names come from the right-hand operand y; R always pulls names from the left side. Choice D is flatly wrong — R does not throw an error when name orderings differ, it simply ignores the name mismatch and proceeds positionally.
A useful rule of thumb: position drives the math, left operand drives the names. When you see named vector arithmetic on the exam, ask yourself where the names originate (always the left vector) and remember that order in memory — not name labels — determines which values get paired.