Cranes

Observations de grues en Suède
Author

Félix Marchais

Published

October 7, 2025

Cette semaine, le jeu de données porte sur l’observation de grues (les oiseaux) au lac Hornborgasjön, en Suède, entre 1994 et 2025.

L’objectif de ma visualisation est de mettre en évidence la saisonnalité des observations ainsi que leur augmentation au fil des ans. L’année la plus récente (2025) est indiquée en blanc.

Le code pour obtenir ce graphe :


##### SETUP ######
library(tidytuesdayR)
library(lubridate)
library(ggplot2)
library(dplyr)
library(scales)
library(grid)

# For months names
Sys.setlocale("LC_TIME", "English")





##### DATA #####
tuesdata <- tidytuesdayR::tt_load('2025-09-30')
cranes <- tuesdata$cranes


##### CLEANING ######
clean_cranes <- cranes %>%
  mutate(
    year = year(date),
    date = as.Date(sprintf("2025-%02d-%02d", month(date), day(date))),
    month_day = format(date, "%b-%d")
  ) %>%
  filter(!is.na(date) & !is.na(observations))

# determine the most recent year to highlight it
latest_year <- max(clean_cranes$year, na.rm = TRUE)

# Get the "next" 10.000 after maximal observation number
y_limit <- ((max(clean_cranes$observations, na.rm = TRUE) %/% 10000) + 1) * 10000




#####PLOT #####
clean_cranes %>%
  ggplot(aes(x = date, y = observations, group = factor(year), color = year)) +

  # light, semi-transparent lines per year
  geom_line(alpha = 0.35, linewidth = 0.6) +

  # points sized by observation count (smaller range so they don't dominate)
  geom_point(aes(size = observations), alpha = 0.6, show.legend = FALSE) +
  scale_size(range = c(0.8, 3.5)) +

  # highlight the most recent year with a bold, bright line and points on top
  geom_line(
    data = filter(clean_cranes, year == latest_year),
    aes(x = date, y = observations, group = factor(year)),
    color = "#FFFFFF",
    size = 1.2,
    inherit.aes = FALSE
  ) +
  geom_point(
    data = filter(clean_cranes, year == latest_year),
    aes(x = date, y = observations),
    color = "#FFFFFF",
    size = 2,
    inherit.aes = FALSE
  ) +

  # continuous color gradient for the year variable using the viridis-like ramp
   scale_color_viridis_c(guide = guide_colorbar(title = "Year", barwidth = unit(6, "cm"))) +

  # radial (y) axis - keep the scale available for plotting but don't show the default radial text
  scale_y_continuous(
    limits = c(0, y_limit),
    expand = c(0, 0),
    breaks = c(0,10000,20000),
    labels = c("0","10k","20k")
  ) +

  # show months around the circle
  scale_x_date(
    breaks = seq(as.Date("2025-01-01"), as.Date("2025-12-01"), by = "1 month"),
    labels = function(x) format(x, "%b"),
    limits = c(as.Date("2025-01-01"), as.Date("2025-12-31"))
  ) +

  # polar coordinates with January at top
  coord_polar(start = -pi/2) +

  # labels
  labs(
    title = "Annual Pattern of Crane Observations",
    subtitle = "Each year mapped onto a single calendar",
    caption = "Data: Tidytuesday | Viz: @fmarchais",
    x = NULL,
    y = NULL
  ) +

  # revert the earlier radial-label tweak (remove visible radial labels in the margin)
  theme(
    panel.background = element_rect(fill = "#0f1115", colour = NA),
    plot.background = element_rect(fill = "#0f1115", colour = NA),
    legend.background = element_rect(fill = "#0f1115", colour = NA),
    legend.key = element_rect(fill = NA, colour = NA),
    text = element_text(color = "white"),
    plot.title = element_text(size = 12, face = "bold", color = "white"),
    plot.subtitle = element_text(size = 9, color = "grey90"),
    plot.caption = element_text(size = 9, color = "grey70"),

    # make y & x axis more visible
    axis.text.y = element_text(color = "white", size = 10, margin = margin(r = 6), hjust = 0.5),
    axis.ticks.y = element_line(color = "white", size = 0.5),
    axis.ticks.length = unit(3, "pt"),
    axis.text.x = element_text(color = "grey95", size = 11),

    # remove the "net" in the background (radial grid)
    panel.grid.major.y = element_blank(),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_blank(),
    plot.margin = margin(10, 10, 10, 10),

    legend.position = "bottom"
  )


# ggsave(filename = "my_plot.png", dpi = "retina")