Demografiske ændringer i danske kommuner¶
Der er stigende opmærksomhed på den seneste stigning i antallet af udenlandske statsborgere, der gør Danmark til deres hjem, især i byer som København, men hvordan ser billedet ud i resten af landet?
Ser vi kun på hovedtallet (16 % nationalt), kan det udjævne lokale forskelle, og hvis man kun fokuserer på hele befolkningen, kan en aldrende befolkning skjule interessante tendenser.
Siden COVID-19-restriktionerne blev ophævet, har migrationsmønstrene ændret sig.
Vi vil se nærmere på nuancerne i dette billede. Siden 2021:
- I hvilke kommuner falder, stiger eller forbliver befolkningen uændret?
- I hvilke kommuner erstatter erhvervsaktive udlændinge tabet af erhvervsaktive danskere?
- Findes der kommuner, hvor befolkningen vokser som følge af tilgangen af erhvervsaktive udlændinge?
- Findes der kommuner, hvor erhvervsaktive danskere flytter væk uden at blive erstattet af udlændinge?
Forudsætninger og datavalg:
- Erhvervsaktiv alder: 18–66 år
- Tidsperiode: 2025 Q3 (seneste data)
- Sammenligningsperiode: 2021 Q3 (for at sammenligne sæsonmæssigt ens perioder umiddelbart efter pandemien)

Dette arbejde er licenseret under en Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
import pandas as pd
import numpy as np
import geopandas as gpd
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap, SymLogNorm
from matplotlib.colors import TwoSlopeNorm
import matplotlib.patches as mpatches
Befolkningsændringer siden 2021¶
Vi kan se, at den generelle tendens er, at folk flytter fra landets yderområder ind mod de større byer.
Der er dog nogle mindre kommuner, der går imod denne tendens.
def build_colormap():
# Farvepalette egnet til farveblinde
colors = [
(0.0, "#6B3F02"), # orangebrun (stort fald)
(0.25, "#F9D35A"), # lys gul
(0.5, "#CCD6D3"), # hvid ved nul
(0.75, "#86D0E0"), # lys blå
(1.0, "#002C39"), # mørk blå (stor stigning)
]
return LinearSegmentedColormap.from_list("yellow_white_purple", colors, N=256)
df = pd.read_csv("./raw/change_since_pandemic_clean.csv", encoding="utf-8")
gdf = gpd.read_file("./raw/cleaned_kommune_copenhagen.geojson")
merged = gdf.merge(df[["Kommune", "Change"]], left_on="label_dk", right_on="Kommune", how="left")
# --- SymLogNorm: log-lignende skala med lineær zone omkring 0 ---
vmin = np.nanmin(merged["Change"])
vmax = np.nanmax(merged["Change"])
bound = max(abs(vmin if vmin is not None else 0.0), abs(vmax if vmax is not None else 0.0)) or 1.0
linthresh = 500
norm = SymLogNorm(linthresh=linthresh, linscale=1, vmin=-bound, vmax=bound)
cmap = build_colormap()
# Plot
fig, ax = plt.subplots(figsize=(8.5, 10))
merged.plot(column="Change", cmap=cmap, norm=norm, linewidth=0.5, edgecolor="#888888", ax=ax, missing_kwds={
"color": "#f0f0f0", "hatch": "///", "label": "Ingen data"
})
ax.set_axis_off()
# Kilde og ophavsret
fig.text(0.5, 0.2, "© Fair og Fornuftig 2025, Kilde: statbank.dk/FOLK1C",
ha='center', va='center', fontsize=11, color='#191C1B', alpha=0.8)
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
cbar = fig.colorbar(sm, ax=ax, fraction=0.025, pad=0.02)
cbar.set_label("Ændring")
plt.title("Demografiske ændringer i erhvervsaktiv alder 2025–2021")
plt.tight_layout()
fig.savefig("./images/change_map_da.png", dpi=220)
plt.show()
Danske demografiske ændringer¶
Når vi kun fokuserer på danskere, kan vi se, at tendensen til at flytte fra mindre byer og landsbyer til større byer er langt mere udtalt, når man ser bort fra personer i erhvervsaktiv alder med udenlandsk statsborgerskab.
df = pd.read_csv("./raw/change_danish.csv", encoding="utf-8")
merged = gdf.merge(df[["Kommune", "Change"]], left_on="label_dk", right_on="Kommune", how="left")
# --- SymLogNorm: log-like scale with linear zone around 0 ---
vmin = np.nanmin(merged["Change"])
vmax = np.nanmax(merged["Change"])
bound = max(abs(vmin if vmin is not None else 0.0), abs(vmax if vmax is not None else 0.0)) or 1.0
linthresh = 500
norm = SymLogNorm(linthresh=linthresh, linscale=1, vmin=-bound, vmax=bound)
cmap = build_colormap()
# Plot
fig, ax = plt.subplots(figsize=(8.5, 10))
merged.plot(column="Change", cmap=cmap, norm=norm, linewidth=0.5, edgecolor="#888888", ax=ax, missing_kwds={
"color": "#f0f0f0", "hatch": "///", "label": "Ingen data"
})
ax.set_axis_off()
plt.title("Demografiske ændringer blandt danskere i erhvervsaktiv alder 2021–2025")
fig.text(0.5, 0.2, "© Fair og Fornuftig 2025, Kilde: statbank.dk/FOLK1C",
ha='center', va='center', fontsize=11, color='#191C1B', alpha=0.8)
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
cbar = fig.colorbar(sm, ax=ax, fraction=0.025, pad=0.02)
cbar.set_label("Ændring")
plt.tight_layout()
fig.savefig("./images/dk_change_map_da.png", dpi=220)
plt.show()
Demografiske ændringer blandt udenlandske statsborgere¶
Her ser vi, at bortset fra et par kommuner har nettomigrationen af personer i erhvervsaktiv alder med udenlandsk statsborgerskab været positiv overalt.
df = pd.read_csv("./raw/foreign_national_change.csv", encoding="utf-8")
merged = gdf.merge(df[["Kommune", "Change"]], left_on="label_dk", right_on="Kommune", how="left")
# --- SymLogNorm: log-lignende skala med lineær zone omkring 0 ---
vmin = np.nanmin(merged["Change"])
vmax = np.nanmax(merged["Change"])
bound = max(abs(vmin if vmin is not None else 0.0), abs(vmax if vmax is not None else 0.0)) or 1.0
linthresh = 500
norm = SymLogNorm(linthresh=linthresh, linscale=1, vmin=-bound, vmax=bound)
cmap = build_colormap()
# Plot
fig, ax = plt.subplots(figsize=(8.5, 10))
merged.plot(
column="Change",
cmap=cmap,
norm=norm,
linewidth=0.5,
edgecolor="#888888",
ax=ax,
missing_kwds={"color": "#f0f0f0", "hatch": "///", "label": "Ingen data"}
)
ax.set_axis_off()
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
cbar = fig.colorbar(sm, ax=ax, fraction=0.025, pad=0.02)
cbar.set_label("Nettoindvandring")
plt.title("Nettoindvandring af udenlandske statsborgere i erhvervsaktiv alder 2025–2021")
fig.text(
0.5, 0.2,
"© Fair og Fornuftig 2025, Kilde: statbank.dk/FOLK1C",
ha='center', va='center', fontsize=11, color='#191C1B', alpha=0.8
)
plt.tight_layout()
fig.savefig("./images/foreign_change_map_da.png", dpi=220)
plt.show()
df_national= pd.read_csv("./raw/change_since_pandemic_clean.csv", encoding="utf-8")
df_danish = pd.read_csv("./raw/change_danish.csv", encoding="utf-8")
df_foreign = pd.read_csv("./raw/foreign_national_change.csv", encoding="utf-8")
# Rename 'Change' columns before merging
df_national = df_national.rename(columns={"Change": "Total"})
df_danish = df_danish.rename(columns={"Change": "Danish"})
df_foreign = df_foreign.rename(columns={"Change": "Foreign National"})
# Merge all on 'Kommune'
df_all = (
df_national[["Kommune", "Total"]]
.merge(df_danish[["Kommune", "Danish"]], on="Kommune", how="left")
.merge(df_foreign[["Kommune", "Foreign National"]], on="Kommune", how="left")
)
Data-udforskning¶
Klassificering af kommunale vækstmønstre i forskellige kategorier
SMALL = 100 # behandl alt mellem -SMALL og +SMALL som ~stabilt/afrundingsstøj
eps = 1e-9
# clean types just in case
df_all["Kommune"] = df_all["Kommune"].astype(str).str.strip()
for c in ["Total", "Danish", "Foreign National"]:
df_all[c] = pd.to_numeric(df_all[c], errors="coerce").fillna(0)
# helper metrics
df_all["replacement_ratio"] = np.where(
df_all["Danish"] < -SMALL,
df_all["Foreign National"] / (-df_all["Danish"] + eps),
np.nan
)
df_all["foreign_share_of_growth"] = np.where(
df_all["Total"] > SMALL,
df_all["Foreign National"] / (df_all["Total"] + eps),
np.nan
)
def classify(row):
T = row["Total"]
D = row["Danish"]
F = row["Foreign National"]
# near-zero helpers
T_pos = T > SMALL
T_neg = T < -SMALL
T_flat = not T_pos and not T_neg
D_pos, D_neg = D > SMALL, D < -SMALL
F_pos, F_neg = F > SMALL, F < -SMALL
# priority order of mutually exclusive categories
if T_pos and D_neg and F_pos:
# growth but Danes down -> foreigners are the driver
return "Vækst drevet af udlændinge"
if T_flat and D_neg and F_pos:
# total ~flat but foreigners offset Danish decline
return "Stabil på grund af udlændinge"
if T_pos and D_pos and F_pos:
return "Dobbelt vækst (danskere + udlændinge)"
if T_pos and D_pos and not F_pos:
return "Vækst drevet af danskere"
if T_neg and D_neg and not F_pos:
return "Fald i befolkningen i erhvervsaktiv alder"
if T_neg and D_neg and F_pos:
return "Fald trods tilstrømning af udlændinge"
if T_flat and ((D_pos and F_neg) or (D_neg and F_pos)):
return "Stabil: udlignende forskydninger"
return "Lille ændring"
df_all["Typology"] = df_all.apply(classify, axis=1)
df_all[df_all["Typology"] == "Vækst drevet af udlændinge"].sort_values(
"Foreign National", ascending=False
).head(10)
df_all["Foreign_share_of_total_growth"] = (
df_all["Foreign National"] / (df_all["Total"].replace(0, np.nan))
)
# Rens flette-nøgler
df_plot = df_all.copy()
df_plot["Kommune"] = df_plot["Kommune"].astype(str).str.strip()
gdf["label_dk"] = gdf["label_dk"].astype(str).str.strip()
merged = gdf.merge(df_plot, left_on="label_dk", right_on="Kommune", how="left")
# Farvepalette egnet til farveblinde (tilpasset danske typologier)
PALETTE = {
"Vækst drevet af udlændinge": "#0B6A55", # blågrøn
"Stabil på grund af udlændinge": "#36B0CC", # lys blå
"Dobbelt vækst (danskere + udlændinge)": "#1CD194", # grøn
"Vækst drevet af danskere": "#B58A21", # orange
"Fald i befolkningen i erhvervsaktiv alder": "#FDA9AF", # lys rosa
"Fald trods tilstrømning af udlændinge": "#F9D35A", # gul
"Lille ændring": "#A3AFAB", # grå
}
merged["cat_color"] = merged["Typology"].map(PALETTE)
fig, ax = plt.subplots(figsize=(9, 10))
merged.plot(
color=merged["cat_color"],
linewidth=0.5,
edgecolor="#888888",
ax=ax,
missing_kwds={"color": "#f0f0f0", "hatch": "///", "label": "Ingen data"}
)
ax.set_axis_off()
plt.title("Udvikling i erhvervsaktiv alder i danske kommuner (2025–2021)")
fig.text(
0.5, 0.1,
"© Fair og Fornuftig 2025, Kilde: statbank.dk/FOLK1C",
ha='center', va='center', fontsize=12, color='#191C1B', alpha=0.8
)
# Fast, læsbar rækkefølge i signaturen
legend_order = [
"Dobbelt vækst (danskere + udlændinge)",
"Vækst drevet af udlændinge",
"Fald i befolkningen i erhvervsaktiv alder",
"Fald trods tilstrømning af udlændinge",
"Stabil på grund af udlændinge",
"Lille ændring",
]
patches = [mpatches.Patch(color=PALETTE[k], label=k) for k in legend_order]
leg = ax.legend(handles=patches, title="Typologi", loc="upper right", frameon=True)
for t in leg.get_texts():
t.set_fontsize(9)
plt.tight_layout()
fig.savefig("./images/typology_map_da.png", dpi=220)
plt.show()
# === INDSTILLINGER ===
INPUT_CSV = "./raw/percentage of foreigners.csv"
Y_VAR = "Total" # "Total", "Danish", or "Foreign National"
OUTPUT_PNG = f"./images/scatter_{Y_VAR.lower()}_vs_foreign_share_typology_minimal_da.png"
TITLE = "Hvordan befolkningsændringer hænger sammen med andelen af udenlandske statsborgere"
# Farvepalette egnet til farveblinde (danske typologier)
TYPOLOGY_COLOURS = {
"Vækst drevet af udlændinge": "#0B6A55", # blågrøn
"Stabil på grund af udlændinge": "#36B0CC", # lys blå
"Dobbelt vækst (danskere + udlændinge)": "#1CD194", # grøn
"Vækst drevet af danskere": "#B58A21", # orange
"Fald i befolkningen i erhvervsaktiv alder": "#FDA9AF", # lys rosa
"Fald trods tilstrømning af udlændinge": "#F9D35A", # gul
"Lille ændring": "#A3AFAB", # grå
}
# Kort dansk label til Y_VAR (data bruges uændret)
Y_LABEL_MAP = {
"Total": "Total",
"Danish": "Danskere",
"Foreign National": "Udenlandske statsborgere",
}
Y_VAR_DA = Y_LABEL_MAP.get(Y_VAR, Y_VAR)
# === 1) Indlæs CSV med % udlændinge ===
foreign = pd.read_csv(INPUT_CSV, encoding="utf-8")
if "Foreign citizen" in foreign.columns:
foreign["pct_foreign_2025Q3"] = pd.to_numeric(foreign["Foreign citizen"], errors="coerce")
else:
raise ValueError("CSV skal indeholde en kolonne 'Foreign citizen'.")
foreign = foreign.rename(columns={"region": "Kommune"})
# === 2) Flet med df_all ===
if "df_all" not in globals():
raise RuntimeError("df_all skal eksistere (Kommune, Total, Danish, Foreign National, Typology).")
merged = df_all.merge(foreign[["Kommune", "pct_foreign_2025Q3"]], on="Kommune", how="left")
for c in ["Total", "Danish", "Foreign National"]:
merged[c] = pd.to_numeric(merged[c], errors="coerce")
# === 3) Plot ===
plt.style.use("seaborn-v0_8-whitegrid")
fig, ax = plt.subplots(figsize=(8.5, 6))
# Punktdiagram efter typologi
for typ, group in merged.groupby("Typology"):
ax.scatter(
group["pct_foreign_2025Q3"],
group[Y_VAR],
s=55,
color=TYPOLOGY_COLOURS.get(typ, "#999999"),
label=typ,
alpha=0.8,
edgecolor="none",
)
# Trendlinje
x = merged["pct_foreign_2025Q3"]
y = merged[Y_VAR]
mask = x.notna() & y.notna()
coef = np.polyfit(x[mask], y[mask], 1)
poly = np.poly1d(coef)
x_line = np.linspace(x.min(), x.max(), 200)
ax.plot(x_line, poly(x_line), linestyle="-", color="#222222", lw=1.0, alpha=0.8)
# Ligning + R² (diskret, øverst til venstre)
r2 = 1 - np.sum((y[mask] - poly(x[mask]))**2) / np.sum((y[mask] - y[mask].mean())**2)
ax.text(
0.02, 0.97, f"y = {coef[0]:.1f}x + {coef[1]:.0f} R² = {r2:.2f}",
transform=ax.transAxes, ha="left", va="top", fontsize=9, color="#333333"
)
# Minimalistiske akser
ax.axhline(0, color="#888888", lw=0.6)
ax.set_xlabel("% udenlandske statsborgere (2025Q3)", fontsize=10, color="#333333")
ax.set_ylabel(f"{Y_VAR_DA} ændring (2025–2021)", fontsize=10, color="#333333")
ax.set_title(TITLE, fontsize=11.5, color="#222222", pad=12)
ax.grid(True, linestyle=":", lw=0.5, alpha=0.3)
ax.set_xlim(0, 40)
# Signatur — lille og i siden
ax.legend(fontsize=10, loc="center left", bbox_to_anchor=(1.0, 0.5), frameon=False)
# Kilde
for spine in ax.spines.values():
spine.set_visible(False)
fig.text(
0.0, 0.01,
"© Fair og Fornuftig 2025, Kilde: statbank.dk/FOLK1C",
ha='left', va='center', fontsize=12, color='#191C1B', alpha=0.6
)
fig.tight_layout()
plt.savefig(OUTPUT_PNG, dpi=220, bbox_inches="tight")
plt.show()
# === Kommuner hvor væksten er drevet af udlændinge ===
growth_foreign = (
merged.loc[
merged["Typology"] == "Vækst drevet af udlændinge",
["Kommune", "Total", "Danish", "Foreign National", "pct_foreign_2025Q3"]
]
.sort_values("pct_foreign_2025Q3", ascending=False)
.reset_index(drop=True)
)
# Vis
growth_foreign.head(90)
| Kommune | Total | Danish | Foreign National | pct_foreign_2025Q3 | |
|---|---|---|---|---|---|
| 0 | Ishøj | 1129 | -185 | 1314 | 34 |
| 1 | Gladsaxe | 1651 | -528 | 2179 | 21 |
| 2 | Lyngby-Taarbæk | 1056 | -547 | 1603 | 19 |
| 3 | Billund | 389 | -555 | 944 | 19 |
| 4 | Sønderborg | 169 | -2026 | 2195 | 17 |
| 5 | Ikast-Brande | 775 | -359 | 1134 | 17 |
| 6 | Hvidovre | 357 | -528 | 885 | 17 |
| 7 | Rudersdal | 123 | -433 | 556 | 13 |
| 8 | Helsingør | 257 | -276 | 533 | 11 |
| 9 | Slagelse | 817 | -233 | 1050 | 11 |
| 10 | Viborg | 519 | -351 | 870 | 10 |
| 11 | Gribskov | 122 | -350 | 472 | 10 |
| 12 | Næstved | 708 | -282 | 990 | 9 |
| 13 | Faaborg-Midtfyn | 253 | -294 | 547 | 9 |
| 14 | Brønderslev-Dronninglund | 154 | -157 | 311 | 9 |
| 15 | Nyborg | 102 | -153 | 255 | 8 |
# === Kommuner hvor væksten holdes stabil af udlændinge ===
steady_foreign = (
merged.loc[
merged["Typology"] == "Stabil på grund af udlændinge",
["Kommune", "Total", "Danish", "Foreign National", "pct_foreign_2025Q3"]
]
.sort_values("pct_foreign_2025Q3", ascending=False)
.reset_index(drop=True)
)
steady_foreign.head(90)
| Kommune | Total | Danish | Foreign National | pct_foreign_2025Q3 | |
|---|---|---|---|---|---|
| 0 | Vejen | -51 | -693 | 642 | 15 |
| 1 | Halsnæs | -13 | -576 | 563 | 13 |
| 2 | Herning | 12 | -1165 | 1177 | 12 |
| 3 | Kerteminde | 70 | -268 | 338 | 10 |
| 4 | Stevns | -21 | -232 | 211 | 10 |
| 5 | Holstebro | 55 | -573 | 628 | 10 |
| 6 | Syddjurs | 97 | -256 | 353 | 9 |
# === Kommuner hvor væksten skyldes både udlændinge og danskere ===
growth_both = (
merged.loc[
merged["Typology"] == "Dobbelt vækst (danskere + udlændinge)",
["Kommune", "Total", "Danish", "Foreign National", "pct_foreign_2025Q3"]
]
.sort_values("pct_foreign_2025Q3", ascending=False)
.reset_index(drop=True)
)
# Vis
growth_both.head(90)
| Kommune | Total | Danish | Foreign National | pct_foreign_2025Q3 | |
|---|---|---|---|---|---|
| 0 | Høje-Taastrup | 5672 | 1368 | 4304 | 30 |
| 1 | Brøndby | 3972 | 992 | 2980 | 29 |
| 2 | Vallensbæk | 1545 | 342 | 1203 | 28 |
| 3 | Copenhagen | 23626 | 2202 | 21424 | 23 |
| 4 | Glostrup | 1804 | 277 | 1527 | 23 |
| 5 | Herlev | 2146 | 347 | 1799 | 21 |
| 6 | Rødovre | 2790 | 561 | 2229 | 21 |
| 7 | Ballerup | 3600 | 1014 | 2586 | 19 |
| 8 | Frederiksberg | 2963 | 1289 | 1674 | 17 |
| 9 | Horsens | 2970 | 929 | 2041 | 16 |
| 10 | Greve | 2215 | 1036 | 1179 | 15 |
| 11 | Vejle | 2772 | 335 | 2437 | 15 |
| 12 | Furesø | 821 | 443 | 378 | 14 |
| 13 | Hillerød | 2108 | 702 | 1406 | 14 |
| 14 | Århus | 15830 | 8683 | 7147 | 13 |
| 15 | Køge | 1618 | 372 | 1246 | 13 |
| 16 | Tårnby | 948 | 221 | 727 | 13 |
| 17 | Odense | 4088 | 1053 | 3035 | 12 |
| 18 | Hedensted | 1004 | 160 | 844 | 11 |
| 19 | Frederikssund | 1035 | 262 | 773 | 10 |
| 20 | Egedal | 1374 | 756 | 618 | 10 |
| 21 | Holbæk | 1017 | 293 | 724 | 10 |
| 22 | Roskilde | 2099 | 1047 | 1052 | 10 |
| 23 | Solrød | 948 | 503 | 445 | 10 |
| 24 | Aalborg | 2695 | 1042 | 1653 | 10 |
| 25 | Lejre | 776 | 419 | 357 | 9 |
| 26 | Odder | 492 | 309 | 183 | 9 |
| 27 | Favrskov | 589 | 160 | 429 | 9 |
| 28 | Silkeborg | 3252 | 2211 | 1041 | 9 |
| 29 | Skanderborg | 960 | 733 | 227 | 7 |
# === Udlændinge flytter til, men kommunens arbejdsstyrke falder stadig ===
contracting_foreign = (
merged.loc[
merged["Typology"] == "Fald trods tilstrømning af udlændinge",
["Kommune", "Total", "Danish", "Foreign National", "pct_foreign_2025Q3"]
]
.sort_values("pct_foreign_2025Q3", ascending=False)
.reset_index(drop=True)
)
# Vis
contracting_foreign.head(90)
| Kommune | Total | Danish | Foreign National | pct_foreign_2025Q3 | |
|---|---|---|---|---|---|
| 0 | Tønder | -1167 | -2032 | 865 | 18 |
| 1 | Aabenraa | -484 | -1844 | 1360 | 17 |
| 2 | Ærø | -207 | -341 | 134 | 15 |
| 3 | Ringkøbing-Skjern | -515 | -1376 | 861 | 15 |
| 4 | Varde | -542 | -1233 | 691 | 14 |
| 5 | Haderslev | -356 | -1450 | 1094 | 13 |
| 6 | Lolland | -1061 | -1906 | 845 | 13 |
| 7 | Lemvig | -662 | -906 | 244 | 12 |
| 8 | Vesthimmerland | -456 | -846 | 390 | 12 |
| 9 | Nordfyns | -345 | -659 | 314 | 11 |
| 10 | Norddjurs | -879 | -1206 | 327 | 11 |
| 11 | Struer | -587 | -715 | 128 | 11 |
| 12 | Thisted | -691 | -1016 | 325 | 11 |
| 13 | Frederikshavn | -1302 | -1893 | 591 | 11 |
| 14 | Esbjerg | -1396 | -2626 | 1230 | 11 |
| 15 | Kalundborg | -163 | -1190 | 1027 | 10 |
| 16 | Bornholm | -1009 | -1295 | 286 | 10 |
| 17 | Assens | -426 | -881 | 455 | 10 |
| 18 | Mariagerfjord | -397 | -844 | 447 | 10 |
| 19 | Vordingborg | -560 | -1089 | 529 | 10 |
| 20 | Guldborgsund | -1125 | -1679 | 554 | 9 |
| 21 | Skive | -909 | -1282 | 373 | 9 |
| 22 | Hjørring | -939 | -1444 | 505 | 9 |
| 23 | Odsherred | -883 | -1089 | 206 | 8 |
| 24 | Jammerbugt | -265 | -628 | 363 | 8 |
| 25 | Svendborg | -376 | -687 | 311 | 7 |
# === Typologiliste over kommuner ===
typology_pct = (
merged.loc[
(merged["Typology"] != "Dobbelt vækst (danskere + udlændinge)") &
(merged["pct_foreign_2025Q3"] >= 10),
["Kommune", "Typology", "pct_foreign_2025Q3"]
]
.sort_values("pct_foreign_2025Q3", ascending=False)
.reset_index(drop=True)
)
typology_pct.head(90)
| Kommune | Typology | pct_foreign_2025Q3 | |
|---|---|---|---|
| 0 | Ishøj | Vækst drevet af udlændinge | 34 |
| 1 | Albertslund | Lille ændring | 23 |
| 2 | Gladsaxe | Vækst drevet af udlændinge | 21 |
| 3 | Lyngby-Taarbæk | Vækst drevet af udlændinge | 19 |
| 4 | Billund | Vækst drevet af udlændinge | 19 |
| 5 | Tønder | Fald trods tilstrømning af udlændinge | 18 |
| 6 | Gentofte | Lille ændring | 17 |
| 7 | Aabenraa | Fald trods tilstrømning af udlændinge | 17 |
| 8 | Sønderborg | Vækst drevet af udlændinge | 17 |
| 9 | Hvidovre | Vækst drevet af udlændinge | 17 |
| 10 | Fredensborg | Lille ændring | 17 |
| 11 | Ikast-Brande | Vækst drevet af udlændinge | 17 |
| 12 | Ærø | Fald trods tilstrømning af udlændinge | 15 |
| 13 | Vejen | Stabil på grund af udlændinge | 15 |
| 14 | Ringkøbing-Skjern | Fald trods tilstrømning af udlændinge | 15 |
| 15 | Ringsted | Lille ændring | 15 |
| 16 | Varde | Fald trods tilstrømning af udlændinge | 14 |
| 17 | Rudersdal | Vækst drevet af udlændinge | 13 |
| 18 | Halsnæs | Stabil på grund af udlændinge | 13 |
| 19 | Lolland | Fald trods tilstrømning af udlændinge | 13 |
| 20 | Haderslev | Fald trods tilstrømning af udlændinge | 13 |
| 21 | Kolding | Lille ændring | 13 |
| 22 | Samsø | Lille ændring | 13 |
| 23 | Vesthimmerland | Fald trods tilstrømning af udlændinge | 12 |
| 24 | Lemvig | Fald trods tilstrømning af udlændinge | 12 |
| 25 | Hørsholm | Lille ændring | 12 |
| 26 | Faxe | Lille ændring | 12 |
| 27 | Herning | Stabil på grund af udlændinge | 12 |
| 28 | Fredericia | Lille ændring | 12 |
| 29 | Thisted | Fald trods tilstrømning af udlændinge | 11 |
| 30 | Frederikshavn | Fald trods tilstrømning af udlændinge | 11 |
| 31 | Slagelse | Vækst drevet af udlændinge | 11 |
| 32 | Helsingør | Vækst drevet af udlændinge | 11 |
| 33 | Norddjurs | Fald trods tilstrømning af udlændinge | 11 |
| 34 | Struer | Fald trods tilstrømning af udlændinge | 11 |
| 35 | Langeland | Fald i befolkningen i erhvervsaktiv alder | 11 |
| 36 | Esbjerg | Fald trods tilstrømning af udlændinge | 11 |
| 37 | Nordfyns | Fald trods tilstrømning af udlændinge | 11 |
| 38 | Gribskov | Vækst drevet af udlændinge | 10 |
| 39 | Kalundborg | Fald trods tilstrømning af udlændinge | 10 |
| 40 | Bornholm | Fald trods tilstrømning af udlændinge | 10 |
| 41 | Allerød | Lille ændring | 10 |
| 42 | Kerteminde | Stabil på grund af udlændinge | 10 |
| 43 | Fanø | Fald i befolkningen i erhvervsaktiv alder | 10 |
| 44 | Assens | Fald trods tilstrømning af udlændinge | 10 |
| 45 | Vordingborg | Fald trods tilstrømning af udlændinge | 10 |
| 46 | Stevns | Stabil på grund af udlændinge | 10 |
| 47 | Holstebro | Stabil på grund af udlændinge | 10 |
| 48 | Randers | Lille ændring | 10 |
| 49 | Viborg | Vækst drevet af udlændinge | 10 |
| 50 | Mariagerfjord | Fald trods tilstrømning af udlændinge | 10 |
Konklusion¶
Nogle kommuner er nu så afhængige af internationale borgere, at det ikke længere giver mening at planlægge uden deres input.
I områder, hvor befolkningen i erhvervsaktiv alder er stabil eller voksende, er det ofte de internationale borgere, der holder linjen: de betaler skat, udfylder vigtige funktioner og holder serviceområderne kørende. Den demografiske balance har allerede ændret sig, men den lokale styring er endnu ikke fulgt med.
Samtalen skal bevæge sig videre fra at handle om ”at tiltrække en international arbejdsstyrke” til ”hvordan vi får disse værdsatte medlemmer af vores lokalsamfund til at trives og om de har særlige udfordringer, vi skal imødekomme.”