Qatar Cars Dataset with ggbeeswarm

Using geom_beeswarm() for visualization of price distributions across vehicle types

#TidyTuesday
{ggbeeswarm}
Author

Aditya Dahiya

Published

December 22, 2025

About the Data

The Qatar Cars dataset provides a modern, internationally-focused alternative to classic automotive datasets like mtcars and mpg. Collected in early 2025 by Paul Musgrave and students in his international politics course at Georgetown University in Qatar, this dataset addresses “U.S. defaultism” in data science education by using International System (SI) units, featuring globally diverse car manufacturers including Chinese brands, and incorporating modern vehicle types like electric and hybrid cars. The dataset includes 2025 prices in Qatari Riyals (QAR), with exchange rates of 1 USD = 3.64 QAR and 1 EUR = 4.15 QAR at the time of collection. Available through the {qatarcars} R package and as part of #TidyTuesday, the dataset contains 15 variables covering car specifications (dimensions in meters, mass in kilograms), performance metrics (horsepower, 0-100 km/h acceleration time), economic factors (fuel economy in liters per 100 km, price), and categorical information (origin country, engine type, vehicle type).

Figure 1: The graphic displays price distribution of 105 cars across four vehicle types (Coupe, Hatchback, Sedan, SUV) in Qatar’s 2025 market. Each dot represents one car, with colors indicating engine type: petrol (orange), hybrid (green), or electric (blue). The vertical axis shows prices in Qatari Riyals on a logarithmic scale, ranging from 30,000 to over 30 million QAR. The beeswarm arrangement prevents dot overlap while revealing price clustering patterns within each category.

How I Made This Graphic

.

Loading required libraries

Code
pacman::p_load(
  tidyverse, # All things tidy

  scales, # Nice Scales for ggplot2
  fontawesome, # Icons display in ggplot2
  ggtext, # Markdown text support for ggplot2
  showtext, # Display fonts in ggplot2
  colorspace, # Lighten and Darken colours

  patchwork  # Composing Plots
)

pacman::p_load(ggbeeswarm)

qatarcars <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/main/data/2025/2025-12-09/qatarcars.csv')

Visualization Parameters

Code
# Font for titles
font_add_google("Saira",
  family = "title_font"
)

# Font for the caption
font_add_google("Saira Condensed",
  family = "body_font"
)

# Font for plot text
font_add_google("Saira Extra Condensed",
  family = "caption_font"
)

showtext_auto()

# A base Colour
bg_col <- "white"
seecolor::print_color(bg_col)

# Colour for highlighted text
text_hil <- "grey40"
seecolor::print_color(text_hil)

# Colour for the text
text_col <- "grey30"
seecolor::print_color(text_col)

line_col <- "grey30"

# Define Base Text Size
bts <- 80

mypal <- paletteer::paletteer_d("ltc::trio3")[c(2, 3, 1)]

# Caption stuff for the plot
sysfonts::font_add(
  family = "Font Awesome 6 Brands",
  regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf")
)
github <- "&#xf09b"
github_username <- "aditya-dahiya"
xtwitter <- "&#xe61b"
xtwitter_username <- "@adityadahiyaias"
social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color: {text_hil}'>{github_username}  </span>")
social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color: {text_hil}'>{xtwitter_username}</span>")
plot_caption <- paste0(
  "**Data:**  Georgetown University in Qatar | Paul Musgrave",
  " |  **Code:** ",
  social_caption_1,
  " |  **Graphics:** ",
  social_caption_2
)
rm(
  github, github_username, xtwitter,
  xtwitter_username, social_caption_1,
  social_caption_2
)

plot_title <- "<span style='color:#E8A628;'>**Petrol**</span> <span style='color:grey30;'>Dominates,</span> <span style='color:#3EB595;'>**Hybrids**</span> <span style='color:grey30;'>Rise,<br>and</span> <span style='color:#5AADE2;'>**Electric**</span> <span style='color:grey30;'>Stays Premium</span>"

plot_subtitle <- "<span style='color:grey40;'>In Qatar's 2025 car market, <span style='color:#E8A628;'>**petrol vehicles**</span> span all price<br>ranges across every category. <span style='color:#3EB595;'>**Hybrids**</span> cluster in mid-to-high<br>tiers, particularly in SUVs and sedans, while <span style='color:#5AADE2;'>**electric cars**</span><br>command premium prices, especially in the coupe<br>and SUV segments.</span>"

Exploratory Data Analysis and Wrangling

Code
bts = 80

plotdf <- qatarcars |> 
  left_join(
    qatarcars |> 
      slice_max(price, n = 15) |> 
      mutate(label_var = paste(model, "\n",make))

  ) |> 
  left_join(
    qatarcars |> 
      slice_min(price, n = 15) |> 
      mutate(label_var = paste(model, "\n",make))

  )

The Plot

Code
bts <- 90

# Create the beeswarm plot
g <- plotdf |> 
  ggplot(
    mapping = aes(
      x = type, 
      y = price, 
      color = enginetype
    )
  ) +
  geom_beeswarm(
    alpha = 0.9, 
    cex = 3,
    pch = 20
  ) +
  geom_text(
    mapping = aes(label = label_var),
    position = position_beeswarm(
      cex = 3
    ),
    family = "caption_font",
    size = bts / 3,
    check_overlap = TRUE,
    hjust = -0.1,
    vjust = -0.2,
    lineheight = 0.25
  ) +
  scale_y_continuous(
    trans = "log10",
    labels = label_number(scale_cut = cut_short_scale()),
    n.breaks = 15
  ) +
  annotate(
    geom = "richtext",
    x = Inf, y = 30e6,
    hjust = 1, vjust = 1,
    label = plot_title,
    size = bts / 1.2,
    label.size = NA,
    family = "body_font",
    lineheight = 0.45,
    fill = alpha(bg_col, 0.3)
  ) +
  annotate(
    geom = "richtext",
    x = Inf, y = 10e6,
    hjust = 1, vjust = 1,
    label = plot_subtitle,
    size = bts / 2.5,
    label.size = NA,
    family = "body_font",
    lineheight = 0.3,
    fill = alpha(bg_col, 0.3)
  ) +
  scale_size_continuous(
    range = c(2, 10),
    name = "Horsepower"
  ) +
  scale_colour_manual(values = mypal) +
  labs(
    title = NULL,
    subtitle = NULL,
    x = NULL,
    y = "Price (in Qatari Riyal)",
    caption = plot_caption
  ) +
  theme_minimal(
    base_family = "body_font",
    base_size = bts
  ) +
  theme(
    legend.position = "inside",
    legend.position.inside = c(1,0.7),
    legend.justification = c(1,1),
    legend.direction = "horizontal",
    legend.margin = margin(0,0,0,0, "mm"),
    legend.title = element_text(
      margin = margin(0,0,0,0, "mm"),
      hjust = 0.5
    ),
    legend.text = element_text(
      margin = margin(0,0,0,0, "mm")
    ),
    legend.text.position = "right",
    legend.title.position = "top",
    
    # Overall
    text = element_text(
      margin = margin(0, 0, 0, 0, "mm"),
      colour = text_hil,
      lineheight = 0.3
    ),
    
    # Axes
    axis.text.x.bottom = element_text(
      margin = margin(4,0,0,0, "mm"),
      size = bts * 1.5,
      family = "caption_font"
    ),
    axis.text.y.left = element_text(
      size = bts,
      margin = margin(0,4,0,0, "mm")
    ),
    axis.title.y.left = element_text(
      margin = margin(0,0,0,0, "mm"),
      size = bts * 1.5
    ),
    axis.ticks.x.bottom = element_blank(),
    axis.ticks.y.left = element_blank(),
    axis.ticks.length.x = unit(0, "mm"),
    axis.ticks.length.y.left = unit(0, "mm"),
    axis.line = element_blank(),
    panel.grid = element_blank(),
    panel.grid.major.y = element_line(
      linetype = 3,
      linewidth = 0.3,
      colour = line_col
    ),
    plot.caption = element_markdown(
      family = "caption_font",
      hjust = 0.5,
      margin = margin(5,0,0,0, "mm"),
      colour = text_hil
    ),
    plot.caption.position = "plot",
    plot.title.position = "plot",
    plot.margin = margin(5, 15, 5, 5, "mm")
  )

ggsave(
  filename = here::here(
    "data_vizs",
    "tidy_qatar_cars.png"
  ),
  plot = g,
  width = 400,
  height = 500,
  units = "mm",
  bg = bg_col
)

Savings the thumbnail for the webpage

Code
# Saving a thumbnail

library(magick)

# Saving a thumbnail for the webpage
image_read(here::here(
  "data_vizs",
  "tidy_qatar_cars.png"
)) |>
  image_resize(geometry = "x400") |>
  image_write(
    here::here(
      "data_vizs",
      "thumbnails",
      "tidy_qatar_cars.png"
    )
  )

Session Info

Code
pacman::p_load(
  tidyverse, # All things tidy

  scales, # Nice Scales for ggplot2
  fontawesome, # Icons display in ggplot2
  ggtext, # Markdown text support for ggplot2
  showtext, # Display fonts in ggplot2
  colorspace, # Lighten and Darken colours

  patchwork,  # Composing Plots
  ggh4x,      # Proportional facets
  ggchicklet, # Rounded bars
  ggstream   # Labels in stacked bar chart
)

sessioninfo::session_info()$packages |>
  as_tibble() |>
  
  # The attached column is TRUE for packages that were 
  # explicitly loaded with library()
  dplyr::filter(attached == TRUE) |>
  dplyr::select(package,
    version = loadedversion,
    date, source
  ) |>
  dplyr::arrange(package) |>
  janitor::clean_names(
    case = "title"
  ) |>
  gt::gt() |>
  gt::opt_interactive(
    use_search = TRUE
  ) |>
  gtExtras::gt_theme_espn()
Table 1: R Packages and their versions used in the creation of this page and graphics

Links