What this quiz covers
This quiz focuses on Building Ggplots, giving you a quick way to practice the rules, question types, and explanations that matter most for R Programming.
A data frame totals has one row per product and contains product and the already-computed numeric variable total_sales.
Which code correctly creates one bar per product whose height equals total_sales?
ggplot(totals, aes(x = product, y = total_sales)) + geom_col(fill = "steelblue")ggplot(totals, aes(x = product, y = total_sales)) + geom_bar(fill = "steelblue")ggplot(totals, aes(x = product)) + geom_col(fill = "steelblue")ggplot(totals, aes(x = product, weight = total_sales)) + geom_col(fill = "steelblue")R Programming Quiz
Practice Building Ggplots 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 Building Ggplots, 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 data frame totals has one row per product and contains product and the already-computed numeric variable total_sales.
Which code correctly creates one bar per product whose height equals total_sales?
ggplot(totals, aes(x = product, y = total_sales)) + geom_col(fill = "steelblue") (correct answer)ggplot(totals, aes(x = product, y = total_sales)) + geom_bar(fill = "steelblue")ggplot(totals, aes(x = product)) + geom_col(fill = "steelblue")ggplot(totals, aes(x = product, weight = total_sales)) + geom_col(fill = "steelblue")geom_col() or geom_bar().
geom_col() is designed exactly for situations like this one — when you already have a numeric column representing bar heights. It requires both x (the categories) and y (the heights) to be mapped in aes(). Option A does precisely that: x = product places products on the horizontal axis, y = total_sales sets each bar's height to the pre-computed value, and fill = "steelblue" colors the bars. This is the correct answer.
Option B uses geom_bar(), which is the wrong tool here. geom_bar() automatically counts the number of rows per category using stat = "count" — it ignores any y mapping unless you override it with stat = "identity". Using it with a y aesthetic but without that override will throw an error or produce unexpected results.
Option C provides only x = product to geom_col() but omits the required y mapping. Since geom_col() doesn't compute anything on its own, this will produce an error — it has no idea how tall to draw the bars.
Option D attempts to use weight inside aes() with geom_col(), but weight is an aesthetic used by geom_bar() (with counting), not geom_col(). This mismatches the tool and the aesthetic.
Study tip: Memorize this pairing — pre-summarized data → geom_col() with both x and y; raw data you want ggplot2 to count → geom_bar() with only x.A long-format data frame records contains occasion, score, and subject. occasion is a factor. The analyst wants one gray line connecting each subject's scores across occasions, with points colored by subject.
Which code most reliably creates the requested grouping and appearance?
ggplot(records, aes(occasion, score)) + geom_line(color = "gray50") + geom_point(aes(color = subject))ggplot(records, aes(occasion, score)) + geom_line(aes(group = subject), color = "gray50") + geom_point(aes(color = subject)) (correct answer)ggplot(records, aes(occasion, score, color = subject)) + geom_line(color = "gray50") + geom_point()ggplot(records, aes(occasion, score)) + geom_line(aes(color = "subject")) + geom_point(color = subject)geom_line() needs an explicit group aesthetic to know which observations belong to the same subject — otherwise, with a factor on the x-axis, ggplot2 may not draw separate lines per subject at all. Option B does exactly this: it maps group = subject inside geom_line()'s aes(), so each subject gets their own connected line, while setting color = "gray50" outside aes() applies a fixed gray color to all lines. Then geom_point(aes(color = subject)) colors the points by subject independently. This cleanly separates the two visual concerns.
Option A omits group = subject from geom_line(), so ggplot2 has no instruction about which points to connect — it will likely draw one jagged line through all points or fail to draw meaningful lines at all. Option C sets color = subject globally in ggplot(), which means the line layer inherits color-by-subject, overriding the fixed color = "gray50" argument — you'd get colored lines, not gray ones. Option D wraps "subject" in quotes inside aes(), which maps the literal string "subject" as a constant aesthetic rather than the variable, and color = subject outside aes() in geom_point() is a syntax error.
A reliable rule: use group = inside aes() to control line connections, and set a fixed color outside aes() to apply it uniformly regardless of any variable mapping.A data frame trial contains numeric variables dose and response and a categorical variable group. The analyst wants points colored by group but a single black smooth fitted across all observations.
Which plot specification best matches the analyst's goal?
ggplot(trial, aes(dose, response, color = group)) + geom_point() + geom_smooth(aes(group = 1), color = "black", se = FALSE) (correct answer)ggplot(trial, aes(dose, response, color = group)) + geom_point() + geom_smooth(color = "black", se = FALSE)ggplot(trial, aes(dose, response, group = 1)) + geom_point(color = group) + geom_smooth(color = "black", se = FALSE)ggplot(trial, aes(dose, response)) + geom_point(color = "group") + geom_smooth(aes(group = group), color = "black", se = FALSE)ggplot() call are inherited by every geom layer — unless you override them. This question tests whether you know how to selectively override an inherited grouping aesthetic.
The analyst's goal requires two things simultaneously: points colored by group, and a single smooth line ignoring those groups. Option A achieves this perfectly. The global aes(color = group) passes color to geom_point(), producing colored points. Then geom_smooth() receives aes(group = 1) — a constant value — which explicitly overrides the inherited grouping, forcing ggplot2 to fit one smooth across all data. Setting color = "black" (outside aes()) then fixes the line color. A is correct.
Option B looks similar but is missing the crucial aes(group = 1) override in geom_smooth(). Because color = group is inherited, ggplot2 silently inherits the grouping structure too, producing one smooth per group — the opposite of what the analyst wants.
Option C tries to map color = group inside geom_point() as a raw argument rather than inside aes(), which is invalid syntax — R will throw an error because group is a variable, not a literal color string.
Option D passes color = "group" to geom_point(), which renders all points in a color literally named "group" (which doesn't exist, defaulting to a warning/error), and the smooth grouping is also wrong.
The key rule to remember: to override an inherited grouping in one layer, explicitly pass aes(group = 1) to that specific geom — a constant group means one group.The plot below uses raw, which contains day, value, and treatment:
p <- ggplot(raw, aes(x = day, y = value, color = treatment)) + geom_line()
A second data frame, means, contains only day and mean_value. The analyst wants to add black points from means.
Which layer can be added without requiring means to contain value or treatment?
geom_point(data = means, aes(x = day, y = mean_value, color = treatment), inherit.aes = FALSE)geom_point(data = means, aes(y = mean_value), color = "black", inherit.aes = TRUE)geom_point(data = means, aes(x = day), y = "mean_value", color = "black", inherit.aes = FALSE)geom_point(data = means, aes(x = day, y = mean_value), color = "black", inherit.aes = FALSE) (correct answer)aes() mappings from ggplot(). This is powerful, but it becomes a problem when a secondary data frame doesn't contain all the variables the global aesthetics expect.
Here, the global aes() maps color = treatment, which means any geom with inherit.aes = TRUE will try to find a treatment column in whatever data it's using. Since means has no treatment column, inheriting that mapping will throw an error. The solution is inherit.aes = FALSE, which tells the geom to ignore the global aesthetics entirely and rely only on what you explicitly provide.
D does exactly this: it supplies x = day and y = mean_value explicitly, sets color = "black" outside aes() as a fixed property (not a mapped variable), and uses inherit.aes = FALSE to avoid pulling in treatment. This is the cleanest, correct approach.
A fails because it still maps color = treatment inside aes(), which requires means to contain treatment — the exact problem you're trying to avoid. B keeps inherit.aes = TRUE, so ggplot will still try to find treatment in means and fail; it also relies on the inherited x = day, which may work but doesn't solve the core issue. C passes y = "mean_value" as a string outside aes(), making it a literal character value rather than a column mapping — the y-axis would not reflect the actual data.
As a general rule: when using a secondary dataset that's missing variables from the global aes(), always set inherit.aes = FALSE and specify all required aesthetics explicitly.A data frame events contains numeric variables x and y. It also contains an integer variable status_code with values 1, 2, and 3 that represent three categories. The analyst wants points to have distinct colors and distinct shapes for the three categories, with categorical legends.
Which aesthetic specification best satisfies the requirement?
ggplot(events, aes(x, y, color = status_code, shape = status_code)) + geom_point()ggplot(events, aes(x, y, color = factor(status_code), shape = factor(status_code))) + geom_point() (correct answer)ggplot(events, aes(x, y)) + geom_point(color = factor(status_code), shape = factor(status_code))ggplot(events, aes(x, y, color = factor(status_code))) + geom_point(shape = status_code)color or shape in ggplot2, the type of the variable determines how ggplot2 interprets and renders it. Continuous (numeric) variables produce gradient scales, while discrete (factor/character) variables produce categorical scales with distinct colors, distinct shapes, and categorical legends — which is exactly what this question requires.
Option B is correct because wrapping status_code in factor() inside aes() explicitly tells ggplot2 to treat the three values as categories. Both color and shape receive the same factor, so ggplot2 generates a unified legend showing distinct colors and shapes together.
Option A fails because status_code is an integer, so ggplot2 treats it as continuous. You'll get a color gradient instead of three discrete colors, and shape doesn't support continuous variables — this will actually throw an error.
Option C places factor(status_code) outside aes(), inside geom_point() directly. Aesthetics set outside aes() are fixed constants, not mappings. You can't pass a vector or a factor call there — ggplot2 expects a single scalar value like color = "red", so this produces an error or meaningless output.
Option D maps only color to factor(status_code) inside aes(), which gives discrete colors. However, shape is set outside aes() as a fixed value equal to the raw integer vector — same problem as C, and this won't produce a shape legend either.
A reliable rule: any time you want a variable to drive an aesthetic, it must live inside aes(). And if the variable is numeric but represents categories, always wrap it in factor() to get discrete scales and proper legends.A data frame results contains day, site, observed, and predicted. An analyst runs:
ggplot(results, aes(x = day, y = observed, color = site)) + geom_line() + geom_point(aes(y = predicted), color = "black")
Which description of the resulting plot is correct?
day mapping. (correct answer)x was not repeated locally.ggplot2, the key concept to understand is aesthetic inheritance: aesthetics defined in the top-level aes() call apply globally to all layers, but individual layers can override or add their own aesthetics locally.
In this code, aes(x = day, y = observed, color = site) establishes three global mappings. Every subsequent layer inherits x = day automatically. geom_line() inherits all three globals, so it draws site-colored lines of observed values. Then geom_point(aes(y = predicted), color = "black") does two things: it overrides y locally to use predicted instead of observed, and it sets color = "black" outside aes() as a fixed aesthetic, overriding the inherited site color. Crucially, x = day is still inherited from the global mapping — points know their horizontal position. This makes A correct: site-colored lines for observed values, black points placed along day for predicted values.
B is wrong because it claims the x-coordinate is missing. Since x = day is a global mapping, all layers inherit it — no local repetition is needed. C describes the opposite of how overriding works; local settings absolutely can override global ones, which is a core feature of ggplot2's layered system. D reverses the logic entirely — local settings only affect the layer they are defined in, not earlier layers, and the lines are site-colored, not black.
A useful mental model: think of global aesthetics as defaults, and layer-level settings as exceptions. Any layer can override them, but only for itself — never backward.A data frame exam_scores contains one numeric column named score. An analyst wants a histogram with bin width 5 whose vertical axis represents density rather than the number of observations.
Which ggplot specification correctly constructs that histogram?
ggplot(exam_scores, aes(x = score, y = after_stat(count))) + geom_histogram(binwidth = 5)ggplot(exam_scores, aes(x = score, y = density)) + geom_histogram(binwidth = 5)ggplot(exam_scores, aes(x = score, y = after_stat(density))) + geom_histogram(binwidth = 5) (correct answer)ggplot(exam_scores, aes(x = score)) + geom_col(aes(y = after_stat(density)), width = 5)count and density — during the stat computation phase. To use those computed values as aesthetics, you must reference them with after_stat(), which tells ggplot2 to evaluate that variable after the statistical transformation runs.
Option C — aes(x = score, y = after_stat(density)) with geom_histogram(binwidth = 5) — is correct because it properly requests the internally computed density variable using after_stat(), and sets binwidth = 5 as required. This scales the y-axis so that the total area of all bars integrates to 1, which is the definition of a density histogram.
Option A fails because after_stat(count) gives you raw observation counts, not density — the y-axis would represent frequency, not probability density. Option B looks plausible but is a trap: writing y = density without after_stat() tells ggplot2 to look for a column literally named density in your data frame. Since no such column exists in exam_scores, this will throw an error or produce unintended results. Option D uses geom_col() instead of geom_histogram(). geom_col() is for pre-summarized bar charts where you explicitly supply both x and y values — it doesn't perform any binning or stat computation, so after_stat(density) has no meaning there.
A reliable rule of thumb: whenever you want a ggplot2-computed variable on an axis, always wrap it in after_stat(). If you see a variable name alone in aes(), ggplot2 searches your data frame — not its internal calculations.A base plot is created from cars, which contains mpg, wt, and cyl:
p <- ggplot(cars, aes(x = mpg, y = wt, color = factor(cyl))) + geom_point()
A data frame selected contains only mpg, wt, and car_name. The analyst wants to label those selected observations in black.
Which layer correctly adds the requested labels?
geom_text(data = selected, aes(x = mpg, y = wt, label = "car_name"), color = "black", inherit.aes = FALSE)geom_text(data = selected, aes(label = car_name), color = "black", inherit.aes = TRUE)geom_text(data = selected, aes(x = mpg, y = wt, label = car_name), color = "black", inherit.aes = FALSE) (correct answer)geom_text(data = selected, aes(x = mpg, y = wt), label = car_name, color = "black", inherit.aes = FALSE)aes().
Because selected lacks the cyl column used in the base plot's color aesthetic, inheriting the parent aesthetics would cause an error — ggplot would look for cyl in selected and fail. Setting inherit.aes = FALSE tells the new layer to ignore the parent mappings entirely and rely only on what you explicitly provide. Since selected has mpg and wt, you must map both x = mpg and y = wt manually inside aes(). The label text comes from the car_name column, so it belongs inside aes() as label = car_name — this tells ggplot to read values from that column. Placing color = "black" outside aes() correctly applies a fixed aesthetic rather than mapping it to a variable. Option C satisfies all of these requirements.
Option A wraps "car_name" in quotes inside aes(), which treats it as a literal string — every point would be labeled with the word car_name rather than the actual car names. Option B uses inherit.aes = TRUE, meaning ggplot tries to pull cyl from selected, which doesn't exist, causing an error. Option D places label = car_name outside aes(), which requires a fixed scalar value, not a column reference — this will error or behave unexpectedly.
A reliable rule: column references go inside aes(), fixed values go outside. When your new layer uses different data, always set inherit.aes = FALSE and re-specify all required mappings explicitly.An analyst wants every point to be red and does not want a color legend. The analyst writes:
ggplot(measurements, aes(x = time, y = response)) + geom_point(aes(color = "red"))
What change most directly produces the intended result?
color = "red" outside aes() in geom_point(), so it is treated as a fixed layer setting. (correct answer)color = "red" inside aes() and add labs(color = NULL) to remove the legend title.color = "red" into the global aes(), so every layer inherits the requested literal color.geom_point(aes(fill = "red")), so the literal fill is used without a scale.aes() means "map a variable to a visual property," while outside aes() means "set a fixed visual property." This is one of the most commonly tested concepts in R visualization questions.
When you write aes(color = "red"), ggplot2 doesn't interpret "red" as a literal color — it treats the string as a one-level categorical variable, maps it to color using its default color scale, and automatically generates a legend showing that "category." That's the opposite of what the analyst wants.
The fix is option A: placing color = "red" outside aes(), directly inside geom_point(). Written as geom_point(color = "red"), ggplot2 treats it as a fixed aesthetic — every point becomes red, no scale is created, and no legend appears. This directly solves both problems.
Option B is wrong because adding labs(color = NULL) only removes the legend title, not the legend itself — the key still appears, and color still goes through a scale mapping. Option C is wrong for the same core reason as the original code: moving color = "red" into the global aes() still maps a string to a scale, producing a legend regardless of which layer inherits it. Option D is wrong because fill doesn't affect standard points (which use color for their appearance), and using aes(fill = "red") has the same inside-aes() problem anyway.
Study tip: Any time you see a literal value like "red" or 0.5 inside aes(), that's a red flag — literal constants belong outside aes().A data frame quarterly contains one row for each combination of quarter and region, along with a precomputed sales value. The analyst wants region-colored bars placed side by side within each quarter.
Which code creates the requested plot?
ggplot(quarterly, aes(quarter, sales, fill = region)) + geom_bar(position = "dodge")ggplot(quarterly, aes(quarter, sales, fill = region)) + geom_col(position = "stack")ggplot(quarterly, aes(quarter, sales, color = region)) + geom_col(position = "dodge")ggplot(quarterly, aes(quarter, sales, fill = region)) + geom_col(position = "dodge") (correct answer)sales values are already calculated in your data frame, you need geom_col(), which maps bar heights directly to existing values. This makes D the correct choice — it uses geom_col(position = "dodge") with fill = region, placing color-coded bars side by side within each quarter, exactly as requested.
Choice A fails because geom_bar() is designed to count rows, not read precomputed values. Using it with a continuous y aesthetic like sales will throw an error (or produce incorrect results) unless you add stat = "identity" — which none of the options do. A is a classic trap when students confuse geom_bar() and geom_col().
Choice B uses the correct geom (geom_col()) but the wrong position. position = "stack" stacks regional bars on top of each other within each quarter, producing a stacked bar chart — not the side-by-side layout the analyst wants.
Choice C is close but uses color = region instead of fill = region. In ggplot2, color controls the outline of bars, while fill controls the interior. Using color would give you bars with colored borders but no interior color differentiation — not the region-colored bars you're after.
A quick memory aid: use geom_col for computed values, geom_bar for counts. And always double-check whether grouping aesthetics for bars require fill (interior) vs. color (outline).