R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
64: Advanced ggplot2: Custom Themes
Look, we've all used theme_minimal() or theme_bw() a thousand times. They're great for quick exploration, but when you're handing a report to a stakeholder or submitting a paper, "standard R output" isn't always the vibe you want. To get a truly professional look, you have to stop relying on the presets and start building your own theme objects.
I'm currently working on a dashboard for some simulated high-frequency trading data. The goal is a "Dark Mode" aesthetic—something that looks like a terminal window rather than a spreadsheet. Let's build this from the ground up.
Starting with a boring default
First, I'll whip up a basic plot. I'm using a simple time series of "Volatility" over a few days. Notice how the default ggplot2 theme, while functional, feels too "bright" for the executive dark-mode look I'm after.
library(ggplot2)
# Some dummy volatility data
df <- data.frame(
time = 1:100,
volatility = cumsum(rnorm(100))
)
ggplot(df, aes(x = time, y = volatility)) +
geom_line(color = "#00FFCC", size = 1) +
labs(title = "Market Volatility Index", subtitle = "Real-time stream")
Painting the canvas dark
To customize a theme, we use the theme() function. Instead of changing the data, we're changing the non-data components. I want a deep charcoal background, white text for readability, and I want to strip away those distracting default gridlines.
ggplot(df, aes(x = time, y = volatility)) +
geom_line(color = "#00FFCC", size = 1) +
labs(title = "Market Volatility Index", subtitle = "Real-time stream") +
theme(
plot.background = element_rect(fill = "#1A1A1A"),
text = element_text(color = "white"),
panel.grid.major = element_line(color = "#333333"),
panel.grid.minor = element_blank()
)
The "Grey Box" glitch
If you run that code, you'll notice something annoying: there's a weird grey rectangle surrounding the actual plot area. I made this mistake a dozen times when I first started with ggplot2. I changed the plot.background (the entire image area), but I forgot that the panel.background (the area where the data actually sits) is a separate entity with its own default grey fill.
It looks like a poorly cropped photo. To fix this, I need to make sure the panel background matches the plot background exactly.
# Correcting the background mismatch
ggplot(df, aes(x = time, y = volatility)) +
geom_line(color = "#00FFCC", size = 1) +
labs(title = "Market Volatility Index", subtitle = "Real-time stream") +
theme(
plot.background = element_rect(fill = "#1A1A1A"),
panel.background = element_rect(fill = "#1A1A1A"), # Fixes the grey box
text = element_text(color = "white"),
panel.grid.major = element_line(color = "#333333"),
panel.grid.minor = element_blank(),
axis.text = element_text(color = "#BBBBBB")
)
Packaging it for the whole project
Writing that theme() block over and over again is a nightmare. In a real project, I'd never do that. Instead, I'll wrap these settings into a custom function. This allows me to apply my "Dark Mode" brand to twenty different plots with a single line of code.
I'll start by inheriting from theme_minimal() so I don't have to manually define every single tiny detail (like margin sizes), and then I'll override the specific elements I care about.
theme_dark_exec <- function() {
theme_minimal() %+%
theme(
plot.background = element_rect(fill = "#1A1A1A", color = NA),
panel.background = element_rect(fill = "#1A1A1A", color = NA),
text = element_text(color = "white"),
axis.text = element_text(color = "#BBBBBB"),
panel.grid.major = element_line(color = "#333333"),
panel.grid.minor = element_blank(),
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "#888888")
)
}
# Now it's a breeze to apply
ggplot(df, aes(x = time, y = volatility)) +
geom_line(color = "#00FFCC", size = 1) +
labs(title = "Market Volatility Index", subtitle = "Real-time stream") +
theme_dark_exec()
Notice I used %+%. That's the "theme addition" operator. It's a handy way to add specific theme() modifications to an existing theme object without overwriting the whole thing. Now we have a reusable brand identity for our visuals.
📋 Practical Task
Build a "Botanical Journal" Custom Theme
You are preparing a series of plots for a botany journal. The journal requires a very specific "organic" look: a light cream background, dark olive green text, and no gridlines at all (only axis lines).
Using the iris dataset, create a scatter plot of Sepal.Length vs Sepal.Width. Then, write a custom theme function called theme_botanical() that implements the following:
- Backgrounds: Both
plot.backgroundandpanel.backgroundshould be a cream color (e.g.,"#F5F5DC"). - Text: All text should be a dark olive green (e.g.,
"#556B2F"). - Grids: Remove all major and minor gridlines (set them to
element_blank()). - Axes: Add a solid dark olive green line for the x and y axes using
axis.line.
Apply your custom theme function to the plot to ensure it meets the journal's specifications.
There are no comments for now.