- Which rows are returned by
results %>% filter(score >= 80, active)? Answer: Only row a is returned.
When using filter() in R, every condition you pass must evaluate to TRUE for a row to be kept — and critically, NA is never treated as TRUE. This question tests whether you understand how filter() handles NA values in both numeric comparisons and logical columns.
Walk through each row against the two conditions score >= 80 and active:
- Row a: 80 >= 80 is TRUE, active is TRUE → both pass, row kept ✓
- Row b: NA >= 80 is NA, not TRUE → fails first condition, row dropped
- Row c: 75 >= 80 is FALSE → fails immediately, row dropped
- Row d: 90 >= 80 is TRUE, but active is NA, not TRUE → fails second condition, row dropped
Only row a survives both filters, making A the correct answer.
Answer B is wrong because row d has active = NA, and filter() drops any row where a condition evaluates to NA rather than TRUE. Answer C is wrong because row b has score = NA, so NA >= 80 produces NA, not TRUE — filter() discards it. Answer D combines both of these misconceptions, treating NA as if it passes a condition.
A reliable rule to remember: in filter(), a row is only kept when conditions return exactly TRUE — both FALSE and NA cause the row to be dropped. Whenever you see NA values in the data, immediately ask yourself how each condition will evaluate for that row.
- Which rows are returned by
readings %>% filter(is.na(temp) | temp < 0, humidity <= 80)? Answer: Row a only
When dplyr::filter() receives multiple comma-separated conditions, it treats the comma as a logical AND — but this interacts subtly with operator precedence when you mix | and , in the same call.
The expression filter(is.na(temp) | temp < 0, humidity <= 80) translates to: keep rows where (is.na(temp) | temp < 0) AND (humidity <= 80). Let's evaluate each row against both conditions:
- Row a: is.na(temp) is TRUE, so the first condition passes. humidity = 70 ≤ 80 is TRUE. Both conditions met → kept.
- Row b: temp = -2 < 0 is TRUE, so the first condition passes. But humidity = NA ≤ 80 evaluates to NA, not TRUE. Filter drops rows that aren't clearly TRUE → excluded.
- Row c: temp = 5, so neither is.na(temp) nor temp < 0 is TRUE. First condition fails → excluded.
- Row d: temp = -1 < 0 is TRUE. But humidity = 85 ≤ 80 is FALSE. Second condition fails → excluded.
Only row a survives, confirming A is correct.
Choice B ignores that row b's NA humidity causes humidity <= 80 to return NA, not TRUE. Choice C additionally mishandles row d, where humidity clearly exceeds 80. Choice D correctly excludes row b for the right reason but wrongly includes row d despite humidity = 85 failing the cutoff.
The key study tip: always remember that filter() silently drops NA results — a condition returning NA behaves like FALSE, not TRUE.
- Which accounts are returned by
accounts %>% filter(!(overdue | flagged))? Answer: Account a only
When working with filter() and logical operators in R, you need to trace the Boolean logic carefully for each row — and pay special attention to how R handles NA values, because they behave differently than you might expect.
The expression !(overdue | flagged) first evaluates overdue | flagged for each account, then negates the result. Only rows where this final expression evaluates to TRUE are kept. For account a (FALSE | FALSE = FALSE, negated → TRUE), it passes the filter. For account b (FALSE | TRUE = TRUE, negated → FALSE), it's excluded. For account c (TRUE | FALSE = TRUE, negated → FALSE), it's excluded. For account d (FALSE | NA = NA, negated → NA), R cannot determine the result — and filter() drops rows where the condition evaluates to NA, not TRUE. So only account a is returned, making C the correct answer.
Choice A is the most tempting trap. Account d seems "not overdue and not flagged" because its overdue is FALSE — but because flagged is NA, R can't confirm whether flagged is FALSE, so the whole OR expression becomes NA, and filter() silently drops it. Choice B returns the accounts that fail the filter (overdue or flagged), essentially the opposite logic. Choice D ignores the negation entirely and returns nearly everyone.
The key study tip: **filter() keeps only TRUE, never NA or FALSE.** Whenever you see NA in a filter condition, assume that row will be dropped unless you explicitly handle it with is.na().
- Which rows are returned by
labels %>% filter(code != "x")? Answer: Rows 2 and 3 only
When filtering with != in R, the key concept to understand is how R handles NA values in logical comparisons. NA represents "not available" — an unknown value — and R treats any comparison involving NA as itself NA, not TRUE or FALSE. Since filter() only keeps rows where the condition evaluates to TRUE, rows where the condition returns NA are silently dropped.
Walking through each row: row 1 has code == "x", so "x" != "x" is FALSE — dropped. Row 2 has code == "X", and since R is case-sensitive by default, "X" != "x" is TRUE — kept. Row 3 has code == "y", so "y" != "x" is TRUE — kept. Row 4 has code == NA, so NA != "x" returns NA, not TRUE — dropped. This gives you rows 2 and 3 only, confirming C is correct.
Choice A is the most common trap: students assume NA simply means "not x," so they include row 4. But unknown ≠ "not x" in R's logic. Choice B incorrectly drops row 2, perhaps from forgetting that R's string comparison is case-sensitive, treating "X" and "x" as equal. Choice D would mean the filter kept row 1, which would require "x" != "x" to be TRUE — clearly wrong.
As a study tip, always remember: **NA is contagious in comparisons**. If you want to include or explicitly handle NA rows, use is.na() alongside your filter condition.
- Which row IDs are returned by
checks %>% filter(x > 5 & group == "A" | approved)? Answer: Rows 1 and 3
When you see a logical expression combining & and | in R's filter(), your first instinct should be to check operator precedence. In R (and most programming languages), & binds more tightly than |, so the expression x > 5 & group == "A" | approved is evaluated as (x > 5 & group == "A") | approved — not left-to-right as it might appear.
With that grouping clarified, evaluate each row: Row 1 has x = 6 > 5 ✓ and group == "A" ✓, so the left side is TRUE, making the whole expression TRUE. Row 2 has x = 6 > 5 ✓ but group == "B" ✗, so the left side is FALSE, and approved = FALSE means the right side is also FALSE — Row 2 is excluded. Row 3 has x = 2, so the left side is FALSE, but approved = TRUE saves it — Row 3 is included. Row 4 has a FALSE left side and approved = FALSE, so it's excluded. This gives you Rows 1 and 3, confirming B is correct.
Choice A is wrong because it ignores that approved = TRUE independently satisfies the | condition for Row 3. Choice C incorrectly includes Row 2 — a common mistake if you read the expression left-to-right without applying precedence, grouping it as (x > 5) & (group == "A" | approved) instead. Choice D drops Row 1 entirely, which clearly satisfies x > 5 & group == "A".
The study tip: whenever you mix & and |, mentally add parentheses around & expressions first. When in doubt, use explicit parentheses in your own code to avoid bugs.
- Which expression correctly filters rows whose
value is within the row-specific bounds, including both endpoints? Answer: filter(measurements, lower <= value, value <= upper)
When filtering rows based on row-specific bounds, you need to think carefully about two things: which comparison operators to use (strict vs. inclusive), and whether conditions should be combined with AND vs. OR logic.
The phrase "within bounds, including both endpoints" signals that you need inclusive inequalities — <= and >= — not strict ones. A value exactly equal to a bound still qualifies. To require both conditions simultaneously, you chain them as separate arguments in filter(), which applies AND logic. Let's verify with the data: Row 1 has lower=5, value=5, upper=8 → 5 <= 5 ✓ and 5 <= 8 ✓; Row 2 has lower=2, value=4, upper=4 → 2 <= 4 ✓ and 4 <= 4 ✓; Row 3 has lower=1, value=0, upper=3 → 1 <= 0 ✗, so it's excluded. Answer A correctly returns rows 1 and 2.
Answer B uses strict inequalities (<), which excludes values that sit exactly on a boundary. Row 1 would fail because value == lower == 5, and Row 2 would fail because value == upper == 4 — so it returns no rows, which is wrong.
Answer C reverses the comparisons entirely (value <= lower and value >= upper), which tests whether the value is outside the bounds, not inside them.
Answer D uses | (OR logic) instead of AND. This keeps a row if it satisfies either condition alone — far too permissive and logically incorrect for a "within bounds" test.
Remember: multiple arguments inside filter() always combine with AND, and "including endpoints" always means <=/>=. These two facts together uniquely identify answer A.
- Which task IDs are returned by
tasks %>% filter(status %in% c("open", "pending") & !(owner %in% c("Sam", "Lee")))? Answer: Tasks 2 and 4 only
When filtering with multiple conditions in R, you need to evaluate each row against both conditions independently before combining them. This question tests your understanding of %in% with & (AND logic) and, critically, how NA values behave inside %in%.
The filter requires two things to be true: status %in% c("open", "pending") AND !(owner %in% c("Sam", "Lee")). Work through each task row by row. Task 1: status is "open" ✓, but owner is "Sam" — so !(owner %in% c("Sam", "Lee")) is FALSE. Task 1 is excluded. Task 2: status is "pending" ✓, owner is "Jo" which is not in the exclusion list ✓ — Task 2 passes. Task 3: status is "closed" — fails the first condition immediately. Task 3 is excluded. Task 4: status is "open" ✓, owner is NA — here's the key insight. NA %in% c("Sam", "Lee") returns FALSE (not NA), because %in% compares NA against each element and finds no match, returning FALSE. So !(FALSE) is TRUE, meaning Task 4 passes. The answer is D — Tasks 2 and 4.
Answer A is wrong because Task 1 is excluded (Sam is in the disallowed owner list). Answer B is wrong because it misses Task 4 — the trap is assuming NA values automatically disqualify a row. Answer C is wrong because Task 3 fails the status filter entirely ("closed" is not "open" or "pending").
Remember: %in% is NA-safe and always returns TRUE or FALSE — unlike ==, which propagates NA. When you see NA in a filter, ask yourself whether you're using == or %in%.
- Which order IDs are returned by
orders %>% filter(region == "East", sales > 100 | priority == "high")? Answer: Orders 1 and 3 only
When filtering rows in R with multiple conditions, operator precedence is everything. The | (OR) operator binds more loosely than , (AND), but inside filter(), comma-separated conditions are evaluated as AND. So the expression region == "East", sales > 100 | priority == "high" means: region is East AND (sales > 100 OR priority is high). The parenthetical grouping happens automatically because | is evaluated before the comma acts as AND.
Now walk through each order. Order 1: East ✓, sales = 90 (not > 100), priority = "high" ✓ — the OR condition is satisfied, so this row passes. Order 2: West ✗ — immediately excluded since it fails the AND condition. Order 3: East ✓, sales = 110 ✓ — passes. Order 4: East ✓, sales = 80 and priority = "low" — both sides of the OR fail, so it's excluded. That leaves orders 1 and 3, making C correct.
Choice A incorrectly includes order 2, which is in the West region and therefore filtered out regardless of its other values. Choice B excludes order 1, likely from misreading the OR condition — because order 1's sales don't exceed 100, some students miss that its "high" priority still satisfies the OR clause. Choice D keeps only order 3, suggesting a misread of the entire condition as requiring all three criteria simultaneously.
Your study tip: whenever you see a filter() call mixing , and |, mentally add explicit parentheses around the | expression first — sales > 100 | priority == "high" — then apply AND with the remaining conditions. This prevents precedence mistakes on exam day.
- What values remain after
values %>% filter(x >= 0) %>% filter(x <= 10)? Answer: The values 0 and 10
When working with dplyr's filter(), the key things to remember are: conditions must be explicitly satisfied to pass through, and NA values are silently dropped — not kept, not flagged, just removed.
Here's the logic: starting with -1, 0, 10, 11, NA, the first filter x >= 0 removes -1 (fails the condition) and drops NA (because any comparison with NA returns NA, which filter() treats as FALSE). That leaves 0, 10, 11. The second filter x <= 10 then removes 11, leaving just 0 and 10 — making D the correct answer.
A is wrong because -1 fails the first filter (-1 >= 0 is FALSE), so it never makes it through the pipeline.
B is wrong because of the NA misconception — students often assume NA "slips through" since it's technically unknown. But filter() requires a condition to evaluate to TRUE; NA >= 0 returns NA, not TRUE, so NA is dropped.
C is wrong because 10 satisfies both conditions (10 >= 0 and 10 <= 10 are both TRUE), so there's no reason to exclude it. This answer likely results from misreading <= as <.
A reliable tip: whenever you see NA in a filter() question, assume it gets dropped unless the filter explicitly includes it using is.na(). This is one of the most common traps in R data-wrangling questions.
- Which employees are returned by
staff %>% group_by(department) %>% filter(salary > mean(salary))? Answer: Employees b and d
When you see group_by() followed by filter() in a dplyr pipeline, the key insight is that any summary function like mean() inside filter() is computed per group, not across the entire dataset. This is what makes grouped filtering powerful — and tricky.
Here, group_by(department) splits the data into two groups before filter() runs. Department A contains employees a (salary 40) and b (salary 60), so the group mean is 240+60=50. Only b earns above 50, so b is kept. Department B contains employees c (salary 80) and d (salary 100), giving a group mean of 280+100=90. Only d earns above 90, so d is kept. The result is employees b and d — confirming B is correct.
A (c and d) is wrong because it applies the filter correctly only within Department B, while mistakenly dropping b from Department A. C (b, c, and d) would be the result if you compared each salary against the global mean of all four employees (440+60+80+100=70), ignoring the grouping entirely. D (only d) would result from comparing against the global maximum or some other single-threshold logic rather than per-group means.
A reliable study habit: whenever you see group_by() in a pipeline, mentally tag every subsequent operation as happening within each group independently. Functions like mean(), sum(), and n() all respect that grouping boundary.