EPPS Math & Coding Camp

Data
Visualization

Histograms, boxplots, and scatter plots in base R, then layered graphics with ggplot2.

JP AdjadehEPPS, UT Dallas
Attendance check-in QR code
Before We Start

See the range of what's possible

Two galleries worth exploring — different chart types, different tools, different visual styles.

Base R

Key Functions

par()Controls layout, margins, and colors
plot(), points(), lines()Creates plots and adds points/lines
axis(), box()Adds custom axes and borders
text(), mtext()Adds labels in the plot or margins
hist()Makes histograms
boxplot()Compares distributions
barplot(), pie()Bar charts and pie charts
abline(), legend()Reference lines and legends
Base R — Histogram

A histogram, with color

Dataset: life_exp

hist() plots the distribution of a numeric column. col and border set the fill and outline.

hist(life_exp$Average_Life_Expectancy,
    main = "Histogram of Average Life Expectancy",
    xlab = "Average Life Expectancy",
    col = "lightblue", border = "grey")
Histogram, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — a histogram of Average_Life_Expectancy with color — in base R. Compare what the AI gives you to the example above.
Base R — Histogram

Adding mean and median lines

Dataset: life_exp

abline() draws a reference line over an existing plot. legend() labels what each color means. Remember na.rm = TRUE — this column has missing values.

mean_le <- mean(life_exp$Average_Life_Expectancy, na.rm = TRUE)
median_le <- median(life_exp$Average_Life_Expectancy, na.rm = TRUE)

hist(life_exp$Average_Life_Expectancy, col = "lightblue", border = "black")
abline(v = mean_le, col = "blue", lwd = 2)
abline(v = median_le, col = "red", lwd = 2)
legend("topleft", legend = c("Mean", "Median"), col = c("blue", "red"), lwd = 2)
Histogram + Lines, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — the same histogram with mean and median reference lines and a legend — in base R. Compare what the AI gives you to the example above.
Base R — Boxplot

Comparing distributions by group

Dataset: life_exp

boxplot() with formula syntax (numeric ~ category) compares a distribution across groups. col takes one color per box.

boxplot(Average_Life_Expectancy ~ Gender, data = life_exp,
    main = "Boxplot of Life Expectancy by Gender",
    xlab = "Gender", ylab = "Average Life Expectancy",
    col = c("lightblue", "lightgreen"))
Boxplot, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — a boxplot of Average_Life_Expectancy by Gender, with one color per box — in base R. Compare what the AI gives you to the example above.
Base R — Scatter Plot

Points colored by category

Dataset: life_exp

pch sets the point shape (19 is a solid filled circle). ifelse() assigns a color conditionally, one value per row.

plot(life_exp$Year, life_exp$Average_Life_Expectancy,
    xlab = "Year", ylab = "Average Life Expectancy",
    main = "Life Expectancy Over Time by Gender",
    pch = 19, col = ifelse(life_exp$Gender == "Male", "blue", "red"))
Scatter Plot, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — a scatter plot of Average_Life_Expectancy over Year, points colored by Gender — in base R. Compare what the AI gives you to the example above.
Base R — Line Plot

Two lines, a legend, and an annotation

Dataset: life_exp

subset() splits the data by group. lines() adds a second line to the same plot. range() sets axis limits wide enough for both. text() and abline() add the annotation.

male_data <- subset(life_exp, Gender == "Male")
female_data <- subset(life_exp, Gender == "Female")

plot(male_data$Year, male_data$Average_Life_Expectancy, type = "l", col = "blue", lwd = 2,
    xlim = range(life_exp$Year),
    ylim = range(life_exp$Average_Life_Expectancy, na.rm = TRUE))
lines(female_data$Year, female_data$Average_Life_Expectancy, col = "red", lwd = 2)
legend("bottomright", legend = c("Male", "Female"), col = c("blue", "red"), lwd = 2)
abline(v = 1918, col = "black", lty = 3)
text(1918, 75, "End of World War I")
Line Plot, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — two life expectancy lines by Gender over Year, with a legend and a labeled reference line at 1918 — in base R. Compare what the AI gives you to the example above.
Base R — Bar Chart

Counting records by category

Dataset: air

table() counts how many rows fall into each category. barplot() turns those counts into bars.

type_counts <- table(air$Type_Name)

barplot(type_counts,
    main = "Number of Readings by Type",
    xlab = "Type", ylab = "Count",
    col = "steelblue")
Bar Chart, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — a vertical bar chart counting air readings by Type_Name — in base R. Compare what the AI gives you to the example above.
Base R — Bar Chart

Same chart, horizontal

Dataset: air

horiz = TRUE flips the bars sideways — useful when category labels are long. las = 1 keeps axis labels horizontal for readability.

barplot(type_counts,
    main = "Number of Readings by Type",
    xlab = "Count", horiz = TRUE,
    col = "steelblue", las = 1)
Horizontal Bar Chart, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — the same bar chart of air readings by Type_Name, oriented horizontally — in base R. Compare what the AI gives you to the example above.
Base R — Pie Chart

Showing a share of the whole

Dataset: ev

table() counts each category again; pie() turns those counts into wedges sized by proportion.

ev_type_counts <- table(ev$`Electric Vehicle Type`)

pie(ev_type_counts,
    main = "Share of Vehicles by Type",
    col = c("cornflowerblue", "coral"))
Pie Chart, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — a pie chart showing the share of ev by `Electric Vehicle Type` — in base R. Compare what the AI gives you to the example above.
Before You Continue

The choice AI can't make

AI can generate a working plot() call in one pass. It will not tell you whether a line chart, bar chart, or boxplot is the right choice for what you're trying to show — that depends on the shape of your data and the question you're asking.
ggplot2

Key Components

ggplot(data, aes())The base canvas and variable mapping
geom_point()Adds points — a scatterplot layer
geom_smooth()Adds a fitted trend line
geom_bar(), geom_boxplot()Bar charts and boxplots as layers
geom_histogram()Histograms as a layer
facet_wrap()Splits into panels by a category
labs(), theme_minimal()Labels and a clean, minimal look

Layers stack with +, in order.

ggplot2 — Points & Trend

A scatterplot with a fitted line

Dataset: ev

geom_point() adds the points; geom_smooth(method = "lm") adds a straight-line fit through them.

ggplot(data = ev, aes(x = Model_Year, y = Electric_Range)) +
  geom_point(color = "cornflowerblue", alpha = .5, size = 2) +
  geom_smooth(method = "lm")
Points + Trend, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — a scatterplot of Electric_Range over Model_Year with a linear trend line — with ggplot2. Compare what the AI gives you to the example above.
ggplot2 — Grouping

Coloring both layers by category

Dataset: ev

Mapping color inside the base aes() applies it to every layer that follows — both the points and the trend line. se = FALSE removes the shaded confidence band.

ggplot(data = ev, aes(x = Model_Year, y = Electric_Range, color = `Electric Vehicle Type`)) +
  geom_point(alpha = .5, size = 2) +
  geom_smooth(method = "lm", se = FALSE, linewidth = 1.5)
Color Mapping, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — coloring both layers by category — with ggplot2. Compare what the AI gives you to the example above.
ggplot2 — Bar Chart

Counting records with geom_bar()

Dataset: air

geom_bar() counts rows per category automatically — no need to build the table yourself first, unlike base R's barplot().

ggplot(data = air, aes(x = Type_Name)) +
  geom_bar(fill = "steelblue") +
  labs(title = "Number of Readings by Type", x = "Type", y = "Count")
Bar Chart, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — a bar chart counting air readings by Type_Name — with ggplot2. Compare what the AI gives you to the example above.
ggplot2 — Boxplot

Comparing distributions with geom_boxplot()

Dataset: life_exp

Unlike base R's formula syntax, ggplot2 maps the category to x and the numeric column to y directly in aes().

ggplot(data = life_exp, aes(x = Gender, y = Average_Life_Expectancy)) +
  geom_boxplot(fill = "lightgreen") +
  labs(title = "Life Expectancy by Gender", x = "Gender", y = "Average Life Expectancy")
Boxplot, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — a boxplot of Average_Life_Expectancy by Gender — with ggplot2. Compare what the AI gives you to the example above.
ggplot2 — Histogram

Distributions with geom_histogram()

Dataset: life_exp

binwidth controls how wide each bar is — try changing it and notice how the shape of the distribution appears to change.

ggplot(data = life_exp, aes(x = Average_Life_Expectancy)) +
  geom_histogram(binwidth = 5, fill = "lightblue", color = "black") +
  labs(title = "Distribution of Life Expectancy", x = "Average Life Expectancy", y = "Count")
Histogram, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — a histogram of Average_Life_Expectancy with binwidth 5 — with ggplot2. Compare what the AI gives you to the example above.
Debug It

Find the bug, then check the fix

ggplot(data = ev, aes(x = Model_Year, y = Electric_Rang)) +
  geom_point(color = "cornflowerblue", alpha = .5, size = 2)
Console OutputError in `geom_point()`: ! Problem while computing aesthetics. Caused by error: ! object 'Electric_Rang' not found
ggplot2 — Polished

Faceting, labels, and a theme together

Dataset: ev

facet_wrap() splits into panels by a category. labs() sets the title and axis labels. theme_minimal() strips the default gray background and gridlines.

ev$range_group <- ifelse(ev$Electric_Range >= 200, "long range", "short range")

ggplot(data = ev, aes(x = Model_Year, y = Electric_Range, color = `Electric Vehicle Type`)) +
  geom_point(alpha = .5) +
  geom_smooth(method = "lm", se = FALSE) +
  facet_wrap(~range_group) +
  labs(title = "Electric range by model year", x = "Model Year", y = "Electric Range (mi)") +
  theme_minimal()
Polished Plot, Prompted

Now prompt it

Now Prompt It
Write your own AI prompt to reproduce this chart — the faceted, labeled, and themed version of the electric range chart — with ggplot2. Compare what the AI gives you to the example above.
Before We Wrap

More layers isn't the goal

AI makes it easy to keep adding layers — a color mapping, a facet, a theme — without stopping to check whether each one adds clarity or just complexity. Before accepting a plot, ask whether a reader unfamiliar with the data could state its main point in one sentence.
Suggested Reading

Go deeper

1. Wickham, Hadley. 2010. "A Layered Grammar of Graphics." Journal of Computational and Graphical Statistics 19, no. 1: 3–28.
2. Healy, Kieran. 2018. Data Visualization: A Practical Introduction. Princeton University Press.

Wickham for the logic behind ggplot2; Healy for visualization design and practice more broadly.

Group Hackathon

Data Manipulation & Visualization — Your Own Dataset

Import your own dataset.

Part 1 — Manipulation (base R + dplyr)

  • Index/subset rows and columns
  • Filter — a single condition, then combined (&/|)
  • Rename a column; create a new variable
  • Convert a column's type (e.g., to factor)
  • Missing values: count them, then compare a summary stat with vs. without them

Part 2 — Visualization (base R + ggplot2)

  • Histogram, bar chart (vertical & horizontal), pie chart, boxplot, scatterplot — your own style
  • Export your charts to .pdf and .jpg
Work time: 15 minutes. Presentation: 3 minutes per group. On PowerPoint, show your code and output for each task — not a live run. Every group member must complete at least one task themselves.
Live Share

Show us what you got

Volunteers walk us through the plot, the code, and how the prompt compared once they tried it.

Recap

Two engines, one habit