This document contains the answers to Independent Exercise —
Data Visualization (Practicum 3, School of Data Science,
Mathematics, and Informatics, IPB University). All visualizations were
created using the ggplot2 package, following the workflow
that has been learned: Data → Aesthetics → Geometry → Labels →
Theme.
The datasets used (mpg, diamonds,
economics) are built-in datasets from the
ggplot2 package, so they do not need to be imported
separately.
# Packages used
library(ggplot2) # data visualization
library(dplyr) # data manipulation (group_by, summarise, etc.)
library(scales) # axis label formatting (percentages, currency, etc.)
# Inspect the structure of the dataset to be used
glimpse(mpg)## Rows: 234
## Columns: 11
## $ manufacturer <chr> "audi", "audi", "audi", "audi", "audi", "audi", "audi", "…
## $ model <chr> "a4", "a4", "a4", "a4", "a4", "a4", "a4", "a4 quattro", "…
## $ displ <dbl> 1.8, 1.8, 2.0, 2.0, 2.8, 2.8, 3.1, 1.8, 1.8, 2.0, 2.0, 2.…
## $ year <int> 1999, 1999, 2008, 2008, 1999, 1999, 2008, 1999, 1999, 200…
## $ cyl <int> 4, 4, 4, 4, 6, 6, 6, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 8, 8, …
## $ trans <chr> "auto(l5)", "manual(m5)", "manual(m6)", "auto(av)", "auto…
## $ drv <chr> "f", "f", "f", "f", "f", "f", "f", "4", "4", "4", "4", "4…
## $ cty <int> 18, 21, 20, 21, 16, 18, 18, 18, 16, 20, 19, 15, 17, 17, 1…
## $ hwy <int> 29, 29, 31, 30, 26, 26, 27, 26, 25, 28, 27, 25, 25, 25, 2…
## $ fl <chr> "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p…
## $ class <chr> "compact", "compact", "compact", "compact", "compact", "c…
mpg)Calculate the average
ctyfor each manufacturer, select the top 10, create a comparison plot, and summarize the main finding.
cty per manufacturermpg_avg_cty <- mpg %>%
group_by(manufacturer) %>%
summarise(avg_cty = mean(cty), n_model = n(), .groups = "drop") %>%
arrange(desc(avg_cty)) %>%
slice_head(n = 10)
mpg_avg_ctyggplot(mpg_avg_cty, aes(x = reorder(manufacturer, avg_cty), y = avg_cty,
fill = avg_cty)) +
geom_col(width = 0.7, show.legend = FALSE) +
geom_text(aes(label = round(avg_cty, 1)), hjust = -0.25, size = 3.6,
fontface = "bold", color = "grey20") +
coord_flip() +
scale_fill_gradient(low = "#7fb3d5", high = "#1b4f72") +
scale_y_continuous(expand = expansion(mult = c(0, 0.18))) +
labs(
title = "Top 10 Manufacturers with the Highest Average City MPG",
subtitle = "Higher `cty` values indicate better fuel efficiency in city driving conditions",
x = "Manufacturer",
y = "Average City MPG (cty)",
caption = "Source: mpg dataset (ggplot2)"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", size = 15),
panel.grid.major.y = element_blank(),
panel.grid.minor = element_blank()
)avg_cty across the manufacturers
above indicate that the type of vehicles produced (not
just the brand) is an important factor affecting city fuel
efficiency.(Note: the exact numbers will adjust once the chunk above is run in your RStudio — the overall order and pattern above should remain consistent.)
diamonds)Choose one numerical variable, compare its distribution across one categorical variable, improve the plot appearance, and interpret the pattern.
The selected numerical variable is
price. The categorical comparison variable
is cut (diamond cut quality).
ggplot(diamonds, aes(x = cut, y = price, fill = cut)) +
geom_violin(alpha = 0.55, trim = FALSE, color = NA) +
geom_boxplot(width = 0.15, alpha = 0.9, outlier.size = 0.6,
outlier.alpha = 0.3, color = "grey20") +
scale_fill_viridis_d(option = "D", guide = "none") +
scale_y_continuous(trans = "log10", labels = scales::dollar_format()) +
labs(
title = "Distribution of Diamond Prices (`price`) by Cut Quality (`cut`)",
subtitle = "The Y-axis uses a logarithmic scale because the price distribution is strongly right-skewed",
x = "Cut (Cut Quality)",
y = "Price in USD (log scale)",
caption = "Source: diamonds dataset (ggplot2)"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", size = 15),
panel.grid.minor = element_blank()
)price
distribution is highly right-skewed (many inexpensive diamonds
and a few very expensive ones), so the log scale makes comparisons
across categories much easier to read.viridis color palette — consistent,
colorblind-friendly, and easy to distinguish across cut
categories.guide = "none")
because the colors correspond directly to the X-axis labels, making the
legend redundant.Surprisingly, the “Fair” (lowest cut quality)
category tends to have a median price that is not lower — and is
sometimes even higher — than the “Ideal” category. This occurs because
price is strongly influenced by carat (diamond
size), while cut contributes relatively little to the final
price.
Each cut category has a very wide
price distribution (the violin extends from low to high prices),
indicating that substantial price variation occurs within each
cut-quality category, not only across categories.
This implies that to understand diamond prices comprehensively,
cut alone is not sufficient — it needs to be combined with
other variables such as carat, color, and
clarity (see Question 3).
diamonds_sample)Visualize the relationship between
caratandprice, add at least one relevant aesthetic, apply suitable customization, and explain the relationship shown.
Because the original diamonds dataset is very large
(53,940 rows) and can make the scatter plot too dense
(overplotting), we first take a random sample of 1,000 rows and
save it as diamonds_sample.
set.seed(123) # ensure the sample can be reproduced
diamonds_sample <- diamonds %>% slice_sample(n = 1000)
glimpse(diamonds_sample)## Rows: 1,000
## Columns: 10
## $ carat <dbl> 0.73, 0.70, 0.31, 0.31, 0.31, 0.83, 0.51, 0.70, 0.40, 1.10, 0.…
## $ cut <ord> Ideal, Ideal, Ideal, Ideal, Ideal, Good, Very Good, Good, Idea…
## $ color <ord> I, G, D, H, E, E, D, H, E, I, E, D, D, F, I, F, I, D, F, I, I,…
## $ clarity <ord> VS1, VS1, VS1, VVS1, IF, SI1, VS2, SI1, VS1, SI1, VVS2, VS2, S…
## $ depth <dbl> 60.7, 60.8, 61.6, 62.2, 60.9, 63.7, 62.5, 64.2, 61.6, 61.2, 60…
## $ table <dbl> 56, 56, 55, 56, 55, 59, 58, 58, 56, 61, 59, 55, 58, 59, 59, 58…
## $ price <int> 2397, 3300, 713, 707, 987, 3250, 1668, 1771, 1053, 4640, 2467,…
## $ x <dbl> 5.85, 5.73, 4.30, 4.34, 4.39, 5.95, 5.12, 5.59, 4.73, 6.61, 5.…
## $ y <dbl> 5.81, 5.80, 4.33, 4.37, 4.41, 5.89, 5.18, 5.62, 4.78, 6.66, 5.…
## $ z <dbl> 3.54, 3.51, 2.66, 2.71, 2.68, 3.77, 3.22, 3.60, 2.93, 4.01, 3.…
carat and priceggplot(diamonds_sample, aes(x = carat, y = price, color = clarity)) +
geom_point(alpha = 0.65, size = 2.3) +
geom_smooth(method = "loess", se = FALSE, color = "grey15",
linewidth = 0.9, linetype = "dashed") +
scale_color_viridis_d(option = "C") +
scale_y_continuous(labels = scales::dollar_format()) +
labs(
title = "Relationship between Carat and Price in Diamonds",
subtitle = "Random sample of 1,000 observations from the diamonds dataset, colored by clarity",
x = "Carat (Diamond Weight)",
y = "Price (USD)",
color = "Clarity",
caption = "Source: diamonds_sample — random subset of ggplot2::diamonds, n = 1000, seed = 123"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", size = 15),
legend.position = "right"
)There is a positive and non-linear relationship
(resembling an exponential pattern) between carat
and price: as diamond size increases, price rises at an
increasingly faster rate rather than linearly or at a constant
rate.
The trend line (geom_smooth, grey dashed line)
reinforces this upward-curving pattern, which is especially clear for
carat > 1.
The additional aesthetic
color = clarity shows that at the same
carat level, diamonds with better clarity (for example,
IF, VVS1) tend to have higher
prices than diamonds with lower clarity (for example, I1) —
indicating that clarity also contributes to price beyond diamond size
itself.
The increasingly wide spread of points (greater price variance) at higher carat values indicates that other factors (cut, color, clarity) become increasingly influential in the final price as diamond size increases.
economics)Visualize
psavertover time, use clear labels and a suitable theme, highlight or annotate a noticeable change, and provide a short interpretation.
krisis_mulai <- as.Date("2008-01-01")
krisis_selesai <- as.Date("2012-06-01")
ggplot(economics, aes(x = date, y = psavert)) +
annotate("rect", xmin = krisis_mulai, xmax = krisis_selesai,
ymin = -Inf, ymax = Inf, fill = "firebrick", alpha = 0.12) +
geom_line(color = "#2C7FB8", linewidth = 0.7) +
geom_hline(yintercept = mean(economics$psavert), linetype = "dashed",
color = "grey45", linewidth = 0.5) +
annotate("text", x = as.Date("2010-03-01"), y = max(economics$psavert) - 1,
label = "Financial Crisis\n2008–2012", color = "firebrick4",
fontface = "bold", size = 3.6, lineheight = 0.9) +
scale_x_date(date_breaks = "5 years", date_labels = "%Y") +
labs(
title = "Trend of the Personal Savings Rate in the U.S.",
subtitle = "Monthly data from the economics dataset (1967–2015)",
x = "Year",
y = "Personal Savings Rate (%)",
caption = "Source: economics dataset (ggplot2) | Grey dashed line = overall average"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", size = 15),
panel.grid.minor = element_blank()
)Overall, psavert shows a long-term downward
trend from the late 1960s–1970s through the mid-2000s,
reflecting changes in saving behavior among U.S. households over several
decades.
The highlighted area (2008–2012) marks the period of the
global financial crisis, during which
psavert increased sharply compared with
previous years — a common pattern during recessions, when households
tend to reduce spending and save more because of economic
uncertainty.
The overall average line (grey dashed line) helps quickly identify which periods are above or below the historical average.
Create one visualization with at least three presentation problems, then redesign it using improvements such as color, theme, scale, labels, legend, or annotation, and briefly explain the changes.
At least 3 presentation problems in the plot above:
No informative title or axis labels — the axes
still use the raw column names (displ, hwy),
which are unclear to general readers.
Points overlap (overplotting) without adjusted
transparency (alpha) or size (size), making it
difficult to see data density in certain areas.
The default ggplot2 color palette has
insufficient contrast for some of the class categories (7
categories), making some colors difficult to distinguish.
The legend is titled “class” — a raw variable name rather than a title that is easy for a general audience to understand.
The default theme (theme_gray()) with a grey
background makes the data points less prominent and the visualization
look less professional.
plot_diperbaiki <- ggplot(mpg, aes(displ, hwy, color = class)) +
geom_point(size = 2.6, alpha = 0.75) +
scale_color_brewer(palette = "Set2") +
labs(
title = "Relationship between Engine Size and Highway Fuel Efficiency",
subtitle = "Each point represents one car model, colored by vehicle class",
x = "Engine Displacement (liter)",
y = "Highway Fuel Efficiency (mpg)",
color = "Vehicle Class",
caption = "Source: mpg dataset (ggplot2)"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", size = 15),
legend.position = "right",
panel.grid.minor = element_blank()
)
plot_diperbaiki| Aspect | Before | After | Reason |
|---|---|---|---|
| Labels | No title/labels | Complete title, subtitle, axis labels & caption | Helps the audience understand the plot context without reading the code |
| Point transparency & size | Default (alpha = 1) |
alpha = 0.75, size = 2.6 |
Reduces the overplotting effect and makes data density more visible |
| Color | Default ggplot2 palette | scale_color_brewer(palette = "Set2") |
Colors are easier to distinguish and visually appealing |
| Theme | theme_gray() (default) |
theme_minimal() |
Cleaner appearance, keeping the focus on the data |
| Legend | Title “class” | Title “Vehicle Class” | Easier for a non-technical audience to understand |
labs()), themes
(theme_minimal()), and annotations
(annotate(), geom_hline()) consistently
improves the readability and communication effectiveness of each plot
compared with its basic version.