100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

ggplot2 Basics

Learn ggplot2's grammar of graphics—aesthetics, geoms, facets, and themes—to build layered, publication-quality charts in R.

Data WranglingBeginner10 min readJul 10, 2026
Analogies

The Grammar of Graphics

ggplot2 implements the grammar of graphics, building a plot as layers rather than choosing a single chart type up front: you start with ggplot(data, aes(x = ..., y = ...)) to declare the data and the aesthetic mappings (which columns map to x, y, color, size, etc.), then add one or more geom_*() layers such as geom_point() or geom_bar() with +, each of which draws a specific kind of mark using those mappings.

🏏

Cricket analogy: Building a ggplot layer by layer is like building a scorecard graphic: first you set up the pitch (ggplot() + aes()), then you add the batting dots (geom_point()), then maybe a run-rate line on top (geom_line()).

Building Plots Layer by Layer

Different geoms interpret the same aesthetic mappings differently: geom_point() draws a scatterplot of individual x/y pairs, geom_line() connects ordered x/y pairs with a line (useful for time series), geom_bar() counts occurrences of a categorical x by default (or uses geom_col() when you already have the y value to plot), and geom_boxplot() summarizes a numeric variable's distribution across categories. Aesthetics set inside aes() are mapped from data and change per row (like color = species), while arguments set outside aes() (like color = 'blue') apply a fixed value to the whole layer.

🏏

Cricket analogy: geom_point() plotting runs vs balls faced for every batter, colored by team via aes(color = team), is like a scatter of individual batting performances, whereas color = 'blue' outside aes() would color every dot the same regardless of team.

r
library(ggplot2)

ggplot(mpg, aes(x = displ, y = hwy, color = class)) +
  geom_point(alpha = 0.7, size = 2) +
  geom_smooth(method = 'lm', se = FALSE, color = 'black') +
  facet_wrap(~ drv) +
  labs(
    title = 'Engine Displacement vs Highway MPG',
    x = 'Displacement (L)',
    y = 'Highway MPG',
    color = 'Class'
  ) +
  theme_minimal()

Facets, Themes, and Labels

facet_wrap() and facet_grid() split a single plot into a grid of small multiples, one panel per level of a categorical variable, so facet_wrap(~ drv) draws a separate scatterplot for each drivetrain while keeping axes comparable, an efficient way to reveal how a relationship differs across groups without cramming everything into one panel with overlapping colors.

🏏

Cricket analogy: facet_wrap(~ format) splitting a batting-average scatterplot into separate panels for Test, ODI, and T20 is like a broadcaster showing three separate screens comparing a player's form across formats side by side.

facet_grid(rows ~ cols) differs from facet_wrap() by arranging panels in a strict matrix defined by two variables (e.g. facet_grid(drv ~ cyl)), which is useful when you want every combination of two categorical variables shown, even if some panels end up empty.

Customizing Appearance with Themes and Labels

labs() sets human-readable titles, axis labels, and legend titles in one call (labs(title = ..., x = ..., y = ..., color = ...)), while theme_*() functions (theme_minimal(), theme_bw(), theme_classic()) swap out the overall visual style—background, gridlines, fonts—in one line; fine-grained tweaks like rotating axis text or removing the legend go through theme(axis.text.x = element_text(angle = 45), legend.position = 'none').

🏏

Cricket analogy: labs(title = 'Run Rate by Over') is like a broadcaster captioning a graphic clearly, while theme_minimal() is choosing a clean broadcast graphics package over a cluttered one full of sponsor logos and gridlines.

Statistical Transformations and Common Pitfalls

Some geoms compute a statistical transformation of the data before drawing, rather than plotting raw values directly: geom_smooth() fits a trend line (linear with method = 'lm', or a loess curve by default) with a shaded confidence ribbon, geom_histogram() bins a continuous variable and counts observations per bin, and geom_density() estimates a smoothed distribution curve, each a convenient shortcut for a stat_*() layer working behind the scenes.

🏏

Cricket analogy: geom_smooth(method = 'lm') fitting a trend line through a batter's strike rate over a career is like a pundit drawing a form-line on a graphic to show whether the player is improving or declining.

Aesthetics set inside aes() (e.g. aes(color = class)) map data values to visual properties and vary per row, while the same argument set outside aes() (e.g. geom_point(color = 'blue')) applies one fixed value to the whole layer; mixing these up is one of the most common ggplot2 beginner mistakes, producing a single-color plot with an unwanted colour legend or vice versa.

  • ggplot2 builds a plot in layers: ggplot(data, aes(...)) sets up mappings, and geom_*() layers added with + draw the marks.
  • Aesthetics inside aes() map data columns to visual properties and vary per row; the same argument outside aes() sets one fixed value.
  • Different geoms suit different data shapes: geom_point() for scatter, geom_line() for ordered sequences, geom_bar()/geom_col() for counts or totals, geom_boxplot() for distributions.
  • facet_wrap() and facet_grid() split one plot into small multiples by a categorical variable, keeping axes comparable.
  • labs() sets titles and axis/legend labels; theme_*() functions and theme() control overall and fine-grained visual style.
  • Some geoms like geom_smooth(), geom_histogram(), and geom_density() apply a statistical transformation before drawing.
  • Confusing aes()-mapped and fixed arguments is a very common source of unexpected colors, sizes, or legends.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#RProgrammingStudyNotes#Ggplot2Basics#Ggplot2#Grammar#Graphics#Building#StudyNotes#SkillVeris#ExamPrep