What this quiz covers
This quiz focuses on Customizing Ggplot, giving you a quick way to practice the rules, question types, and explanations that matter most for R Programming.
A plot adds the following theme customization:
theme(
axis.text = element_text(color = "gray40"),
axis.text.x = element_blank(),
axis.title = element_text(face = "bold")
)
Which outcome should be expected?
axis.text overrides its child elements.R Programming Quiz
Practice Customizing Ggplot 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 Customizing Ggplot, 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 plot adds the following theme customization:
theme(
axis.text = element_text(color = "gray40"),
axis.text.x = element_blank(),
axis.title = element_text(face = "bold")
)
Which outcome should be expected?
axis.text overrides its child elements.theme() in ggplot2, think about element inheritance. ggplot2's theme system is hierarchical — general elements like axis.text act as parents to more specific elements like axis.text.x and axis.text.y. The key rule: more specific settings override more general ones, not the other way around.
In this code, axis.text = element_text(color = "gray40") sets gray text for both axes as a baseline. Then axis.text.x = element_blank() overrides that baseline specifically for the x-axis, removing those labels entirely. Meanwhile, axis.text.y was never explicitly set, so it inherits the gray color from axis.text. Finally, axis.title = element_text(face = "bold") makes both axis titles bold, since neither axis.title.x nor axis.title.y overrides it. The result: x tick labels disappear, y tick labels are gray, and both titles are bold — which is answer B.
Answer A is wrong because it reverses the inheritance direction. Child elements like axis.text.x always take priority over the parent axis.text when explicitly set — the parent does not win. Answer C is wrong because only axis.text.x is set to element_blank(), not axis.text itself, so the y-axis labels survive. Answer D gets the axes backwards — it's the x labels that are blanked, not the y labels.
A helpful tip: whenever you see both a general and a specific theme element set together, the more specific one always wins. Think of it as CSS specificity — the narrower selector overrides the broader one.A plot is built with the following additions in order:
scale_color_manual(name = "Status", values = c(A = "red", B = "blue")) +
labs(color = "Group") +
scale_color_brewer(name = "Cohort", palette = "Dark2")
Assuming the mapped values are compatible with the Brewer scale, what controls the final color legend?
scale_* functions that control the same aesthetic in ggplot2, the last one wins — it completely replaces any earlier definitions. This is the core concept being tested here.
Walking through the code in order: scale_color_manual() sets up a red-blue palette with the title "Status." Then labs(color = "Group") updates the legend title to "Group." Finally, scale_color_brewer(name = "Cohort", palette = "Dark2") is added — and because this is a second color scale, it overrides the first entirely, including all title information. The labs() call is also rendered irrelevant because the incoming scale_color_brewer() provides its own name argument. The result is the Dark2 palette with the legend title "Cohort," making B correct.
Answer A is wrong because scale_color_manual() is fully replaced by scale_color_brewer() — the red-blue palette never appears, and "Group" from labs() is overwritten by the name = "Cohort" argument in the final scale. Answer C reflects a misunderstanding that ggplot2 somehow merges or layers multiple scales — it doesn't; scales for the same aesthetic cannot coexist. Answer D is close but incorrect: the name = "Cohort" argument inside scale_color_brewer() explicitly sets a legend title, so a title is definitely displayed.
A handy rule of thumb: in ggplot2, the last scale for a given aesthetic always wins. When you spot multiple scale_color_* calls in a chain, immediately focus on whichever one appears last — that's what controls the final output.A data frame stores rates as proportions rather than whole percentages. Two of its y-values are 0.126 and 0.504.
The plot uses scale_y_continuous(labels = scales::label_percent(accuracy = 1)). How will those two tick values be labeled?
13% and 50%, rounded to whole percentage points. (correct answer)12.6% and 50.4%, preserving one decimal place.1% and 1%, because accuracy limits values to one.0.126% and 0.504%, without multiplying the proportions.scales::label_percent() in ggplot2, there are two key behaviors to understand: it multiplies proportions by 100 and rounds to the precision you specify with accuracy.
By default, label_percent() assumes your data is stored as proportions (values between 0 and 1), so it automatically multiplies by 100 before appending the % symbol. The accuracy = 1 argument controls rounding — it means values are rounded to the nearest whole number (1 unit of precision). So 0.126×100=12.6, rounded to 13, becomes 13%, and 0.504×100=50.4, rounded to 50, becomes 50%. That confirms A is correct.
B is tempting if you confuse accuracy = 1 with "preserve one decimal place." In fact, accuracy = 1 means round to the nearest whole number — one decimal place would require accuracy = 0.1.
C reflects a fundamental misunderstanding of what accuracy does. It does not cap or limit the output value to 1; it sets the rounding increment. A value of accuracy = 1 simply means "round to the nearest 1."
D describes what would happen if label_percent() skipped the multiplication step entirely — but it never does. The function is specifically designed to convert proportions to percentages automatically.
A good rule of thumb: whenever you see label_percent(), remember multiply by 100, then round. The accuracy argument is always about the rounding unit, not decimal places to display or a ceiling on values.An R session first runs theme_set(theme_classic()). A particular plot is then created with:
ggplot(df, aes(x, y)) +
geom_point() +
theme_minimal() +
theme(panel.grid.major = element_blank())
Which description best characterizes this plot?
theme() call restores classic major grid lines.theme_set() acts as the baseline, but any theme function added directly to a plot chain takes precedence over it.
Here's how the layers resolve in this plot: theme_minimal() is added first within the chain, completely replacing the session-level theme_classic() baseline for this plot. Then theme(panel.grid.major = element_blank()) is applied on top, which selectively removes only the major grid lines that theme_minimal() would otherwise display. The result is a minimal-style plot — no axis lines in the classic sense, clean background — but without major grid lines. That makes D the correct answer.
A is wrong because theme_set() sets a session default, not a lock. Any theme added directly in the plot call overrides it for that specific plot. B misreads how theme() works — a bare theme() call with element_blank() removes an element rather than restoring something from a prior theme. It doesn't reach back and reactivate theme_classic(). C incorrectly imagines a merge between theme_classic() and theme_minimal(); because theme_minimal() is explicitly called in the chain, it fully replaces the session default — there's no blending of classic axes into the result.
A useful rule of thumb: in ggplot, later layers win, and theme_*() functions always reset the full theme, while theme() makes targeted modifications on top of whatever full theme preceded it.Consider the following plot construction:
p <- ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
labs(x = "Engine size", title = "Fuel economy") +
xlab("Displacement") +
ggtitle(NULL)
Which description of the resulting plot is correct?
ggplot2 functions modify the same plot element, the last call wins — this is the core principle being tested here. Think of each layer as overwriting the previous setting for that element.
In this plot, labs(x = "Engine size", title = "Fuel economy") sets the x-axis label to "Engine size" and the title to "Fuel economy." However, two more calls follow: xlab("Displacement") overrides the x-axis label, replacing "Engine size" with "Displacement." Then ggtitle(NULL) overrides the title, removing it entirely — passing NULL to ggtitle() explicitly clears the title rather than displaying the string "NULL." So the final plot has an x-axis labeled "Displacement" and no title, making A correct.
B is wrong because it describes the state of the plot after labs() but before the subsequent overrides — it ignores that xlab() and ggtitle() are called afterward. C is a partial-override trap: it correctly identifies that xlab("Displacement") wins over labs(x = ...), but incorrectly assumes the title from labs() survives when ggtitle(NULL) explicitly removes it. D misunderstands how NULL works in ggplot2 — it doesn't render the word "NULL" as text; it signals the absence of a value, effectively deleting the title.
A useful rule of thumb: when you see a ggplot2 chain, mentally scan bottom to top for the last time each element is set — that's what you'll actually see in the output.An analyst creates a scatterplot with a fitted regression line. A few response values lie outside the desired display range, but the analyst wants those observations to remain part of the regression fit.
Which addition displays only the y-range from 0 through 100 while allowing geom_smooth(method = "lm") to fit the model using all observations?
scale_y_continuous(limits = c(0, 100))coord_cartesian(ylim = c(0, 100)) (correct answer)ylim(c(0, 100))scale_y_continuous(breaks = c(0, 100))coord_cartesian(ylim = c(0, 100)) — choice B — is the correct approach because it acts like a camera zoom. It restricts what portion of the plot is displayed without removing any observations from the dataset. This means geom_smooth(method = "lm") still receives every data point when computing the regression line, producing an unbiased fit, while the visible window is cleanly bounded between 0 and 100.
Choice A, scale_y_continuous(limits = c(0, 100)), silently converts any out-of-range y-values to NA before statistical layers are computed. Those observations are effectively dropped from the regression, which distorts the fitted line — exactly the behavior the analyst wants to avoid. Choice C, ylim(c(0, 100)), is simply shorthand for scale_y_continuous(limits = ...), so it has the identical problem: data outside the range is excluded before modeling. Choice D, scale_y_continuous(breaks = c(0, 100)), only controls where tick marks appear on the axis — it does nothing to restrict the displayed range at all.
A handy rule to remember: scale functions filter data; coord functions zoom the view. Whenever a question asks you to restrict the visible range without affecting statistical summaries or model fits, reach for coord_cartesian(). This distinction appears frequently in ggplot2 questions, so internalizing it will serve you well.A scatterplot maps one variable to color and a different variable to shape, producing two legends. The analyst wants to hide only the color legend while retaining the shape legend and the mapped point colors.
Which addition most directly produces the requested result?
labs(color = NULL) removes the color legend title but leaves the legend itself visibleguides(shape = "none")theme(legend.position = "none")guides(color = "none") (correct answer)guides() controls individual legends by aesthetic, while theme() controls all legends globally. Keeping that in mind makes this question straightforward.
The guides() function lets you target a specific aesthetic and tell ggplot2 what to do with its legend. Setting guides(color = "none") suppresses only the color legend while leaving the shape legend fully intact — exactly what the analyst needs. The mapped colors on the points themselves are unaffected; guides() only controls the legend display, not the actual aesthetic mapping. So D is the direct, surgical solution here.
A is a common trap: labs(color = NULL) removes the legend title but the legend itself remains visible. It doesn't hide the legend panel — it just makes the title blank. That's not what the analyst wants.
B goes in the wrong direction entirely. guides(shape = "none") hides the shape legend, which is the one the analyst explicitly wants to keep. This is the opposite of the requested behavior.
C uses theme(legend.position = "none"), which is a blunt instrument — it removes all legends from the plot. Since the analyst wants to retain the shape legend, this approach overcorrects and loses both.
A useful rule of thumb: reach for guides() when you need per-aesthetic legend control, and reach for theme() when you want to change how legends look globally. Whenever a question asks you to hide one legend while keeping another, guides(<aesthetic> = "none") is almost always the right tool.A variable region contains the levels North, South, and West. A plot maps color = region and adds:
scale_color_manual(
values = c(West = "orange", North = "navy", South = "gray"),
breaks = c("South", "North")
)
What is the result?
scale_color_manual() in ggplot2, it helps to understand that two things happen independently: color assignment and legend display. Conflating them is the source of most confusion here.
Color assignment uses the named values vector — c(West = "orange", North = "navy", South = "gray") — to map each level to a color by name, not position. This means West gets orange, North gets navy, and South gets gray throughout the entire plot, regardless of anything else. The breaks argument only controls which levels appear in the legend and in what order. Setting breaks = c("South", "North") hides West from the legend but does not remove West's data points from the plot — they still render in orange. This confirms A is correct: West appears visually in orange, while the legend shows only South then North.
B is wrong because breaks governs legend entries, not which data is plotted. Omitting a level from breaks suppresses its legend key, not its visual representation. C describes what would happen if the values vector were unnamed — in that case, ggplot2 assigns colors by factor-level order positionally — but because the vector uses explicit names, each level is matched by name, not position. D is incorrect because breaks explicitly overrides the default display order; the legend follows the breaks order (South, then North), not the factor-level order.
A good rule of thumb: in scale_*_manual(), values (with named elements) controls appearance, and breaks controls legend visibility and order — these are fully independent mechanisms.A scatterplot contains observations at x-values 1, 10, and 100. The analyst adds scale_x_log10(breaks = c(1, 10, 100), labels = c("1", "10", "100")).
How are these x-values positioned and labeled on the resulting axis?
0, 1, and 2 shown after transformation.0, 1, and 2.1, 10, and 100 in increasing order. (correct answer)1, 10, and 100 in increasing order.scale_x_log10() controls both independently.
scale_x_log10() transforms the axis so that positions are determined by log10(x). This means x=1, 10, and 100 map to positions 0, 1, and 2 respectively — equally spaced on the rendered axis. However, the labels argument lets you override what text actually appears at those tick marks. Here, labels = c("1", "10", "100") instructs ggplot2 to display the original values, not the transformed positions. So the points sit at equal intervals (log scale spacing) but carry human-readable labels of 1, 10, and 100. That's exactly what C describes.
A is wrong because it conflates positioning with labeling — while the positions correspond to 0,1,2 on the log scale, those numbers are not shown as labels when you explicitly set labels = c("1", "10", "100").
B makes the same mistake: it correctly notes equal spacing but incorrectly claims the labels become 0, 1, and 2. The labels argument explicitly prevents that.
D is wrong because it misunderstands what scale_x_log10() does. The whole point of the log transformation is to equalize the spacing — the values are not linearly spaced on this axis.
A useful mental model: think of the log scale as the ruler, and labels as the stickers you place on the tick marks. They're independent — the ruler changes spacing, but the stickers control what you read.A bar chart maps group to the x-axis. The observed values are ctrl, trt1, and trt2. The chart adds:
scale_x_discrete(
limits = c("trt2", "ctrl"),
labels = function(x) ifelse(x == "ctrl", "Control", "High dose")
)
Which description is correct?
trt2, ctrl, and trt1, with only ctrl renamed.trt1 remain between them.trt1 are excluded. (correct answer)scale_x_discrete() in ggplot2, you need to understand that limits does two things simultaneously: it defines which categories appear and in what order. It is not merely a reordering tool — any value omitted from limits is dropped from the plot entirely, including its associated data.
Here, limits = c("trt2", "ctrl") specifies exactly two categories in that order, so trt1 is excluded from the axis and its observations are silently removed from the visualization. The labels function then renames the remaining values: "trt2" becomes "High dose" and "ctrl" becomes "Control". The result is an axis showing High dose first, then Control — which is precisely what C describes.
A is wrong because it assumes limits only reorders without filtering, keeping trt1 visible. In reality, omitting a value from limits removes it. B shares the same misconception — it correctly identifies that trt1 observations would disappear but claims the axis still shows trt1 data "between" the two groups, which contradicts how limits works. D reflects a misunderstanding of the labels function: it receives only the values that survive the limits filter, not every original observation. Since "trt2" is not equal to "ctrl", the ifelse maps it to "High dose" — it does not duplicate anything.
A useful rule of thumb: limits is a whitelist. Only categories you explicitly include will appear, and the order you list them becomes the axis order. Keep this in mind whenever you see scale_*_discrete() on the exam.