## ----setup, include=FALSE----------------------------------------------------- knitr::opts_chunk$set( message = FALSE, warning = FALSE, fig.width = 6.5, fig.height = 3.8 ) ## ----html-report-code, eval=FALSE--------------------------------------------- # report <- analyze_plot(good_plot) # render_report(report) # save_report(report, "goldenviz-report.html") # view_report(report) ## ----examples-report-annotated, echo=FALSE, out.width="100%"------------------ knitr::include_graphics("figures/goldenviz-report-annotated.png") ## ----r1-bad------------------------------------------------------------------- library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + labs(x = "Weight", y = "Miles per gallon") + theme_minimal() ## ----r1-good------------------------------------------------------------------ library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + labs( title = "Fuel economy falls as vehicle weight increases", x = "Weight", y = "Miles per gallon" ) + theme_minimal() ## ----r2-bad------------------------------------------------------------------- library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + labs(title = "Fuel economy by weight") + theme_minimal() ## ----r2-good------------------------------------------------------------------ library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + labs( title = "Fuel economy by vehicle weight", x = "Weight in 1000 pounds", y = "Miles per gallon" ) + theme_minimal() ## ----r3-bad------------------------------------------------------------------- library(ggplot2) sales <- data.frame( month = as.Date(c("2026-01-01", "2026-02-01", "2026-04-01", "2026-05-01")), revenue = c(12, 14, 18, 17) ) ggplot(sales, aes(month, revenue)) + geom_line() + geom_point() + labs(title = "Monthly revenue", x = "Month", y = "Revenue") + theme_minimal() ## ----r3-good------------------------------------------------------------------ library(ggplot2) sales <- data.frame( month = as.Date(c("2026-01-01", "2026-02-01", "2026-03-01", "2026-04-01", "2026-05-01")), revenue = c(12, 14, NA, 18, 17) ) ggplot(sales, aes(month, revenue)) + geom_line(na.rm = TRUE) + geom_point(size = 2, na.rm = TRUE) + annotate("text", x = as.Date("2026-03-01"), y = 15.5, label = "March missing", size = 3) + labs( title = "Monthly revenue with missing March data shown", x = "Month", y = "Revenue", caption = "March was not reported." ) + theme_minimal() ## ----r4-bad------------------------------------------------------------------- library(ggplot2) ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) + geom_point(size = 2.5) + labs(title = "Fuel economy by weight", x = "Weight", y = "Miles per gallon") + theme_minimal() ## ----r4-good------------------------------------------------------------------ library(ggplot2) ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) + geom_point(size = 2.5) + scale_colour_discrete( name = "Cylinder count", labels = c("4 cylinders", "6 cylinders", "8 cylinders") ) + labs(title = "Fuel economy by weight and cylinder count", x = "Weight", y = "Miles per gallon") + theme_minimal() ## ----r5-bad------------------------------------------------------------------- library(ggplot2) ggplot(mtcars, aes(factor(cyl), mpg)) + geom_boxplot() + labs(title = "Fuel economy by cylinders", x = "Cylinders", y = "Miles per gallon") + theme_minimal() ## ----r5-good------------------------------------------------------------------ library(ggplot2) ggplot(mtcars, aes(factor(cyl), mpg)) + geom_boxplot() + labs( title = "Fuel economy by cylinder count", subtitle = "Motor Trend road tests", x = "Cylinders", y = "Miles per gallon", caption = "Source: mtcars dataset." ) + theme_minimal() ## ----r6-bad------------------------------------------------------------------- library(ggplot2) traffic <- data.frame(day = 1:10, visits = c(40, 42, 45, 47, 52, 88, 56, 54, 53, 51)) ggplot(traffic, aes(day, visits)) + geom_line() + geom_point() + labs(title = "Daily visits", x = "Day", y = "Visits") + theme_minimal() ## ----r6-good------------------------------------------------------------------ library(ggplot2) traffic <- data.frame(day = 1:10, visits = c(40, 42, 45, 47, 52, 88, 56, 54, 53, 51)) ggplot(traffic, aes(day, visits)) + geom_line() + geom_point() + annotate("label", x = 6.8, y = 82, label = "Campaign launch", size = 3) + coord_cartesian(ylim = c(35, 94), clip = "off") + labs(title = "Daily visits increased during campaign launch", x = "Day", y = "Visits") + theme_minimal() ## ----r7-bad------------------------------------------------------------------- library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + labs(title = "Fuel economy by weight", x = "Weight", y = "Miles per gallon") + theme_minimal(base_size = 6) ## ----r7-good------------------------------------------------------------------ library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + labs(title = "Fuel economy by weight", x = "Weight", y = "Miles per gallon") + theme_minimal(base_size = 12) ## ----r8-bad------------------------------------------------------------------- library(ggplot2) segments <- data.frame( segment = c("Home", "Food", "Bills", "Health", "Auto", "Shopping"), value = c(32, 16, 21, 11, 15, 5) ) ggplot(segments, aes(segment, value, fill = segment)) + geom_col() + scale_fill_manual(values = c("#d95f02", "#e66101", "#fdb863", "#b35806", "#fdae61", "#fee0b6")) + labs(title = "Monthly spending", x = "Category", y = "Share") + theme_minimal() ## ----r8-good------------------------------------------------------------------ library(ggplot2) segments <- data.frame( segment = c("Home", "Food", "Bills", "Health", "Auto", "Shopping"), value = c(32, 16, 21, 11, 15, 5) ) ggplot(segments, aes(reorder(segment, value), value)) + geom_col(fill = "#4c78a8") + geom_text(aes(label = paste0(value, "%")), hjust = -0.15, size = 3.3) + coord_flip() + labs(title = "Monthly spending by category", x = NULL, y = "Share of spending") + theme_minimal() + theme(legend.position = "none") ## ----r9-bad------------------------------------------------------------------- library(ggplot2) ggplot(mtcars, aes(wt, mpg, colour = factor(cyl), shape = factor(cyl))) + geom_point(size = 4) + geom_line() + geom_smooth(se = FALSE) + labs(title = "Fuel economy", x = "Weight", y = "Miles per gallon") + theme_bw() + theme(panel.grid.minor = element_line(colour = "grey70")) ## ----r9-good------------------------------------------------------------------ library(ggplot2) ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) + geom_point(size = 2.4, alpha = 0.85) + labs( title = "Fuel economy by vehicle weight", x = "Weight", y = "Miles per gallon", colour = "Cylinders" ) + theme_minimal() ## ----r10-bad------------------------------------------------------------------ library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point(size = 2.2) + labs(title = "Fuel economy by weight", x = "Weight", y = "Miles per gallon") + theme_minimal() + theme( panel.grid.major = element_line(colour = "grey25", linewidth = 0.8), panel.grid.minor = element_line(colour = "grey45", linewidth = 0.6) ) ## ----r10-good----------------------------------------------------------------- library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point(size = 2.2) + labs(title = "Fuel economy by weight", x = "Weight", y = "Miles per gallon") + theme_minimal() + theme( panel.grid.major = element_line(colour = "grey88", linewidth = 0.4), panel.grid.minor = element_blank() ) ## ----r11-bad------------------------------------------------------------------ library(ggplot2) region <- data.frame(group = c("north", "SOUTH", "East", "west"), value = c(18, 24, 20, 16)) ggplot(region, aes(group, value, fill = group)) + geom_col() + scale_fill_manual(values = c("red", "grey50", "navy", "orange")) + labs(title = "Regional performance", x = "region", y = "VALUE") + theme_minimal() ## ----r11-good----------------------------------------------------------------- library(ggplot2) region <- data.frame(group = c("North", "South", "East", "West"), value = c(18, 24, 20, 16)) ggplot(region, aes(group, value)) + geom_col(fill = "#4c78a8") + labs(title = "Regional performance", x = "Region", y = "Score") + theme_minimal() ## ----r12-bad------------------------------------------------------------------ library(ggplot2) region <- data.frame(region = c("North", "South", "East", "West"), value = c(18, 24, 20, 16)) ggplot(region, aes(region, value, group = 1)) + geom_line() + geom_point(size = 2.5) + labs(title = "Regional performance", x = "Region", y = "Score") + theme_minimal() ## ----r12-good----------------------------------------------------------------- library(ggplot2) region <- data.frame(region = c("North", "South", "East", "West"), value = c(18, 24, 20, 16)) ggplot(region, aes(reorder(region, value), value)) + geom_col(fill = "#4c78a8") + coord_flip() + labs(title = "Regional performance", x = "Region", y = "Score") + theme_minimal() ## ----r13-bad------------------------------------------------------------------ library(ggplot2) revenue <- data.frame(year = 2021:2025, amount = c(1200000, 1450000, 1380000, 1700000, 1850000)) ggplot(revenue, aes(year, amount)) + geom_line() + geom_point() + labs(title = "Revenue trend", x = "Year", y = "Revenue") + theme_minimal() ## ----r13-good----------------------------------------------------------------- library(ggplot2) revenue <- data.frame(year = 2021:2025, amount = c(1200000, 1450000, 1380000, 1700000, 1850000)) ggplot(revenue, aes(year, amount / 1e6)) + geom_line() + geom_point() + scale_x_continuous(breaks = revenue$year) + labs(title = "Revenue trend", x = "Year", y = "Revenue, millions") + theme_minimal() ## ----r14-bad------------------------------------------------------------------ library(ggplot2) set.seed(7) dense <- data.frame(x = rnorm(900), y = rnorm(900)) ggplot(dense, aes(x, y)) + geom_point() + labs(title = "Customer scores", x = "Score A", y = "Score B") + theme_minimal() ## ----r14-good----------------------------------------------------------------- library(ggplot2) set.seed(7) dense <- data.frame(x = rnorm(900), y = rnorm(900)) ggplot(dense, aes(x, y)) + geom_point(alpha = 0.25, size = 1.6) + labs(title = "Customer scores with point density visible", x = "Score A", y = "Score B") + theme_minimal() ## ----r15-bad------------------------------------------------------------------ library(ggplot2) trend <- data.frame( year = rep(2021:2025, 3), product = rep(c("Basic", "Plus", "Pro"), each = 5), revenue = c(10, 12, 14, 15, 17, 8, 11, 13, 16, 20, 6, 8, 12, 18, 24) ) ggplot(trend, aes(year, revenue, colour = product)) + geom_line(linewidth = 1) + labs(title = "Revenue by product", x = "Year", y = "Revenue") + theme_minimal() ## ----r15-good----------------------------------------------------------------- library(ggplot2) trend <- data.frame( year = rep(2021:2025, 3), product = rep(c("Basic", "Plus", "Pro"), each = 5), revenue = c(10, 12, 14, 15, 17, 8, 11, 13, 16, 20, 6, 8, 12, 18, 24) ) end_labels <- trend[trend$year == 2025, ] ggplot(trend, aes(year, revenue, colour = product)) + geom_line(linewidth = 1) + geom_text(data = end_labels, aes(label = product), hjust = -0.1, show.legend = FALSE) + scale_x_continuous(breaks = 2021:2025, limits = c(2021, 2025.8)) + labs(title = "Revenue by product", x = "Year", y = "Revenue") + theme_minimal() + theme(legend.position = "none") ## ----r16-bad------------------------------------------------------------------ library(ggplot2) trend <- data.frame(year = rep(2021:2025, 3), product = rep(c("Basic", "Plus", "Pro"), each = 5), revenue = c(10, 12, 14, 15, 17, 8, 11, 13, 16, 20, 6, 8, 12, 18, 24)) ggplot(trend, aes(year, revenue, colour = product)) + geom_line(linewidth = 1.1) + labs(title = "Revenue by product", x = "Year", y = "Revenue") + theme_minimal() ## ----r16-good----------------------------------------------------------------- library(ggplot2) trend <- data.frame(year = rep(2021:2025, 3), product = rep(c("Basic", "Plus", "Pro"), each = 5), revenue = c(10, 12, 14, 15, 17, 8, 11, 13, 16, 20, 6, 8, 12, 18, 24)) ggplot(trend, aes(year, revenue, group = product)) + geom_line(data = subset(trend, product != "Pro"), colour = "grey70", linewidth = 0.8) + geom_line(data = subset(trend, product == "Pro"), colour = "#8a3f24", linewidth = 1.4) + labs(title = "Pro revenue is accelerating", x = "Year", y = "Revenue") + theme_minimal() ## ----r17-bad------------------------------------------------------------------ library(ggplot2) approval <- data.frame(month = 1:6, score = c(71, 72, 72, 73, 74, 75)) ggplot(approval, aes(month, score)) + geom_line() + geom_point() + coord_cartesian(ylim = c(70, 76)) + labs(title = "Approval score", x = "Month", y = "Score") + theme_minimal() ## ----r17-good----------------------------------------------------------------- library(ggplot2) approval <- data.frame(month = 1:6, score = c(71, 72, 72, 73, 74, 75)) ggplot(approval, aes(month, score)) + geom_line() + geom_point() + coord_cartesian(ylim = c(0, 100)) + labs(title = "Approval score rose modestly", x = "Month", y = "Score out of 100") + theme_minimal() ## ----r18-bad------------------------------------------------------------------ library(ggplot2) teams <- data.frame(team = c("A", "B", "C"), score = c(92, 95, 97)) ggplot(teams, aes(team, score)) + geom_col(fill = "#4c78a8") + coord_cartesian(ylim = c(90, 98)) + labs(title = "Team scores", x = "Team", y = "Score") + theme_minimal() ## ----r18-good----------------------------------------------------------------- library(ggplot2) teams <- data.frame(team = c("A", "B", "C"), score = c(92, 95, 97)) ggplot(teams, aes(team, score)) + geom_col(fill = "#4c78a8") + coord_cartesian(ylim = c(0, 100)) + labs(title = "Team scores", x = "Team", y = "Score out of 100") + theme_minimal() ## ----r19-bad------------------------------------------------------------------ library(ggplot2) markets <- data.frame(market = c("A", "B", "C", "D"), share = c(12, 18, 24, 30)) ggplot(markets, aes(market, 1, size = share)) + geom_point(colour = "#8a3f24", alpha = 0.7) + scale_size(range = c(6, 28)) + labs(title = "Market share", x = "Market", y = NULL, size = "Share") + theme_minimal() ## ----r19-good----------------------------------------------------------------- library(ggplot2) markets <- data.frame(market = c("A", "B", "C", "D"), share = c(12, 18, 24, 30)) ggplot(markets, aes(reorder(market, share), share)) + geom_col(fill = "#4c78a8") + coord_flip() + labs(title = "Market share", x = "Market", y = "Share (%)") + theme_minimal() ## ----r20-bad------------------------------------------------------------------ library(ggplot2) orders <- data.frame(order = 1:12, value = c(22, 24, 20, 23, 25, 21, 24, 26, 22, 23, 25, 80)) ggplot(subset(orders, value < 50), aes(order, value)) + geom_point(size = 2.5) + labs(title = "Order values", x = "Order", y = "Value") + theme_minimal() ## ----r20-good----------------------------------------------------------------- library(ggplot2) orders <- data.frame(order = 1:12, value = c(22, 24, 20, 23, 25, 21, 24, 26, 22, 23, 25, 80)) ggplot(orders, aes(order, value)) + geom_point(size = 2.5) + annotate("label", x = 12, y = 80, label = "Bulk order", hjust = 1.05, size = 3) + labs(title = "Order values with bulk order shown", x = "Order", y = "Value") + theme_minimal() ## ----r21-bad------------------------------------------------------------------ library(ggplot2) share <- data.frame(group = c("Small", "Medium", "Large"), value = c(10, 20, 40)) ggplot(share, aes(group, value, size = group)) + geom_point(colour = "#8a3f24") + scale_size_manual(values = c(8, 18, 30)) + labs(title = "Group size", x = "Group", y = "Value") + theme_minimal() ## ----r21-good----------------------------------------------------------------- library(ggplot2) share <- data.frame(group = c("Small", "Medium", "Large"), value = c(10, 20, 40)) ggplot(share, aes(group, value)) + geom_col(fill = "#4c78a8") + labs(title = "Group size", x = "Group", y = "Value") + theme_minimal() ## ----r22-bad------------------------------------------------------------------ library(ggplot2) estimate <- data.frame(group = c("A", "B", "C"), mean = c(10, 13, 15), low = c(8, 10, 12), high = c(12, 16, 18)) ggplot(estimate, aes(group, mean)) + geom_point(size = 3) + labs(title = "Average outcome by group", x = "Group", y = "Mean outcome") + theme_minimal() ## ----r22-good----------------------------------------------------------------- library(ggplot2) estimate <- data.frame(group = c("A", "B", "C"), mean = c(10, 13, 15), low = c(8, 10, 12), high = c(12, 16, 18)) ggplot(estimate, aes(group, mean)) + geom_errorbar(aes(ymin = low, ymax = high), width = 0.15) + geom_point(size = 3, colour = "#4c78a8") + labs(title = "Average outcome by group with uncertainty", x = "Group", y = "Mean outcome") + theme_minimal() ## ----r23-bad------------------------------------------------------------------ library(ggplot2) index <- data.frame(year = 2017:2026, value = c(120, 118, 114, 110, 112, 116, 119, 123, 126, 128)) ggplot(subset(index, year >= 2021), aes(year, value)) + geom_line() + geom_point() + labs(title = "Index is rising", x = "Year", y = "Index") + theme_minimal() ## ----r23-good----------------------------------------------------------------- library(ggplot2) index <- data.frame(year = 2017:2026, value = c(120, 118, 114, 110, 112, 116, 119, 123, 126, 128)) ggplot(index, aes(year, value)) + geom_line() + geom_point() + scale_x_continuous(breaks = 2017:2026) + labs(title = "Index recovered after an earlier decline", x = "Year", y = "Index") + theme_minimal() ## ----r24-bad------------------------------------------------------------------ library(ggplot2) survey <- data.frame(wave = c("Wave 1", "Wave 2", "Wave 3", "Wave 4"), score = c(62, 66, 65, 70)) ggplot(survey, aes(wave, score, group = 1)) + geom_line() + geom_point(size = 2.5) + labs(title = "Survey score by wave", x = "Wave", y = "Score") + theme_minimal() ## ----r24-good----------------------------------------------------------------- library(ggplot2) survey <- data.frame(wave = c("Wave 1", "Wave 2", "Wave 3", "Wave 4"), score = c(62, 66, 65, 70)) ggplot(survey, aes(wave, score)) + geom_point(size = 3, colour = "#4c78a8") + labs(title = "Survey score by wave", x = "Survey wave", y = "Score") + theme_minimal() ## ----r25-bad------------------------------------------------------------------ library(ggplot2) costs <- data.frame(year = 2021:2025, cost = c(40, 42, 45, 48, 51)) ggplot(costs, aes(year, cost)) + geom_line() + geom_point() + labs(title = "Costs are exploding", x = "Year", y = "Cost") + theme_minimal() ## ----r25-good----------------------------------------------------------------- library(ggplot2) costs <- data.frame(year = 2021:2025, cost = c(40, 42, 45, 48, 51)) ggplot(costs, aes(year, cost)) + geom_line() + geom_point() + scale_x_continuous(breaks = costs$year) + labs(title = "Costs increased from 2021 to 2025", x = "Year", y = "Cost") + theme_minimal()