Skip to content

Instantly share code, notes, and snippets.

@bjulius
Created March 21, 2025 04:46
Show Gist options
  • Save bjulius/e4ab68e67c982e6c00dfe3e7e216d9ae to your computer and use it in GitHub Desktop.
Save bjulius/e4ab68e67c982e6c00dfe3e7e216d9ae to your computer and use it in GitHub Desktop.
R Arrow Chart Code
# Load necessary libraries
library(ggplot2)
library(dplyr)
library(tidyr)
library(gridExtra)
library(grid)
# Create the dataframe with the data from the image
sales_data <- data.frame(
Country = c("Finland", "Finland", "France", "France", "Netherlands", "Netherlands",
"Norway", "Norway", "Poland", "Poland"),
Year = c(2023, 2024, 2023, 2024, 2023, 2024, 2023, 2024, 2023, 2024),
TotalSales = c(160994, 186026, 360184, 652563, 818001, 400207,
272589, 157921, 943896, 1186120)
)
# Calculate changes from 2023 to 2024
sales_summary <- sales_data %>%
pivot_wider(names_from = Year, values_from = TotalSales) %>%
mutate(
Change_K = (`2024` - `2023`) / 1000, # Convert to $K
Change_Percent = (`2024` - `2023`) / `2023` * 100
)
# Set breaks for x-axes
k_breaks <- seq(-400, 300, 100)
pct_breaks <- seq(-40, 80, 20)
# Create plot for Change in $K
p1 <- ggplot(sales_summary) +
geom_segment(
aes(x = 0, y = Country, xend = Change_K, yend = Country, color = Change_K >= 0),
arrow = arrow(length = unit(0.3, "cm"), type = "closed"),
size = 1
) +
geom_point(aes(x = 0, y = Country), color = "darkgrey", size = 3) +
scale_color_manual(values = c("FALSE" = "#F44336", "TRUE" = "#4CAF50")) +
scale_x_continuous(
breaks = k_breaks,
labels = function(x) paste0("$", x)
) +
labs(title = "Change in $K", x = NULL, y = NULL) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 12),
legend.position = "none",
panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank(),
axis.text.y = element_text(size = 10)
)
# Create plot for Change in %
p2 <- ggplot(sales_summary) +
geom_segment(
aes(x = 0, y = Country, xend = Change_Percent, yend = Country, color = Change_Percent >= 0),
arrow = arrow(length = unit(0.3, "cm"), type = "closed"),
size = 1
) +
geom_point(aes(x = 0, y = Country), color = "darkgrey", size = 3) +
scale_color_manual(values = c("FALSE" = "#F44336", "TRUE" = "#4CAF50")) +
scale_x_continuous(
breaks = pct_breaks,
labels = function(x) paste0(x, "%")
) +
labs(title = "Change in %", x = NULL, y = NULL) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 12),
legend.position = "none",
panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank(),
axis.text.y = element_blank() # Remove y-axis text for the second plot
)
# Combine the plots with a shared title
grid.arrange(
p1, p2,
ncol = 2,
widths = c(1, 1),
top = textGrob(
"Change in Sales by Country from 2023 to 2024",
gp = gpar(fontsize = 16, fontface = "bold")
)
)
@bjulius
Copy link
Author

bjulius commented Mar 21, 2025

This is the code I generated by " vibe coding" in Claude Sonnet 3.7 extended.

Here's the prompt I used:

For the attached dataset, write the R code using ggplot2 to produce an arrow chart showing the changes in absolute and percentage sales by country from 2023 to 2024.

Style the chart as closely as possible to the attached screenshot - use the same fonts, colors, spacing, title podsition and style, axes (but substitute country for region), and have both charts share the same y-axis labels.

And this is the resulting chart:
image

@bjulius
Copy link
Author

bjulius commented Mar 21, 2025

Nick had some suggested edits. Here is the revised code:

Load necessary libraries

library(ggplot2)
library(dplyr)
library(tidyr)
library(gridExtra)
library(grid)

Create the dataframe with the data

sales_data <- data.frame(
Country = c("Finland", "Finland", "France", "France", "Netherlands", "Netherlands",
"Norway", "Norway", "Poland", "Poland"),
Year = c(2023, 2024, 2023, 2024, 2023, 2024, 2023, 2024, 2023, 2024),
TotalSales = c(160994, 186026, 360184, 652563, 818001, 400207, 272589, 157921,
943896, 1208230)
)

Calculate changes and sort countries by 2023 sales in descending order

sales_summary <- sales_data %>%
pivot_wider(names_from = Year, values_from = TotalSales) %>%
mutate(
Change_K = (2024 - 2023) / 1000, # Convert to $K
Change_Percent = (2024 - 2023) / 2023 * 100
) %>%
arrange(desc(2023)) # Sort by 2023 sales in descending order

Create an explicit vector of countries in descending order of 2023 sales

ordered_countries <- sales_summary$Country

Set the factor levels explicitly to ensure correct ordering

sales_summary$Country <- factor(sales_summary$Country, levels = ordered_countries)

Set breaks for x-axes with adjusted ranges

k_breaks <- seq(-600, 600, 100)
pct_breaks <- seq(-60, 100, 20)

Create plot for nominal values (but keep "Change in $K" title)

p1 <- ggplot(sales_summary) +
geom_segment(
aes(x = 2023, y = Country, xend = 2024, yend = Country,
color = 2024 >= 2023),
arrow = arrow(length = unit(0.12, "cm"), type = "open"),
size = 0.8
) +
geom_point(aes(x = 2023, y = Country, color = 2024 >= 2023), size = 2) +
scale_color_manual(values = c("FALSE" = "#F44336", "TRUE" = "#4CAF50")) +
scale_x_continuous(
breaks = seq(0, 1300000, 200000),
labels = function(x) paste0("$", x/1000, "K"),
limits = c(0, 1300000) # Ensure no values extend off chart
) +
scale_y_discrete(limits = rev(ordered_countries)) +
labs(title = "Change in $K", x = NULL, y = NULL) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 12),
legend.position = "none",
panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank(),
axis.text.y = element_text(size = 10),
axis.text.x = element_text(size = 6) # Even smaller x-axis labels
)

Create plot for Change in %

p2 <- ggplot(sales_summary) +
geom_segment(
aes(x = 0, y = Country, xend = Change_Percent, yend = Country,
color = Change_Percent >= 0),
arrow = arrow(length = unit(0.12, "cm"), type = "open"),
size = 0.8
) +
geom_point(aes(x = 0, y = Country, color = Change_Percent >= 0), size = 2) +
scale_color_manual(values = c("FALSE" = "#F44336", "TRUE" = "#4CAF50")) +
scale_x_continuous(
breaks = pct_breaks,
labels = function(x) paste0(x, "%"),
limits = c(-60, 100) # Extended axis from -60% to 100%
) +
scale_y_discrete(limits = rev(ordered_countries)) +
labs(title = "Change in %", x = NULL, y = NULL) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 12),
legend.position = "none",
panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_text(size = 6) # Even smaller x-axis labels
)

Combine the plots with a shared title

Extended width by 30% by adjusting the relative widths

grid.arrange(
p1, p2,
ncol = 2,
widths = c(1.3, 1.3), # Increased widths by 30%
top = textGrob(
"Change in Sales by Country from 2023 to 2024",
gp = gpar(fontsize = 16, fontface = "bold")
)
)

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment