1 Introduction

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…

2 Question 1 — Comparison (dataset mpg)

Calculate the average cty for each manufacturer, select the top 10, create a comparison plot, and summarize the main finding.

2.1 Calculating the average cty per manufacturer

mpg_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_cty

2.2 Comparison Visualization

ggplot(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()
  )

2.3 Summary of Findings

  • The top ten manufacturers are dominated by producers focusing on fuel-efficient compact / sedan cars (such as Honda, Toyota, and Volkswagen, Hyundai), which generally have smaller engines and therefore more efficient fuel consumption in city driving conditions.
  • Manufacturers that produce more pickup trucks and large SUVs (such as Jeep, Lincoln, Mercury, and Dodge) tend not to appear at the top because their engines are generally larger and less fuel-efficient.
  • The differences in 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.)


3 Question 2 — Distribution (dataset 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()
  )

3.1 Visual Improvements Applied

  1. Combination of violin + boxplot — the violin shows the full distribution shape, while the boxplot adds precise quartile and median summaries.
  2. Log scale on the Y-axis — the 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.
  3. viridis color palette — consistent, colorblind-friendly, and easy to distinguish across cut categories.
  4. Legend removed (guide = "none") because the colors correspond directly to the X-axis labels, making the legend redundant.

3.2 Pattern Interpretation

  • 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).


4 Question 3 — Relationship (dataset diamonds_sample)

Visualize the relationship between carat and price, 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.…

4.1 Visualizing the Relationship between carat and price

ggplot(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"
  )

4.2 Explanation of the Relationship Shown

  • 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.


5 Question 4 — Time Series (dataset economics)

Visualize psavert over 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()
  )

5.1 Brief Interpretation

  • 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.


6 Question 5 — Improve a Visualization

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.

6.1 Initial Version (Problematic)

plot_bermasalah <- ggplot(mpg, aes(displ, hwy, color = class)) +
  geom_point()

plot_bermasalah

At least 3 presentation problems in the plot above:

  1. No informative title or axis labels — the axes still use the raw column names (displ, hwy), which are unclear to general readers.

  2. Points overlap (overplotting) without adjusted transparency (alpha) or size (size), making it difficult to see data density in certain areas.

  3. The default ggplot2 color palette has insufficient contrast for some of the class categories (7 categories), making some colors difficult to distinguish.

  4. The legend is titled “class” — a raw variable name rather than a title that is easy for a general audience to understand.

  5. The default theme (theme_gray()) with a grey background makes the data points less prominent and the visualization look less professional.

6.2 Improved Version

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

6.3 Explanation of Changes

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

7 General Conclusion

  • The five visualizations above show how the choice of geometry (bar, violin/boxplot, scatter, line) should be adapted to the type of analytical question: category comparison, distribution, relationships among numerical variables, or time trends.
  • Adding aesthetics (color, fill), scales (log, date, color), labels (labs()), themes (theme_minimal()), and annotations (annotate(), geom_hline()) consistently improves the readability and communication effectiveness of each plot compared with its basic version.
  • This practice is consistent with the main principle of Practicum 3: a good plot is not only statistically correct, but also easy to interpret for the intended audience.