Bevölkerung (DEU) · 🇩🇪

Autor:in

Holger Döring

Veröffentlichungsdatum

18. Mai 2025

Code
library(conflicted) # create errors for function name conflicts

library(tidyverse)
conflicts_prefer(dplyr::filter, .quiet = TRUE)
library(knitr) # layout // tables in RMarkdown with kable()
ggplot2::theme_set(theme_minimal())
options(scipen = 99999) # suppress scientific notation

library(patchwork) # plots  // arrange
library(reactable) # layout // interactive tables
library(sf) # geo // spatial data

round_first_two <- function(x) {
  scale <- 10^ceiling(log10(x))
  round(x / scale, 2) * scale
}

Deutschland-Karte · 🗺️

Code
cen_raw <- read_csv("data/1000A-0000_de_clean.csv", show_col_types = FALSE)

map_deu <- read_rds("data/r-ne_states-deu.rds")

map_met <-
  read_rds("data/r-metro-region-gecoding_sf.rds") |>
  mutate(region_search = str_remove(region_search, " .*")) |>
  filter(year_2023 >= 1000000)

map_place <-
  read_rds("data/r-census-gecoding_sf.rds") |>
  inner_join(select(cen_raw, ags, population), by = "ags") |>
  mutate(
    pop_1M = population / 1000000,
    pop_1000 = ifelse(population >= 100000, TRUE, FALSE)
  ) |>
  select(-state_name)

Deutschland · 🇩🇪

  • Metropol-Regionen — mehr als 1 Mio. Einwohner (beschriftet)
  • Großstädte — mehr als 100,000 Einwohner (grün)
  • Städte — mehr als 25,000 Einwohner (rot)
  • Gemeinden — mehr als 10,000 Einwohner (grau)
Code
pl_dt <-
  map_place |>
  mutate(
    pop_25 = if_else(population >= 25000, pop_1M, NA),
    pop_10 = if_else(population < 25000, pop_1M, NA)
  )

pl <-
  ggplot() +
  geom_sf(data = map_deu, colour = "grey85", lwd = 0.3) +
  geom_sf(data = pl_dt, aes(size = pop_25, colour = pop_1000), alpha = 0.4) +
  geom_sf(data = pl_dt, aes(colour = pop_10), colour = "grey", alpha = 0.2) +
  coord_sf(crs = "EPSG:4839") + # LCC projection Germany
  ggrepel::geom_label_repel(
    data = map_met,
    aes(label = region_search, geometry = geometry),
    stat = "sf_coordinates",
    alpha = 0.7,
    min.segment.length = 0,
    size = 3
  ) +
  scale_size_continuous(trans = "log") +
  labs(caption = "Quellen: Zensus 2022 und Eurostat") +
  guides(alpha = "none", color = "none", size = "none") +
  theme_void()

ggsave("z-staedte-2022.png", pl, width = 8, height = 6)
pl

NRW Metropolen · 📍

Metropol-Regionen Ruhrgebiet, Düsseldorf und Köln

Code
create_sf_rectangle <- function(xmin, xmax, ymin, ymax, crs = "EPSG:4326") {
  matrix(
    c(xmin, ymin, xmax, ymin, xmax, ymax, xmin, ymax, xmin, ymin),
    ncol = 2, byrow = TRUE
  ) |>
    list() |>
    st_polygon() |>
    st_sfc(crs = crs)
}

nrw_select <- create_sf_rectangle(6.5, 7.9, 50.9, 51.7)
Code
map_nrw_ruhr <-
  map_deu |>
  st_intersection(nrw_select)

map_place_nrw <-
  map_place |>
  st_intersection(map_nrw_ruhr) |>
  mutate(
    place_1000 = if_else(population >= 100000,
      str_remove(place, ",.*"),
      NA_character_
    ),
    place_1000 = str_remove(place_1000, " an der Ruhr")
  )

ggplot() +
  geom_sf(data = map_nrw_ruhr, colour = "grey85", lwd = 0.3) +
  geom_sf(data = map_place_nrw, aes(size = pop_1M, colour = pop_1000), alpha = 0.3) +
  coord_sf(crs = "EPSG:4839") + # LCC projection Germany
  ggrepel::geom_label_repel(
    data = map_place_nrw,
    aes(label = place_1000, geometry = geometry),
    stat = "sf_coordinates",
    alpha = 0.7,
    min.segment.length = 0,
    size = 2.5
  ) +
  scale_size_continuous(trans = "log") +
  labs(caption = "Quelle: Zensus 2022") +
  guides(alpha = "none", color = "none", size = "none") +
  theme_void()

Bevölkerungszahl · 📈

Zensus 2022 · Deutschland · 🇩🇪

Code
pop <-
  cen_raw |>
  arrange(-population) |>
  mutate(
    place = str_remove(place, ",.*"),
    rank = row_number(),
    cum_population = cumsum(population),
  ) |>
  relocate(state, rank, .after = place)

Städte / Gemeinden · 🏘

Code
pop_tsd_all <-
  pop |>
  mutate(
    pop_category = case_when(
      population >= 1000000 ~ 1000000,
      population >= 500000 ~ 500000,
      population >= 100000 ~ 100000,
      population >= 50000 ~ 50000,
      population >= 25000 ~ 25000,
      population >= 10000 ~ 10000,
      population >= 5000 ~ 5000,
      population >= 1000 ~ 1000,
      population >= 0 ~ 100,
      .default = NA
    )
  )

pop_tsd <-
  pop_tsd_all |>
  summarise(
    n = n(),
    pop_median = median(population),
    pop_sum = sum(population),
    .by = pop_category
  ) |>
  mutate(
    n_cum = cumsum(n),
    pop_cum = cumsum(pop_sum),
  ) |>
  relocate(n_cum, .after = n) |>
  arrange(desc(pop_category))
Code
pl_dt <-
  pop_tsd |>
  mutate(pop_category = fct_rev(fct_inorder(as.character(pop_category))))

pl1 <-
  ggplot(pl_dt, aes(x = pop_sum, y = pop_category)) +
  geom_col(fill = "grey85") +
  scale_x_continuous(
    breaks = c(5000000, 10000000, 15000000),
    labels = c("5M", "10M", "15M")
  ) +
  labs(x = "", y = "")

pl2 <-
  ggplot(pl_dt, aes(x = pop_cum, y = pop_category)) +
  geom_col(fill = "grey85") +
  scale_x_continuous(
    breaks = c(20000000, 40000000, 60000000),
    labels = c("20M", "40M", "60M")
  ) +
  theme(axis.text.y = element_blank()) +
  labs(x = "", y = "")

pl1 + pl2 +
  plot_annotation(caption = "Quelle: Zensus 2022") +
  plot_layout(widths = c(3, 2))

  • Kategorie — Bevölkerungskategorie (z.B. 25.000 – 50.000)
  • n — Anzahl der Städte/Gemeinden
  • n_cum — Kumulierte Anzahl der Städte/Gemeinden
  • median — Median der Bevölkerungszahlen
  • sum_1M — Summe der Bevölkerungszahlen (1 Mio.)
  • cum_1M — Kumulierte Summe der Bevölkerungszahlen (1 Mio.)
Code
pop_tsd |>
  mutate(
    median = round_first_two(pop_median),
    sum_1M = round(pop_sum / 1000000, 1),
    cum_1M = round(pop_cum / 1000000, 1)
  ) |>
  select(category = pop_category, n, n_cum, median, sum_1M, cum_1M) |>
  reactable(
    sortable = FALSE,
    striped = TRUE,
    columns = list(
      category = colDef(format = colFormat(separators = TRUE)),
      median = colDef(format = colFormat(separators = TRUE))
    )
  )
Code
pl_dt <-
  pop |>
  arrange(population) |>
  mutate(cum_population = cumsum(population) / 1000000)

pl_dt_largest <-
  pl_dt |>
  slice_tail(n = 6) |>
  mutate(place = fct_reorder(place, population, .desc = TRUE))

pl <-
  ggplot(pl_dt, aes(x = population, y = cum_population)) +
  geom_point(data = pl_dt_largest, aes(color = place)) +
  geom_line() +
  scale_x_log10(limits = c(1000, 4000000)) +
  labs(x = "Bevölkerung", y = "") +
  labs(color = "Stadt")

pl + plot_annotation(caption = "Quelle: Zensus 2022")

Bundesländer · 🏛️

Code
ags_state <-
  read_csv("data/ags-land.csv", show_col_types = FALSE) |>
  select(-ags_state)

pop_states <-
  pop |>
  left_join(ags_state, by = "state") |>
  summarise(
    population = sum(population),
    .by = c(state, state_name)
  ) |>
  arrange(-population) |>
  mutate(
    pop_1M = round(population / 1000000, 1),
    cum_1M = cumsum(pop_1M)
  )
Code
pl1 <-
  pop_states |>
  mutate(Bundesland = fct_reorder(state_name, population)) |>
  ggplot(aes(x = pop_1M, y = Bundesland)) +
  geom_col(fill = "grey90", alpha = 0.8) +
  scale_x_continuous(
    breaks = c(0, 5, 10, 15),
    labels = c("", "5M", "10M", "15M")
  ) +
  labs(x = "", y = "")

map_centroids <-
  map_deu |>
  st_point_on_surface() |>
  left_join(pop_states, by = "state")

pl2 <-
  ggplot() +
  geom_sf(data = map_deu, color = "grey85") +
  geom_sf(
    data = map_centroids, aes(size = pop_1M),
    colour = "blue", alpha = 0.4, show.legend = FALSE
  ) +
  theme_void()

pl1 + pl2 +
  plot_annotation(caption = "Quelle: Zensus 2022") +
  plot_layout(widths = c(1, 2))

  • pop_1M — Bevölkerung in Millionen
  • cum_1M — Kumulierte Bevölkerung in Millionen
Code
pop_states |>
  select(-state, -population) |>
  reactable(searchable = TRUE, striped = TRUE, defaultPageSize = 4, fullWidth = FALSE)

Metropol-Regionen · 🏙

Eurostat · 🇪🇺

Code
met_raw <- read_csv("data/met_pjanaggr3__custom_12151542_clean.csv", show_col_types = FALSE)

met_geo <-
  read_rds("data/r-metro-region-gecoding_sf.rds") |>
  select(metroreg, geometry)

met_geo_state <-
  map_deu |>
  st_join(met_geo) |>
  select(metroreg, state) |>
  st_set_geometry(NULL) |>
  bind_rows(tibble(metroreg = c("DE054M", "DE074M"), state = c("BW", "SN")))

met <-
  met_geo |>
  right_join(met_raw, by = "metroreg") |>
  left_join(met_geo_state, by = "metroreg") |>
  select(metroreg, state, region = label, population = y2023) |>
  mutate(pop_1M = population / 1000000) |>
  arrange(-population)
Code
pl_dt <-
  met |>
  mutate(
    region = str_remove(region, "-.*"),
    Metropole = fct_reorder(region, population)
  ) |>
  slice(1:15)

pl1 <-
  ggplot(pl_dt, aes(x = pop_1M, y = Metropole)) +
  geom_col(fill = "grey90", alpha = 0.8) +
  scale_x_continuous(
    breaks = c(1, 3, 5),
    labels = c("1M", "3M", "5M")
  ) +
  labs(x = "", y = "")

pl2 <-
  ggplot() +
  geom_sf(data = map_deu, fill = "grey90", color = "grey85") +
  geom_sf(
    data = met, aes(size = pop_1M),
    colour = "red", alpha = 0.33, show.legend = FALSE
  ) +
  coord_sf(crs = "EPSG:4839") +
  guides(alpha = "none", color = "none") +
  theme_void()

pl1 + pl2 + plot_layout(widths = c(2, 3)) +
  plot_annotation(caption = "Quelle: Eurostat") +
  plot_layout(widths = c(1, 2))

Metropol-Region (“Metro regions”)

(…) at least 50 % of the population lives inside a functional urban area (FUA) that is composed of at least 250 000 inhabitants.

Pendel-Zone (“Commuting zone”)

(…) at least 15 % of employed residents are working in a city.

Daten · 💯

Städte / Gemeinden · 🏘

Zensus 2022 · Deutschland · 🇩🇪

Code
pop |>
  filter(population >= 5000) |>
  mutate(
    population = round_first_two(population),
    cum_1M = round(cum_population / 1000000, 1)
  ) |>
  select(-ags, -cum_population) |>
  reactable(
    defaultPageSize = 5,
    searchable = TRUE,
    striped = TRUE,
    columns = list(
      population = colDef(format = colFormat(separators = TRUE))
    )
  )
Code
library(tmap)

tmap_mode("view")

pl_dt <-
  map_place |>
  mutate(
    population = round_first_two(population),
    pop_size = log10(pop_1M),
    pop_type = case_when(
      population >= 100000 ~ "100,000",
      population >= 25000 ~ " 25,000",
      population >= 10000 ~ " 10,000"
    )
  ) |>
  filter(population >= 1000)

pl_scale <- scales::pal_brewer(type = "qual", palette = 6)(3)[c(2, 1, 3)]

tm_shape(pl_dt) +
  tm_dots(
    size = "pop_size",
    size.legend = tm_legend_hide(),
    fill = "pop_type",
    fill_alpha = 0.5,
    fill.scale = tm_scale(values = pl_scale),
    fill.legend = tm_legend(title = ""),
    popup.vars = c("place", "population")
  ) +
  tm_basemap(
    server = c(
      "OpenStreetMap.DE", "OpenTopoMap",
      "Esri.WorldStreetMap", "Esri.WorldTopoMap", "Esri.WorldImagery"
    ),
    zoom = 6
  )

Metropol-Regionen · 🏙

Eurostat · 🇪🇺

Code
met_raw |>
  arrange(-y2023) |>
  mutate(across(where(is.numeric), ~ round(.x / 1000000, 2))) |>
  left_join(met_geo_state, by = "metroreg") |>
  select(state, region = label, y2000, y2010, y2020) |>
  rename_with(~ str_remove(.x, "y"), .cols = everything()) |>
  reactable(searchable = TRUE, striped = TRUE)