R Programming Quiz: Customizing Ggplot
10 questions · exam conditions
0:00
Customizing GgplotQuestion 1 of 10

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?

Both axes retain gray tick labels, because axis.text overrides its child elements.
The x tick labels disappear, the y tick labels are gray, and both axis titles are bold.
All tick labels disappear, while only the x-axis title is displayed in bold.
The x tick labels are gray, the y tick labels disappear, and both titles remain unchanged.
← Back to quizzes

R Programming Quiz

R Programming Quiz: Customizing Ggplot

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.

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.

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

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?

  1. Both axes retain gray tick labels, because axis.text overrides its child elements.
  2. The x tick labels disappear, the y tick labels are gray, and both axis titles are bold. (correct answer)
  3. All tick labels disappear, while only the x-axis title is displayed in bold.
  4. The x tick labels are gray, the y tick labels disappear, and both titles remain unchanged.
Explanation: When you see a question about 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.

Question 2

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?

  1. The manual red-blue palette is used with the legend title "Group."
  2. The Dark2 palette is used with the legend title "Cohort." (correct answer)
  3. The two palettes are combined under the legend title "Status."
  4. The Dark2 palette is used without any displayed legend title.
Explanation: When you add multiple 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.

Question 3

A data frame stores rates as proportions rather than whole percentages. Two of its y-values are 0.1260.126 and 0.5040.504.

The plot uses scale_y_continuous(labels = scales::label_percent(accuracy = 1)). How will those two tick values be labeled?

  1. They will be labeled 13% and 50%, rounded to whole percentage points. (correct answer)
  2. They will be labeled 12.6% and 50.4%, preserving one decimal place.
  3. They will be labeled 1% and 1%, because accuracy limits values to one.
  4. They will be labeled 0.126% and 0.504%, without multiplying the proportions.
Explanation: When working with 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.60.126 \times 100 = 12.6, rounded to 1313, becomes 13%, and 0.504×100=50.40.504 \times 100 = 50.4, rounded to 5050, 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.

Question 4

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?

  1. It uses the classic theme because a session-level theme cannot be replaced on one plot.
  2. It uses the minimal theme, but the final theme() call restores classic major grid lines.
  3. It combines the classic axes with all grid lines supplied by the minimal theme.
  4. It uses the minimal theme, with major panel grid lines removed by the final customization. (correct answer)
Explanation: When building a ggplot, think of theme layers as a stack: each one overwrites or extends the previous, processed left to right. The session-level default set by 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.

Question 5

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?

  1. The x-axis is labeled "Displacement," and the plot has no title. (correct answer)
  2. The x-axis is labeled "Engine size," and the title is "Fuel economy."
  3. The x-axis is labeled "Displacement," and the title is "Fuel economy."
  4. The x-axis is unlabeled, and the plot title is displayed as "NULL."
Explanation: When multiple 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.

Question 6

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 00 through 100100 while allowing geom_smooth(method = "lm") to fit the model using all observations?

  1. scale_y_continuous(limits = c(0, 100))
  2. coord_cartesian(ylim = c(0, 100)) (correct answer)
  3. ylim(c(0, 100))
  4. scale_y_continuous(breaks = c(0, 100))
Explanation: When working with axis limits in ggplot2, the critical distinction is whether the limit is applied before or after statistical transformations. Some functions filter the underlying data before models are fit; others simply zoom the viewport after all calculations are complete. 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.

Question 7

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?

  1. labs(color = NULL) removes the color legend title but leaves the legend itself visible
  2. guides(shape = "none")
  3. theme(legend.position = "none")
  4. guides(color = "none") (correct answer)
Explanation: When working with ggplot2 legends, the key distinction to understand is that 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.

Question 8

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?

  1. West is orange in the plot, while the legend lists South and North in that order. (correct answer)
  2. West is omitted from the plot because it is not included in the legend breaks.
  3. Colors are assigned by vector position, so North is orange and South is navy.
  4. The legend lists all three regions in factor-level order despite the specified breaks.
Explanation: When working with 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 vectorc(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.

Question 9

A scatterplot contains observations at x-values 11, 1010, and 100100. 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?

  1. They are equally spaced on the log scale, with labels 0, 1, and 2 shown after transformation.
  2. They are equally spaced on the log scale, but their labels are replaced by 0, 1, and 2.
  3. They are equally spaced on the log scale, with labels 1, 10, and 100 in increasing order. (correct answer)
  4. They retain their unequal linear spacing, with labels 1, 10, and 100 in increasing order.
Explanation: When working with logarithmic scales in ggplot2, the key distinction is between how values are positioned and how they are labeled. These are two separate concerns, and scale_x_log10() controls both independently. scale_x_log10() transforms the axis so that positions are determined by log10(x)\log_{10}(x). This means x=1x = 1, 1010, and 100100 map to positions 00, 11, and 22 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,20, 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.

Question 10

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?

  1. The axis shows trt2, ctrl, and trt1, with only ctrl renamed.
  2. The axis shows Control then High dose, and observations for trt1 remain between them.
  3. The axis shows High dose then Control, and observations for trt1 are excluded. (correct answer)
  4. The axis shows High dose twice because the labeling function receives every observation.
Explanation: When working with 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.