The Grammar of Graphics

A Systemic Guide to ggplot2

Author

Abdullah Al Shamim

Published

January 24, 2026

Introduction

In this tutorial, we will learn how to transform raw data into meaningful visual stories using ggplot2, the most powerful visualization tool in the R ecosystem. We will move beyond traditional nested coding and embrace the modern Pipe Operator (|>) to build professional graphs layer by layer.

What you will achieve:

  • Understand and analyze the underlying structure of data.
  • Construct graphs using the Grammar of Graphics framework.
  • Manage complex relationships using colors, aesthetics, and facets.
  • Export publication-quality visualizations.

The Grammar of Graphics

The Grammar of Graphics is a theoretical framework for data visualization. Much like a sentence is composed of a subject, verb, and object, a graph is composed of specific independent layers.

The 7 Layers:

  1. Data: The raw material (usually a Data Frame or Tibble).
  2. Aesthetics (aes): Mapping variables to visual properties (X-axis, Y-axis, Color, Size).
  3. Geometries (geom): The shape of the data (e.g., geom_point for scatter plots).
  4. Facets: Splitting data into smaller sub-plots for comparison.
  5. Statistics (stat): Mathematical transformations (e.g., calculating a mean).
  6. Coordinates (coord): The physical space of the graph (Cartesian vs. Polar).
  7. Themes: Styling the non-data elements (fonts, backgrounds, grids).

1. Environment Setup

To begin, we need to load the tidyverse package, which contains ggplot2 and the necessary tools for data manipulation.

Code
# Load the library
library(tidyverse)

2. Data Inspection & Meta-data Analysis

Before building our visualization, we must perform a “Structural Audit.” Understanding the Meta-data (data about data) ensures we choose the correct variables for our axes and aesthetics.

Dataset Overview

The iris dataset is a classic multivariate dataset. It consists of 150 observations across 5 variables, detailing the physical characteristics of three iris flower species.

Code
iris %>% head()
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

The glimpse() function allows us to see the data types (Double, Factor, etc.) and a preview of the values.

Code
iris %>% glimpse()
Rows: 150
Columns: 5
$ Sepal.Length <dbl> 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4, 4.9, 5.4, 4.…
$ Sepal.Width  <dbl> 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.…
$ Petal.Length <dbl> 1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, 1.…
$ Petal.Width  <dbl> 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.…
$ Species      <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa, s…
Variable Name Data Type Description
Sepal.Length Numeric (dbl) Length of the sepal in centimeters.
Sepal.Width Numeric (dbl) Width of the sepal in centimeters.
Petal.Length Numeric (dbl) Length of the petal in centimeters.
Petal.Width Numeric (dbl) Width of the petal in centimeters.
Species Factor (fct) Flower species name (Setosa, Versicolor, Virginica).
Code
iris %>% summary()
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
 Median :5.800   Median :3.000   Median :4.350   Median :1.300  
 Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
 3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
 Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
       Species  
 setosa    :50  
 versicolor:50  
 virginica :50  
                
                
                

3. Creating a Bar Chart (Layer by Layer)

Layer 1 & 2: Data and Aesthetics

First, we initialize the canvas and map the Species variable to the X-axis.

Code
iris %>%
  ggplot(aes(x = Species))

Layer 3: Geometry

We add the geom_bar() function to tell R to represent the data counts as bars.

Code
iris %>%
  ggplot(aes(x = Species)) +
  geom_bar()

Layer 4: Enhancing Aesthetics (Color & Fill)

We map Species to the fill property and adjust transparency and outlines.

Code
iris %>%
  ggplot(aes(x = Species, fill = Species)) +
  geom_bar(alpha = 0.8, color = "black", size = 0.5, width = 0.6)

Layer 5: Faceting (Sub-plots)

Faceting splits the graph into separate panels for each species.

Code
iris %>%
  ggplot(aes(x = Species, fill = Species)) +
  geom_bar() +
  facet_wrap(~ Species)


4. Final Polish & Professional Themes

We add descriptive labels and apply theme_bw() for a clean background.

Code
iris_plot <- iris %>%
  ggplot(aes(x = Species, fill = Species)) +
  geom_bar(color = "black", alpha = 0.8) +
  labs(
    title = "Sample Count by Species in Iris Dataset",
    subtitle = "Analysis of 150 flower samples",
    x = "Species Name",
    y = "Total Samples",
    fill = "Flower Species",
    caption = "Source: In-built R Dataset | Tutorial by Abdullah Al Shamim"
  ) +
  theme_bw() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    legend.position = "right"
  )

iris_plot


5. Exporting the Visualization

Code
ggsave("iris_bar_chart.png", 
       plot = iris_plot, 
       width = 15, 
       height = 10, 
       units = 'cm', 
       dpi = 300)

Systemic Summary

For any future visualization, remember this Master Formula:

Data |> ggplot(aes(x, y)) + geom_type() + facet_type() + labs() + theme()

By applying these layers, you move from simply writing code to architecting a visual narrative.