What this quiz covers
This quiz focuses on Separate And Unite, giving you a quick way to practice the rules, question types, and explanations that matter most for R Programming.
Consider this two-step transformation:
df <- tibble(code = c("A/01", "B/NA"))
df <- separate(df, code, c("prefix", "part"), sep = "/", convert = TRUE)
df <- unite(df, rebuilt, prefix, part, sep = "/")
What values does rebuilt contain?
c("A/01", "B/NA")c("A/1", "B/NA")c("A/1", "B")c("A/01", NA)R Programming Quiz
Practice Separate And Unite 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 Separate And Unite, 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.
Consider this two-step transformation:
df <- tibble(code = c("A/01", "B/NA"))
df <- separate(df, code, c("prefix", "part"), sep = "/", convert = TRUE)
df <- unite(df, rebuilt, prefix, part, sep = "/")
What values does rebuilt contain?
c("A/01", "B/NA")c("A/1", "B/NA") (correct answer)c("A/1", "B")c("A/01", NA)tidyr operations, trace each transformation step by step rather than reading the pipeline as a whole — the devil is in the intermediate state.
After separate() splits "A/01" and "B/NA" on "/", you get two columns: prefix = c("A", "B") and part = c("01", "B/NA"'s second piece, "NA"). The critical detail here is convert = TRUE. This argument automatically coerces the resulting character columns to appropriate types — so "01" becomes the integer 1 (dropping the leading zero), and "NA" becomes a true NA value (not the string "NA"). After conversion, part = c(1L, NA).
Then unite() collapses prefix and part back together with sep = "/". By default, unite() converts everything to character for pasting. Crucially, it also has a default behavior of na.rm = FALSE, meaning NA values are included as the string "NA" in the output. So "B" and NA become "B/NA", and "A" and 1 become "A/1". The correct answer is B: c("A/1", "B/NA").
Choice A is wrong because it assumes convert = TRUE had no effect — "01" would remain "01", but it gets parsed to integer 1. Choice C is wrong because it assumes unite() drops NA values silently, but it doesn't by default. Choice D is wrong because it assumes the whole row becomes NA when a piece is NA, which isn't how unite() works.
A good rule of thumb: convert = TRUE in separate() is a silent type-changer that surprises many students — always mentally simulate what type each piece becomes before predicting downstream behavior.A tibble has two rows with (first, middle, last) equal to ("Ada", NA, "Lovelace") and (NA, "Alan", "Turing"). What does this call place in full_name?
unite(df, full_name, first, middle, last, sep = " ", na.rm = TRUE)
c("Ada Lovelace", " Alan Turing")c("Ada NA Lovelace", "NA Alan Turing")c("Ada Lovelace", "Alan Turing") (correct answer)c("Ada Lovelace", NA)tidyr::unite(), the key parameter to focus on is na.rm. This argument controls whether NA values are silently dropped before concatenation — and understanding it is the entire puzzle here.
With na.rm = TRUE, any NA values in the selected columns are removed before the strings are joined with sep. For row one, first = "Ada", middle = NA, last = "Lovelace" — the NA is dropped, leaving "Ada" and "Lovelace" joined by a single space: "Ada Lovelace". For row two, first = NA, middle = "Alan", last = "Turing" — again the NA is dropped, yielding "Alan Turing". That makes C the correct answer.
Choice B reflects what happens when na.rm = FALSE (the default): NA is coerced to the string "NA" and included literally in the result. This is the most common trap here — forgetting that the default behavior does insert "NA" as text. Choice A is a variation of the same misconception, but instead imagines that NA produces an empty string, leaving a double space (e.g., "Ada Lovelace"). That's not how either setting works. Choice D suggests the entire output cell becomes NA when any input is NA, which would be the behavior of something like paste() with NA in base R under certain conditions — but unite() with na.rm = TRUE doesn't propagate NA to the result.
Your study tip: always check na.rm in unite() questions. The default is FALSE, meaning NA becomes the literal text "NA" — setting na.rm = TRUE cleanly removes it instead.The tibble df initially has columns id and label, where a label resembles "sales-senior". After running the code below, which set of columns is available for later operations?
separate(df, label, into = c("department", "level"), sep = "-", remove = FALSE)
label, department, and level onlyid, department, and level onlyid and label only, with label modifiedid, label, department, and level (correct answer)tidyr::separate(), the key parameter to focus on is remove. This argument controls whether the original column used for splitting is kept or discarded after the operation — and it's easy to overlook in a question stem.
By default, remove = TRUE, meaning separate() drops the source column once it's been split. However, the code here explicitly sets remove = FALSE, which tells R to retain the original label column alongside the newly created columns. Since df starts with id and label, and the function adds department and level as new columns while keeping label intact, the resulting tibble contains all four columns: id, label, department, and level. That makes D the correct answer.
A is wrong because it drops id, which separate() never touches — columns not involved in the split are always preserved. B reflects the default behavior where remove = TRUE would drop label, but the code explicitly overrides that default, so label survives. C is a common misconception — separate() doesn't modify the original column in place; it creates entirely new columns from the split, and if remove = FALSE, the original stays unchanged alongside them.
A reliable study tip: whenever you see separate() or unite() in a question, immediately locate the remove argument. If it's absent, assume the default (TRUE) drops the source column. If it's explicitly set to FALSE, that column survives. That single argument is responsible for most trick questions built around these functions.A tibble df has character columns year = "2026", month = "07", day = "09", and note = "review". Which call creates date = "2026-07-09" while retaining all three component columns and note?
unite(df, date, day:year, sep = "-", remove = FALSE)unite(df, date, year:day, sep = "-", remove = FALSE) (correct answer)unite(df, date, year:day, sep = "-", remove = TRUE)unite(df, date, c(year, day), sep = "-", remove = FALSE)tidyr::unite(), two things determine whether the result is correct: the order of columns in the selection and the remove argument. The function pastes columns together in the exact order you list them, and remove = FALSE keeps the source columns in the final tibble.
To produce "2026-07-09", you need year, then month, then day — in that order. The column range year:day selects them in the order they appear in the tibble (year, month, day), which matches the target format. Pairing that range with sep = "-" and remove = FALSE gives you the new date column while preserving all original columns, including note. That makes B the correct answer.
A is wrong because day:year reverses the column order, producing "09-07-2026" instead of "2026-07-09". Column ranges are order-sensitive — day:year sweeps from day back to year.
C uses the correct column order (year:day) but sets remove = TRUE, which drops year, month, and day from the result. The question explicitly requires retaining the component columns, so this fails that condition.
D uses c(year, day), which skips month entirely — your date would become "2026-09", missing the month component altogether.
A quick study tip: when you see unite(), immediately check two things — the column order in the selection (it's literal, not sorted) and whether remove is TRUE or FALSE. Exam questions often bait you with one correct element and one wrong detail.Suppose df$token contains c("001-TRUE", "010-FALSE"). After the following operation, what are the types and values of the new columns?
separate(df, token, c("key", "flag"), sep = "-", convert = TRUE)
key is integer c(1, 10); flag is logical c(TRUE, FALSE) (correct answer)key is character c("001", "010"); flag is logical c(TRUE, FALSE)key is integer c(1, 10); flag is character c("TRUE", "FALSE")key is character c("1", "10"); flag is factor with two levelstidyr::separate(), the key detail to understand is the convert = TRUE argument — this is what the question is really testing. Without it, separate() always produces character columns. With convert = TRUE, R applies type.convert() to each resulting column, which attempts to parse strings into their most appropriate native type.
Here's the logic: splitting "001-TRUE" and "010-FALSE" on "-" yields "001"/"010" for key and "TRUE"/"FALSE" for flag. When convert = TRUE is applied, "001" becomes the integer 1 (leading zeros are dropped because integers don't preserve them), "010" becomes 10, and the strings "TRUE"/"FALSE" are recognized as R's built-in logical literals and converted to TRUE/FALSE. This makes A correct: key is integer c(1, 10) and flag is logical c(TRUE, FALSE).
B is wrong because it assumes key stays as character — it ignores that convert = TRUE triggers type parsing. C makes the opposite mistake: it correctly converts key to integer but fails to apply the same conversion logic to flag, leaving it as character when it should become logical. D is a red herring combining two errors — character for key and an invented factor type for flag; separate() with convert = TRUE never produces factors.
A useful rule of thumb: whenever you see convert = TRUE in a tidyr function, mentally run each column through type.convert() — integers, doubles, logicals, and even complex types can emerge from what looked like plain strings.A tibble contains code = "north.07". Which call correctly creates region = "north" and an integer column batch = 7?
separate(df, code, c("region", "batch"), sep = "\\.", convert = TRUE) (correct answer)separate(df, code, c("region", "batch"), sep = ".", convert = TRUE)separate(df, code, c("region", "batch"), sep = "\\s+", convert = TRUE)separate(df, code, c("region", "batch"), sep = "[0-9]+", convert = TRUE)separate() from the tidyr package, two things matter: the separator pattern and whether R should auto-convert column types. The sep argument accepts a regular expression, which is a critical detail this question tests.
In "north.07", the delimiter is a literal dot. In regex, a bare . means any character, so you must escape it with a double backslash (\\.) to match a literal period. With convert = TRUE, tidyr will automatically parse "07" into the integer 7 — exactly what the question asks for. That makes A the correct answer: sep = "\\." targets the dot precisely, and convert = TRUE handles the type coercion.
B fails because sep = "." passes an unescaped dot to the regex engine, which matches any character. While it might accidentally work on this specific string, it's semantically wrong and unreliable — and on an exam, "technically incorrect" matters. C uses sep = "\\s+", which matches one or more whitespace characters. There's no whitespace in "north.07", so the string wouldn't split at all. D uses sep = "[0-9]+", which matches the numeric portion itself (07) rather than the dot separator — this would consume the digits and leave you without a proper batch value.
A good rule of thumb: in R regex strings, special characters like ., +, *, ?, and ( must be escaped with \\. Whenever you see a sep argument with a literal dot, immediately ask yourself whether it's escaped — that's a classic exam trap.Consider df <- tibble(x = "AA-BB-CC"). What values are produced by separate(df, x, into = c("group", "detail"), sep = "-", extra = "merge")?
group = "AA" and detail = "BB-CC" (correct answer)group = "AA-BB" and detail = "CC"group = "AA" and detail = "BB"group = "AA-BB-CC" and detail = NAseparate() from the tidyr package, the key parameter to understand is extra, which controls what happens when a string splits into more pieces than you have into columns to receive them.
By default, separate() will warn and drop extra pieces. But when you set extra = "merge", you're telling R to keep splitting from the left until it fills all but the last column, then merge everything remaining into the final column. Think of it as a "left-to-right fill, keep the rest together" rule.
In this question, "AA-BB-CC" splits on "-" into three pieces: "AA", "BB", and "CC". You only have two destination columns: "group" and "detail". With extra = "merge", R fills group with the first piece "AA", then merges everything left over — "BB-CC" — into detail. That makes A correct: group = "AA" and detail = "BB-CC".
B is wrong because it reverses the merge direction — extra = "merge" always fills from the left, never from the right. C reflects the default extra = "warn" (or "drop") behavior, where the third piece "CC" is simply discarded, giving you "AA" and "BB" but losing data. D would occur if sep didn't match anything at all, leaving the entire string unbroken.
A good tip: mentally pair extra with fill — extra handles too many pieces, while fill handles too few. Knowing this contrast helps you quickly eliminate distractors on any separate() question.A row contains period = "2025-07". What does the following call produce?
separate(df, period, into = c("year", "month", "day"), sep = "-", fill = "right")
year = "2025", month = "07", and day = NA (correct answer)year = NA, month = "2025", and day = "07"year = "2025", month = NA, and day = "07"year = "2025", month = "07", and day = ""tidyr::separate(), two parameters control what happens when the input has fewer pieces than the into vector expects: fill and extra. Here, period = "2025-07" splits on "-" into only two pieces — "2025" and "07" — but you've requested three columns: "year", "month", and "day". Something has to give.
The fill = "right" argument tells R to pad missing values on the right side of the column list. So "2025" fills year, "07" fills month, and since there's nothing left, day receives NA. That makes A the correct answer.
B is wrong because fill = "left" would push the NA to the leftmost column, giving year = NA, not fill = "right". C is a tempting distractor — it looks like the NA is placed in the middle, which doesn't correspond to any valid fill direction; R always fills from one end, not the interior. D is wrong because separate() does not insert empty strings "" for missing pieces — it uses NA by default, which is the tidyverse standard for representing absent data.
A handy mental model: think of fill like justifying text. fill = "right" means the existing values are left-aligned, and the blank space (i.e., NA) appears on the right. Remembering that separate() uses NA — never "" — for missing pieces will help you eliminate distractors like D quickly on the exam.Suppose df has left = c("x", NA) and right = c("y", "z"). What is produced by unite(df, pair, left, right, sep = ":") when na.rm is not specified?
pair = c("x:y", NA)pair = c("x:y", "z")pair = c("x:y", "NA:z") (correct answer)pair = c("x:y", ":z")tidyr::unite(), the key behavior to understand is how the function handles NA values by default. The na.rm parameter controls whether NA values are silently dropped before pasting columns together — and critically, its default is FALSE.
With na.rm = FALSE, unite() treats NA as the literal string "NA" when combining values. So for the second row, where left = NA and right = "z", the function pastes "NA" and "z" together with your separator, producing "NA:z". Combined with the first row's clean "x:y", you get c("x:y", "NA:z") — confirming that C is correct.
Here's why the other choices miss the mark: A assumes that unite() propagates NA the way many base R operations do, where NA contaminates the result and returns NA. That's not what happens here — the NA is coerced to a character string. B suggests na.rm = TRUE behavior, where the NA value in left is simply dropped, leaving only "z". That would only occur if you explicitly set na.rm = TRUE. D implies the NA becomes an empty string "", giving ":z", but unite() has no behavior that converts NA to blank by default.
A useful study tip: whenever you see unite() with NA values and no na.rm argument, remember the default is FALSE, which means NA becomes the string "NA" — it doesn't disappear, and it doesn't propagate as a true missing value.What values result from separate(df, code, into = c("p1", "p2", "p3"), sep = c(2, 4)) when code is "ABCDE"?
p1 = "A", p2 = "BCD", and p3 = "E"p1 = "A", p2 = "BC", and p3 = "DE"p1 = "AB", p2 = "C", and p3 = "DE"p1 = "AB", p2 = "CD", and p3 = "E" (correct answer)separate() in R's tidyr package, the key is understanding how numeric sep values define split positions, not delimiters. When sep is a numeric vector, each number represents a character position index where the string gets cut — think of them as "cut after position N" instructions.
For "ABCDE" with sep = c(2, 4), the string is sliced at positions 2 and 4: characters 1–2 go to p1, characters 3–4 go to p2, and characters 5 onward go to p3. That gives you p1 = "AB", p2 = "CD", p3 = "E" — confirming D is correct.
Choice A (p1 = "A", p2 = "BCD", p3 = "E") would result from splitting at positions 1 and 4, not 2 and 4 — someone misread the first cut point. Choice B (p1 = "A", p2 = "BC", p3 = "DE") reflects splitting at positions 1 and 3, confusing the cut indices by off-by-one errors throughout. Choice C (p1 = "AB", p2 = "C", p3 = "DE") gets the first split right but misapplies the second — splitting at position 3 instead of 4, likely by treating sep[2] as relative to the previous cut rather than as an absolute position.
A reliable mental model: numeric sep values are absolute character positions in the original string, not relative jumps. Before answering these questions, quickly sketch the string with position numbers above each character — it prevents the off-by-one trap that makes distractors like A, B, and C so tempting.