R Programming Quiz: Which And Logical Subsetting
8 questions · exam conditions
0:00
Which And Logical SubsettingQuestion 1 of 8

The following matrix is created in R:

m <- matrix(c(3, 8, 1, 8, 5, 2), nrow = 2) which(m >= 5, arr.ind = TRUE)

In what order are the (row, column) coordinate pairs returned?

(2, 1), (2, 2), (1, 3)
(1, 3), (2, 1), (2, 2)
(2, 1), (1, 2), (1, 3)
(1, 2), (2, 2), (2, 3)
← Back to quizzes

R Programming Quiz

R Programming Quiz: Which And Logical Subsetting

Practice Which And Logical Subsetting in R Programming with focused quiz questions that help you check what you know, review explanations, and build confidence with test-style prompts.

What this quiz covers

This quiz focuses on Which And Logical Subsetting, giving you a quick way to practice the rules, question types, and explanations that matter most for R Programming.

How to use this quiz

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.

All questions

Question 1

The following matrix is created in R:

m <- matrix(c(3, 8, 1, 8, 5, 2), nrow = 2) which(m >= 5, arr.ind = TRUE)

In what order are the (row, column) coordinate pairs returned?

  1. (2, 1), (2, 2), (1, 3) (correct answer)
  2. (1, 3), (2, 1), (2, 2)
  3. (2, 1), (1, 2), (1, 3)
  4. (1, 2), (2, 2), (2, 3)
Explanation: When working with which() in R, the critical concept to internalize is that R stores matrices column by column (column-major order). This means R scans through the first column entirely before moving to the second, and so on — and which() follows that same traversal order when identifying elements that meet a condition. Here's how m actually looks after matrix(c(3, 8, 1, 8, 5, 2), nrow = 2) is created:
     [,1] [,2] [,3]
[1,]   3    1    5
[2,]   8    8    2
Now apply m >= 5: the value 8 at position (2,1) qualifies, then 8 at (2,2) qualifies, then 5 at (1,3) qualifies. So the results are returned as (2,1), (2,2), (1,3) — which is answer A. Answer B lists the same coordinates but in the wrong order (1,3), (2,1), (2,2), as if R scanned by value magnitude rather than position. Answer C includes (1,2) for the value 1, which doesn't satisfy >= 5 — this is simply a misread of the matrix layout. Answer D references position (2,3) where the value is 2, which also fails the condition, indicating a confusion about how the input vector maps to the matrix. Your study tip: always reconstruct the matrix mentally before applying any operation. Fill values down each column first. When arr.ind = TRUE is used, remember that results are returned in the same column-major scan order R uses internally — row index alone does not determine position priority.

Question 2

Consider the following named vector and operations:

v <- c(a = 8, b = 3, c = 8, d = 5) idx <- which(v == max(v)) v[idx + 1]

What is the result?

  1. c(a = 8, c = 8)
  2. c(c = 8, NA = NA)
  3. c(b = 3, d = 5) (correct answer)
  4. c(b = 3, c = 8)
Explanation: When working with named vectors in R, you need to track both the values and the positional indices separately — they're not the same thing, and this question tests exactly that distinction. Start by tracing the code step by step. max(v) returns 8. Then which(v == max(v)) finds the positions where v equals 8. Since a is at position 1 and c is at position 3, idx becomes the integer vector c(a = 1, c = 3). Now the critical part: v[idx + 1] adds 1 to each positional index, giving positions 2 and 4. Position 2 holds b = 3 and position 4 holds d = 5. So the result is c(b = 3, d = 5), confirming C is correct. Choice A is the most tempting trap — it returns the elements where the max occurs, not the elements after them. This is what you'd get from v[idx], not v[idx + 1]. Choice D is a partial trap: b = 3 is correct (position 2), but c = 8 confuses the name "c" as a vector element with position 3+1=4, ignoring that +1 already shifts past c. Choice B suggests only one valid result with an NA, which would only happen if one shifted index exceeded the vector's length — but both positions 2 and 4 are valid here. As a study habit, always mentally separate names, values, and positions in named vectors. When you see arithmetic on index results from which(), you're manipulating positions, not names or values.

Question 3

What does the following expression return?

x <- c(2, 3, 4, 3, 5) which(!x %in% c(2, 4) & seq_along(x) > 1)

  1. c(1, 3)
  2. c(2, 4, 5) (correct answer)
  3. c(2, 3, 4, 5)
  4. c(1, 2, 4, 5)
Explanation: When you see chained logical conditions in R, break them into individual components first, then combine. This question tests your understanding of %in%, seq_along(), logical negation, and the which() function. Start by evaluating each piece for x <- c(2, 3, 4, 3, 5). The expression !x %in% c(2, 4) asks: which elements of x are not in {2, 4}? Elements at positions 1, 2, 3, 4, 5 are 2, 3, 4, 3, 5 — so !x %in% c(2, 4) yields c(FALSE, TRUE, FALSE, TRUE, TRUE). Next, seq_along(x) > 1 generates indices 1:5 and checks which exceed 1, giving c(FALSE, TRUE, TRUE, TRUE, TRUE). Combining with &: position 1 is FALSE (fails both), position 2 is TRUE & TRUE = TRUE, position 3 is FALSE & TRUE = FALSE, position 4 is TRUE & TRUE = TRUE, position 5 is TRUE & TRUE = TRUE. So which(...) returns the positions where the combined condition is TRUE: c(2, 4, 5), confirming B. Choice A, c(1, 3), mistakenly returns positions where x %in% c(2, 4) — the un-negated version — ignoring the index filter entirely. Choice C, c(2, 3, 4, 5), drops the seq_along > 1 filter and only applies the !x %in% condition, forgetting that position 3 (value 4) is excluded. Choice D, c(1, 2, 4, 5), ignores the negation on %in%, treating values 2 and 4 as matches rather than exclusions. When you see !x %in% vector, mentally read it as "elements NOT found in that vector" — the ! negates the entire %in% result, not just x.

Question 4

Consider the following R code:

x <- c(4, NA, 7, 2, NA, 7) x[which(x >= 4 & x != 7)]

What is returned?

  1. [1] 4 (correct answer)
  2. [1] 4 NA NA
  3. [1] 4 7 7
  4. [1] 1
Explanation: When working with subsetting in R, it's crucial to distinguish between logical subsetting with [ ] and index-based subsetting with which() — they handle NA values very differently. Here's what the code actually does. First, the condition x >= 4 & x != 7 is evaluated across the vector c(4, NA, 7, 2, NA, 7). This produces c(TRUE, NA, FALSE, FALSE, NA, FALSE) — only position 1 (value 4) returns TRUE, because 4 >= 4 and 4 != 7. The two NA positions return NA (not TRUE), and 7 and 2 return FALSE. Then which() steps in: it returns only the integer indices where the condition is strictly TRUE, completely ignoring NA values. So which(...) returns 1. Finally, x[1] gives you 4. Answer A is correct. Answer B (4 NA NA) is the trap most students fall into. This is what you'd get if you used logical subsetting directlyx[x >= 4 & x != 7] — because that approach preserves NA positions as NA in the output. which() eliminates that behavior entirely. Answer C (4 7 7) reflects a misreading of the condition: if you only applied x >= 4 without x != 7, the 7s would pass through — but they're explicitly excluded. Answer D (1) confuses the index returned by which() with the final output; which() gives you position 1, but you then use that to extract the value at that position. The key takeaway: which() converts a logical vector to integer indices and drops NAs, while direct logical subsetting keeps them. Watch for this distinction whenever NA values are present in a subsetting question.

Question 5

What does the following R code return?

x <- c(2, 5, 8, 11, 14, 17) keep <- x > 6 & c(TRUE, FALSE) x[which(keep)]

  1. [1] 8 11 14 17
  2. [1] 8 14 (correct answer)
  3. [1] 11 17
  4. [1] 2 8 14
Explanation: When you see R code combining logical vectors with subsetting, slow down and trace through each operation separately before combining them. Start with x > 6, which compares each element of x <- c(2, 5, 8, 11, 14, 17) against 6, producing c(FALSE, FALSE, TRUE, TRUE, TRUE, TRUE). Now notice that the second operand in the & is c(TRUE, FALSE) — only two elements long. R recycles this shorter vector, repeating it to match the length of the longer one, giving c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE). The element-wise & then produces: FALSE&TRUE, FALSE&FALSE, TRUE&TRUE, TRUE&FALSE, TRUE&TRUE, TRUE&FALSE, which simplifies to c(FALSE, FALSE, TRUE, FALSE, TRUE, FALSE). These are positions 3 and 5, corresponding to values 8 and 14. Finally, which(keep) extracts those indices, and x[which(keep)] returns c(8, 14) — confirming B is correct. Choice A (8 11 14 17) ignores recycling entirely and just takes all values greater than 6. Choice C (11 17) picks positions 4 and 6, which would result from recycling starting with FALSE instead of TRUE — a common off-by-one error when misreading the recycled pattern. Choice D (2 8 14) incorrectly assumes the TRUE in the recycled vector always activates the first element, mixing up the indexing logic. Your study tip: whenever you see a short vector used in a logical operation, immediately write out the recycled version before evaluating anything else — recycling mistakes are the number-one trap in R vector logic questions.

Question 6

Consider the following code:

x <- c(NA, 2, 5, NA, 2) i <- which(is.na(x) | x %in% c(2, 4))

What is the value of i?

  1. c(2, 5)
  2. c(1, 4)
  3. c(1, 2, 3, 4, 5)
  4. c(1, 2, 4, 5) (correct answer)
Explanation: When working with logical indexing in R, the key is to trace through each operation layer by layer. Here, two functions work together: is.na() checks for missing values, %in% checks for membership, and which() converts a logical vector into the positions where TRUE appears. Start with x <- c(NA, 2, 5, NA, 2). Evaluating is.na(x) gives c(TRUE, FALSE, FALSE, TRUE, FALSE) — positions 1 and 4 are NA. Next, x %in% c(2, 4) gives c(FALSE, TRUE, FALSE, FALSE, TRUE) — positions 2 and 5 equal 2 (no element equals 4). Combining with | (OR), you get c(TRUE, TRUE, FALSE, TRUE, TRUE). Applying which() returns the indices where this is TRUE: positions 1, 2, 4, 5, making D the correct answer. A (c(2, 5)) is tempting if you confuse the values at the matching positions with the positions themselves — which() always returns indices, never values. B (c(1, 4)) is what you'd get if you only ran which(is.na(x)) and forgot the %in% condition entirely. C (c(1, 2, 3, 4, 5)) would require every element to match, but position 3 (value 5) is neither NA nor in c(2, 4), so it evaluates to FALSE. A reliable strategy: when you see which(), mentally remind yourself "this gives me where, not what." Always evaluate each sub-expression separately before combining them with logical operators.

Question 7

Consider this data frame operation:

df <- data.frame( score = c(70, NA, 85, 90, 85), team = c("A", "A", "B", "A", "B") ) rows <- which((df$score >= 85 & df$team == "B") | df$score == 90)

What is stored in rows?

  1. c(2, 3, 4, 5)
  2. c(3, 5)
  3. c(3, 4, 5) (correct answer)
  4. c(4)
Explanation: When working with which() and logical conditions in R, your job is to carefully evaluate each row against the full condition — paying special attention to NA values and operator precedence. The condition is (df$score >= 85 & df$team == "B") | df$score == 90. Let's trace through each row:
  • Row 1: score=70, team="A" → (70>=85 & "A"=="B") | 70==90 → (FALSE & FALSE) | FALSE → FALSE
  • Row 2: score=NA, team="A" → (NA & FALSE) | FALSE → FALSE | FALSE → FALSE
  • Row 3: score=85, team="B" → (85>=85 & "B"=="B") | 85==90 → (TRUE & TRUE) | FALSE → TRUE
  • Row 4: score=90, team="A" → (90>=85 & "A"=="B") | 90==90 → (TRUE & FALSE) | TRUE → TRUE
  • Row 5: score=85, team="B" → same as row 3 → TRUE
which() returns the indices where the condition is TRUE, giving c(3, 4, 5) — confirming C is correct. A (c(2, 3, 4, 5)) is wrong because row 2 has NA in score. Crucially, which() silently drops NA results rather than including them, so row 2 is excluded. B (c(3, 5)) forgets row 4 — the | df$score == 90 clause independently makes row 4 TRUE regardless of the team condition. D (c(4)) ignores rows 3 and 5, missing that team "B" with score ≥ 85 satisfies the left side of the |. Remember: which() is your friend with NA — unlike bracket subsetting, it never returns NA indices. Always trace compound conditions row-by-row, and remember | only requires one side to be TRUE.

Question 8

Consider the following named vector computation:

x <- c(alpha = 6, beta = 2, gamma = 6, delta = 9) i <- which(x == 6) c(indices = sum(i), values = sum(x[i]), labels = length(names(i)))

What is returned?

  1. c(indices = 2, values = 12, labels = 2)
  2. c(indices = 4, values = 6, labels = 2)
  3. c(indices = 4, values = 12, labels = 0)
  4. c(indices = 4, values = 12, labels = 2) (correct answer)
Explanation: When working with which() on named vectors in R, you need to track three things simultaneously: what indices are returned, what names those indices carry, and what values they point to. Starting with x <- c(alpha = 6, beta = 2, gamma = 6, delta = 9), calling which(x == 6) finds positions where the condition is true. That's positions 1 and 3 (alpha and gamma). Crucially, which() preserves the names of the original vector, so i becomes c(alpha = 1, gamma = 3) — a named integer vector with values 1 and 3. Now evaluate each piece of the final c() call: sum(i) adds the index values 1 + 3 = 4. sum(x[i]) subsets x at positions 1 and 3, giving 6 + 6 = 12. length(names(i)) asks how many names i has — since which() preserved "alpha" and "gamma", names(i) is a character vector of length 2. This confirms D is correct. A is wrong because it uses sum(i) = 2, confusing the count of matching elements with the sum of their positions. B is wrong on two counts — it correctly gets sum(i) = 4 but then computes sum(x[i]) = 6, as if only one element matched. C gets sum(i) and sum(x[i]) right but sets labels = 0, reflecting the mistaken belief that which() strips names from its output. A good rule of thumb: which() in R behaves like a named subsetting operation — it inherits the names of whatever logical vector it evaluated, so the result is never anonymous.