PythonLoading editor…
import json
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from pyodide.http import pyfetch
# ============================================================
# BFS PxWeb API — Daten direkt abfragen (kein Asset-Upload nötig)
# ============================================================
API = "https://www.pxweb.bfs.admin.ch/api/v1/de/px-x-0103010000_151/px-x-0103010000_151.px"
async def pxweb_query(components, nationalities):
query = {
"query": [
{"code": "Jahr", "selection": {"filter": "all", "values": ["*"]}},
{"code": "Staatsangehörigkeit", "selection": {"filter": "item", "values": nationalities}},
{"code": "Geschlecht", "selection": {"filter": "item", "values": ["0"]}},
{"code": "Demografische Komponente", "selection": {"filter": "item", "values": components}},
],
"response": {"format": "json-stat2"},
}
resp = await pyfetch(API, method="POST", headers={"Content-Type": "application/json"}, body=json.dumps(query))
return await resp.json()
def jsonstat_to_long(d):
"""JSON-stat2-Antwort in ein Long-Format-DataFrame umwandeln (wie es pyaxis.parse liefern würde)."""
dims = d["id"]
sizes = d["size"]
labels = [d["dimension"][dim]["category"]["label"] for dim in dims]
codes = [list(d["dimension"][dim]["category"]["index"].keys()) for dim in dims]
vals = [np.nan if v is None else v for v in d["value"]]
arr = np.array(vals, dtype=float).reshape(sizes)
rows = []
for idx in np.ndindex(*sizes):
val = arr[idx]
if np.isnan(val):
continue
row = {dims[k]: labels[k][codes[k][idx[k]]] for k in range(len(dims))}
row["DATA"] = val
rows.append(row)
return pd.DataFrame(rows)
# Verfügbare Staatsangehörigkeits-Codes aus den Metadaten holen (statt fest zu verdrahten)
meta_resp = await pyfetch(API)
meta = await meta_resp.json()
nat_var = next(v for v in meta["variables"] if v["code"] == "Staatsangehörigkeit")
other_nat_codes = [c for c in nat_var["values"] if c not in ("0", "8100")]
# Query 1: Total + Schweiz, die 6 Komponenten, die wir für die Übersichts-Plots brauchen
# 14=Bestand 31.12, 3=Geburtenüberschuss, 6=Wanderungssaldo, 10=Erwerb Bürgerrecht,
# 13=Bestandesbereinigung, 15=Veränderung
main_json = await pxweb_query(["14", "3", "6", "10", "13", "15"], ["0", "8100"])
# Query 2: alle übrigen Staatsangehörigkeiten, nur Bestand 31.12 (für die Herkunfts-Aufschlüsselung)
nat_json = await pxweb_query(["14"], other_nat_codes)
df = pd.concat([jsonstat_to_long(main_json), jsonstat_to_long(nat_json)], ignore_index=True)
df["DATA"] = pd.to_numeric(df["DATA"], errors="coerce")
# ============================================================
# Filter: Total gender, key nationalities (Total, Schweiz)
# ============================================================
mask_gender = df["Geschlecht"] == "Geschlecht - Total"
components = [
"Bestand am 31. Dezember",
"Geburtenüberschuss",
"Wanderungssaldo inkl. Änderung des Bevölkerungstyps",
"Erwerb des Schweizer Bürgerrechts",
"Veränderung",
]
nationalities = ["Staatsangehörigkeit - Total", "Schweiz"]
filtered = df[mask_gender &
df["Staatsangehörigkeit"].isin(nationalities) &
df["Demografische Komponente"].isin(components)].copy()
# Pivot to wide format
pivot = filtered.pivot_table(
index="Jahr",
columns=["Staatsangehörigkeit", "Demografische Komponente"],
values="DATA"
).reset_index()
# Flatten column names
pivot.columns = ["_".join(col).strip("_") if col[1] else col[0] for col in pivot.columns]
pivot["Jahr"] = pivot["Jahr"].astype(int)
pivot = pivot.sort_values("Jahr")
# Extract time series
years = pivot["Jahr"].values
# Total population (end of year)
total_pop = pivot["Staatsangehörigkeit - Total_Bestand am 31. Dezember"].values
swiss_pop = pivot["Schweiz_Bestand am 31. Dezember"].values
foreign_pop = total_pop - swiss_pop
# Einbürgerungen: auf Gesamtebene = 0 (kein Effekt auf Total),
# daher nehmen wir "Schweiz" -> "Erwerb des Schweizer Bürgerrechts" = Zugang zu Schweizer Bev.
einbuergerungen = pivot["Schweiz_Erwerb des Schweizer Bürgerrechts"].values
# Geburtenüberschuss Total
geburtenueberschuss_total = pivot["Staatsangehörigkeit - Total_Geburtenüberschuss"].values
# Veränderung foreign pop
veraenderung_foreign = np.diff(foreign_pop, prepend=np.nan)
veraenderung_foreign[0] = np.nan # kein Vorjahr
# Ausländeranteil
anteil = foreign_pop / total_pop * 100
# ============================================================
# NATIONALITY BREAKDOWN — grouped: Top 3 + Rest EU/EFTA + Rest Europa + Rest Welt
# ============================================================
mask_nat = (df["Geschlecht"] == "Geschlecht - Total") & \
(df["Demografische Komponente"] == "Bestand am 31. Dezember") & \
(~df["Staatsangehörigkeit"].isin(["Staatsangehörigkeit - Total", "Schweiz"]))
nat_data = df[mask_nat][["Jahr", "Staatsangehörigkeit", "DATA"]].copy()
nat_data["Jahr"] = nat_data["Jahr"].astype(int)
eu_efta = {
"Belgien", "Bulgarien", "Dänemark", "Deutschland", "Estland", "Finnland",
"Frankreich", "Griechenland", "Irland", "Italien", "Kroatien", "Lettland",
"Litauen", "Luxemburg", "Malta", "Niederlande", "Österreich", "Polen",
"Portugal", "Rumänien", "Schweden", "Slowakei", "Slowenien", "Spanien",
"Tschechien", "Ungarn", "Zypern",
"Island", "Liechtenstein", "Norwegen",
}
europa_rest = {
"Albanien", "Andorra", "Bosnien und Herzegowina", "Vereinigtes Königreich",
"Kosovo", "Moldau", "Monaco", "Montenegro", "Nordmazedonien", "Russland",
"San Marino", "Serbien", "Serbien und Montenegro", "Türkiye", "Ukraine",
"Vatikanstadt", "Belarus", "Georgien", "Armenien", "Aserbaidschan",
"Jugoslawien", "Sowjetunion", "Tschechoslowakei",
}
top3 = {"Deutschland", "Kosovo", "Portugal"}
def classify(nat):
if nat in top3:
return nat
elif nat in eu_efta:
return "Übrige EU/EFTA"
elif nat in europa_rest:
return "Übriges Europa"
else:
return "Rest der Welt"
nat_data["Gruppe"] = nat_data["Staatsangehörigkeit"].apply(classify)
grouped = nat_data.groupby(["Jahr", "Gruppe"])["DATA"].sum().unstack(fill_value=0).sort_index()
nat_changes_grouped = grouped.diff()
nat_stacked = nat_changes_grouped.dropna(how="all") # erstes Jahr hat keinen Vorjahreswert
stack_order = ["Deutschland", "Portugal", "Kosovo", "Übrige EU/EFTA", "Übriges Europa", "Rest der Welt"]
# ============================================================
# SWISS POPULATION COMPONENTS (für Plot 3)
# ============================================================
mask_ch = (df["Geschlecht"] == "Geschlecht - Total") & \
(df["Staatsangehörigkeit"] == "Schweiz") & \
(df["Demografische Komponente"].isin([
"Geburtenüberschuss",
"Wanderungssaldo inkl. Änderung des Bevölkerungstyps",
"Erwerb des Schweizer Bürgerrechts",
"Bestandesbereinigung",
"Veränderung",
]))
ch_data = df[mask_ch].pivot_table(index="Jahr", columns="Demografische Komponente", values="DATA")
ch_data.index = ch_data.index.astype(int)
ch_geburt = ch_data["Geburtenüberschuss"].values
ch_wanderung = ch_data["Wanderungssaldo inkl. Änderung des Bevölkerungstyps"].values
ch_einbuergerung = ch_data["Erwerb des Schweizer Bürgerrechts"].values
ch_bereinigung = ch_data["Bestandesbereinigung"].values
ch_veraenderung = ch_data["Veränderung"].values
# ============================================================
# HISTORISCHE ZEITREIHE — Zusammenfassung
# ============================================================
print("=" * 70)
print("BFS DEMOGRAFISCHE BILANZ — ZEITREIHE 1981-2024")
print("=" * 70)
print(f"{'Jahr':>6} {'Total':>12} {'Schweizer':>12} {'Ausländer':>12} {'Anteil%':>8} {'Δ Ausl.':>10} {'Einbürg.':>10}")
print("-" * 70)
for i, y in enumerate(years):
delta = f"{veraenderung_foreign[i]:>10,.0f}" if not np.isnan(veraenderung_foreign[i]) else " --"
print(f"{y:>6} {total_pop[i]:>12,.0f} {swiss_pop[i]:>12,.0f} {foreign_pop[i]:>12,.0f} {anteil[i]:>7.1f}% {delta} {einbuergerungen[i]:>10,.0f}")
# ============================================================
# DURCHSCHNITTSWERTE (informativ, nicht mehr für die Projektion genutzt —
# siehe unten die offiziellen BFS-Szenarien)
# ============================================================
mask_10y = (years >= 2015) & (years <= 2024)
idx_10y = np.where(mask_10y)[0]
total_changes_10y = [total_pop[i] - total_pop[i - 1] for i in idx_10y if i > 0]
avg_total_change_10y = np.mean(total_changes_10y)
foreign_changes_10y = [foreign_pop[i] - foreign_pop[i - 1] for i in idx_10y if i > 0]
avg_foreign_change_10y = np.mean(foreign_changes_10y)
avg_einbuergerungen_10y = np.mean(einbuergerungen[idx_10y])
# FZA-era averages (2002-2024)
# Korrektur: 2023 enthält ~50'600 Statuswechsel von S-Status (Ukraine) — kein
# echter Zuzug in 2023, sondern eine statistische Umbuchung. Wird aus dem
# Durchschnitt herausgerechnet.
ukraine_s_effect_2023 = 50600
mask_fza = (years >= 2002) & (years <= 2024)
idx_fza = np.where(mask_fza)[0]
total_changes_fza = []
foreign_changes_fza = []
for i in idx_fza:
if i == 0:
continue
fc = foreign_pop[i] - foreign_pop[i - 1]
tc = total_pop[i] - total_pop[i - 1]
if years[i] == 2023:
fc -= ukraine_s_effect_2023
tc -= ukraine_s_effect_2023
total_changes_fza.append(tc)
foreign_changes_fza.append(fc)
avg_total_change_fza = np.mean(total_changes_fza)
avg_foreign_change_fza = np.mean(foreign_changes_fza)
avg_einbuergerungen_fza = np.mean(einbuergerungen[idx_fza])
print(f"\n{'=' * 70}")
print("DURCHSCHNITTSWERTE (informativ)")
print(f"{'=' * 70}")
print("10-Jahres-Durchschnitt (2015-2024):")
print(f" Veränderung Gesamtbev.: {avg_total_change_10y:>10,.0f} / Jahr")
print(f" Veränderung Ausländer: {avg_foreign_change_10y:>10,.0f} / Jahr")
print(f" Einbürgerungen: {avg_einbuergerungen_10y:>10,.0f} / Jahr")
print("\nFZA-Ära Durchschnitt (2002-2024), bereinigt um Ukraine-S-Effekt 2023:")
print(f" Veränderung Gesamtbev.: {avg_total_change_fza:>10,.0f} / Jahr")
print(f" Veränderung Ausländer: {avg_foreign_change_fza:>10,.0f} / Jahr")
print(f" Einbürgerungen: {avg_einbuergerungen_fza:>10,.0f} / Jahr")
# ============================================================
# BFS-BEVÖLKERUNGSSZENARIEN 2024–2075 (px-x-0104000000_102)
# Referenzszenario A-00-2025, "hohes" Szenario B-00-2025, "tiefes" Szenario C-00-2025
# ============================================================
API_SCEN = "https://www.pxweb.bfs.admin.ch/api/v1/de/px-x-0104000000_102/px-x-0104000000_102.px"
async def pxweb_query_scenarios(szenarien, nationalities, komponenten):
query = {
"query": [
{"code": "Szenario-Variante", "selection": {"filter": "item", "values": szenarien}},
{"code": "Staatsangehörigkeit (Kategorie)", "selection": {"filter": "item", "values": nationalities}},
{"code": "Geschlecht", "selection": {"filter": "item", "values": ["0"]}},
{"code": "Alter", "selection": {"filter": "item", "values": ["0"]}},
{"code": "Jahr", "selection": {"filter": "all", "values": ["*"]}},
{"code": "Beobachtungseinheit", "selection": {"filter": "item", "values": komponenten}},
],
"response": {"format": "json-stat2"},
}
resp = await pyfetch(API_SCEN, method="POST", headers={"Content-Type": "application/json"}, body=json.dumps(query))
return await resp.json()
# 0=Referenz A-00-2025, 1=hoch B-00-2025, 2=tief C-00-2025; 0=Total, 1=Schweiz; 6=Bestand 31.12
scen_json = await pxweb_query_scenarios(["0", "1", "2"], ["0", "1"], ["6"])
df_scen = jsonstat_to_long(scen_json)
df_scen["Jahr"] = df_scen["Jahr"].astype(int)
scen_pivot = df_scen.pivot_table(
index="Jahr", columns=["Szenario-Variante", "Staatsangehörigkeit (Kategorie)"], values="DATA"
).sort_index()
scen_years = scen_pivot.index.values
scenarios = {
"Referenz (A-00-2025)": ("Referenzszenario A-00-2025", "#2c3e50", "-"),
"hoch (B-00-2025)": ("'hohes' Szenario B-00-2025", "#27ae60", "--"),
"tief (C-00-2025)": ("'tiefes' Szenario C-00-2025", "#e74c3c", "--"),
}
scen_total = {name: scen_pivot[(col, "Staatsangehörigkeit - Total")].values for name, (col, _, _) in scenarios.items()}
scen_swiss = {name: scen_pivot[(col, "Schweiz")].values for name, (col, _, _) in scenarios.items()}
scen_foreign = {name: scen_total[name] - scen_swiss[name] for name in scenarios}
scen_anteil = {name: scen_foreign[name] / scen_total[name] * 100 for name in scenarios}
# ============================================================
# PLOTTING
# ============================================================
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
fig.suptitle("Schweiz: Bevölkerungsentwicklung nach den drei BFS-Hauptszenarien (2024–2075)\n"
"Quelle: BFS – Demografische Bilanz nach Staatsangehörigkeit, 1981–2024, plus Bevölkerungsszenarien",
fontsize=13, fontweight="bold", y=0.98)
c_total = "#2c3e50"
c_foreign = "#e74c3c"
c_swiss = "#3498db"
c_anteil = "#e67e22"
c_einb = "#27ae60"
c_bereinigung = "#95a5a6"
# --- Plot 1: Bevölkerung ---
ax1 = axes[0, 0]
ax1.plot(years, total_pop / 1e6, "o-", color=c_total, linewidth=2, markersize=2.5, label="Gesamtbevölkerung")
ax1.plot(years, foreign_pop / 1e6, "s-", color=c_foreign, linewidth=2, markersize=2.5, label="Ausländische Bev.")
ax1.plot(years, swiss_pop / 1e6, "^-", color=c_swiss, linewidth=2, markersize=2.5, label="Schweizer Bev.")
for name, (col, color, ls) in scenarios.items():
ax1.plot(scen_years, scen_total[name] / 1e6, ls, color=color, linewidth=1.6, alpha=0.85, label=f"Total, {name}")
ax1.axvline(x=2024.5, color="gray", linestyle=":", alpha=0.4)
ax1.axvline(x=2002, color="black", linestyle=":", alpha=0.3)
ax1.text(2002.5, 5.2, "FZA", fontsize=7, color="black", alpha=0.6)
ax1.axhline(y=10, color="gray", linestyle=":", alpha=0.2)
ref_total = scen_total["Referenz (A-00-2025)"]
for i, v in enumerate(ref_total):
if v / 1e6 >= 10:
ax1.annotate(f"10 Mio. ~{scen_years[i]} (Referenz)", xy=(scen_years[i], 10), fontsize=8, color=c_total, ha="center", va="bottom")
break
ax1.set_title("Bevölkerungsentwicklung — BFS-Szenarien (Mio.)", fontweight="bold")
ax1.set_ylabel("Millionen")
ax1.legend(fontsize=6.5, loc="upper left")
ax1.grid(True, alpha=0.3)
# --- Plot 2: Ausländeranteil ---
ax2 = axes[0, 1]
ax2.plot(years, anteil, "o-", color=c_anteil, linewidth=2, markersize=2.5, label="Historisch")
for name, (col, color, ls) in scenarios.items():
ax2.plot(scen_years, scen_anteil[name], ls, color=color, linewidth=1.6, alpha=0.85, label=name)
ax2.axvline(x=2024.5, color="gray", linestyle=":", alpha=0.4)
ax2.axvline(x=2002, color="black", linestyle=":", alpha=0.3)
ax2.text(2002.5, 14.8, "FZA", fontsize=7, color="black", alpha=0.6)
ax2.annotate(f"{anteil[-1]:.1f}% (2024)", xy=(2024, anteil[-1]), fontsize=9, color=c_anteil, fontweight="bold", ha="right")
ax2.set_title("Ausländeranteil — BFS-Szenarien (%)", fontweight="bold")
ax2.set_ylabel("Prozent")
ax2.legend(fontsize=7, loc="upper left")
ax2.grid(True, alpha=0.3)
# --- Plot 3: Schweizer Bevölkerung — Komponenten der Veränderung ---
# Echtes kumulatives Stacking (positive Werte aufeinander, negative aufeinander) —
# ein reines Balken-Overlay hätte in Jahren mit mehreren positiven Komponenten
# (z.B. 1980er: Geburtenüberschuss UND Einbürgerungen beide positiv) die Balken
# einfach übereinandergelegt statt addiert, sodass die sichtbare Balkenhöhe unter
# der echten Summe (= Netto-Veränderung) lag. Bestandesbereinigung (BFS-eigene
# Registerkorrektur, kein reales demografisches Ereignis) als vierte Komponente
# ergänzt, damit die Linie exakt auf dem Balkenstapel landet.
ax3 = axes[1, 0]
bar_width = 0.8
ch_bottom_pos = np.zeros(len(years))
ch_bottom_neg = np.zeros(len(years))
for label, vals, color, alpha in [
("Einbürgerungen", ch_einbuergerung, c_einb, 0.75),
("Natürliches Wachstum (Geburten − Todesfälle)", ch_geburt, "#e74c3c", 0.65),
("Wanderungssaldo Schweizer Staatsangehörige", ch_wanderung, "#9b59b6", 0.55),
("Bestandesbereinigung", ch_bereinigung, c_bereinigung, 0.6),
]:
pos_vals = np.where(vals > 0, vals, 0)
neg_vals = np.where(vals < 0, vals, 0)
ax3.bar(years, pos_vals, bottom=ch_bottom_pos, width=bar_width, color=color, alpha=alpha, label=label)
if np.any(neg_vals < 0):
ax3.bar(years, neg_vals, bottom=ch_bottom_neg, width=bar_width, color=color, alpha=alpha)
ch_bottom_pos += pos_vals
ch_bottom_neg += neg_vals
ax3.plot(years, ch_veraenderung, "o-", color="black", linewidth=1.8, markersize=2.5,
label="Netto-Veränderung", zorder=5)
ax3.axhline(y=0, color="black", linewidth=0.8)
ax3.axvline(x=2002, color="black", linestyle=":", alpha=0.3)
ax3.text(2003, max(ch_einbuergerung) * 0.95, "FZA", fontsize=7, color="black", alpha=0.6)
ax3.set_title("Veränderung Schweizer Staatsangehörige in der Schweiz", fontweight="bold")
ax3.set_ylabel("Personen")
ax3.legend(fontsize=6, loc="upper left")
ax3.grid(True, alpha=0.3, axis="y")
ax3.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, p: f"{x:,.0f}"))
# --- Plot 4: Jährliches Wachstum ausl. Bev. nach Herkunft (gestapelt) ---
ax4 = axes[1, 1]
group_colors = {
"Deutschland": "#FFCC00",
"Portugal": "#006600",
"Kosovo": "#244AA5",
"Übrige EU/EFTA": "#4682B4",
"Übriges Europa": "#B8860B",
"Rest der Welt": "#8B4513",
}
plot_years_nat = nat_stacked.index.values
bottom_pos = np.zeros(len(plot_years_nat))
bottom_neg = np.zeros(len(plot_years_nat))
for col in stack_order:
if col not in nat_stacked.columns:
continue
vals = nat_stacked[col].values
pos_vals = np.where(vals > 0, vals, 0)
neg_vals = np.where(vals < 0, vals, 0)
color = group_colors.get(col, "#999999")
ax4.bar(plot_years_nat, pos_vals, bottom=bottom_pos, width=0.8, color=color, alpha=0.85, label=col)
if np.any(neg_vals < 0):
ax4.bar(plot_years_nat, neg_vals, bottom=bottom_neg, width=0.8, color=color, alpha=0.85)
bottom_pos += pos_vals
bottom_neg += neg_vals
ax4.plot(years[1:], veraenderung_foreign[1:], "o-", color="black", linewidth=1.8, markersize=2.5,
label="Netto-Veränderung", zorder=5)
idx_2023_nat = np.where(plot_years_nat == 2023)[0][0]
total_2023 = bottom_pos[idx_2023_nat]
ax4.annotate(
f"2023: ~{ukraine_s_effect_2023 // 1000}k Statuswechsel\nUkraine S → ständige Bev.",
xy=(2023, total_2023),
xytext=(2015, 105000),
fontsize=6,
arrowprops=dict(arrowstyle="->", color="#f39c12", lw=1.2),
bbox=dict(boxstyle="round,pad=0.3", facecolor="#fef9e7", edgecolor="#f39c12", alpha=0.9),
ha="left", va="top"
)
# 2010: Kosovo taucht erstmals als eigener BFS-Code auf (+63'465), vorher unter
# "Übriges Europa" (Serbien/Jugoslawien-Sammelkategorie) mitgezählt — der fast
# gleich grosse Rückgang dort (-63'583) ist eine Umbuchung, keine Abwanderung.
idx_2010_nat = np.where(plot_years_nat == 2010)[0][0]
neg_2010 = bottom_neg[idx_2010_nat]
ax4.annotate(
"2010: Kosovo erstmals eigener BFS-Code\n(vorher unter Übriges Europa)",
xy=(2010, neg_2010),
xytext=(1993, -55000),
fontsize=6,
arrowprops=dict(arrowstyle="->", color="#f39c12", lw=1.2),
bbox=dict(boxstyle="round,pad=0.3", facecolor="#fef9e7", edgecolor="#f39c12", alpha=0.9),
ha="left", va="top"
)
ax4.axvline(x=2002, color="black", linestyle=":", alpha=0.3)
ax4.text(2002.5, 120000, "FZA", fontsize=7, color="black", alpha=0.6)
ax4.axhline(y=0, color="black", linewidth=0.5)
ax4.set_title("Jährliches Wachstum ausländische Bevölkerung nach Herkunft", fontweight="bold")
ax4.set_ylabel("Personen")
ax4.legend(fontsize=6.5, loc="upper left", ncol=1)
ax4.grid(True, alpha=0.3, axis="y")
ax4.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, p: f"{x:,.0f}"))
# Untere Panels (ax3, ax4) auf gemeinsame y-Achse skalieren, damit Balkenhöhen
# direkt vergleichbar sind (gleiche Personenzahl = gleiche visuelle Höhe).
shared_ymin = min(ch_bottom_neg.min(), bottom_neg.min())
shared_ymax = max(ch_bottom_pos.max(), bottom_pos.max())
shared_margin = (shared_ymax - shared_ymin) * 0.08
shared_ylim = (shared_ymin - shared_margin, shared_ymax + shared_margin)
ax3.set_ylim(shared_ylim)
ax4.set_ylim(shared_ylim)
plt.tight_layout(rect=[0, 0.02, 1, 0.94])
fig.text(0.5, 0.005,
"Datenquelle: BFS – Demografische Bilanz nach Staatsangehörigkeit (ESPOP 1981–2010, STATPOP ab 2011) + "
"Bevölkerungsszenarien 2025–2055 (Referenz/hoch/tief A/B/C-00-2025), live via PxWeb-API",
ha="center", fontsize=7, color="gray")
plt.show()
# ============================================================
# BFS-SZENARIEN — ERGEBNISSE
# ============================================================
print(f"\n{'=' * 70}")
print("BFS-SZENARIEN — GESAMTBEVÖLKERUNG")
print(f"{'=' * 70}")
print("Basis 2024 (BFS definitiv):")
print(f" Gesamtbevölkerung: {total_pop[-1]:>12,.0f}")
print(f" Ausländer: {foreign_pop[-1]:>12,.0f}")
print(f" Schweizer: {swiss_pop[-1]:>12,.0f}")
print(f" Ausländeranteil: {anteil[-1]:>11.1f}%")
idx_2050 = np.where(scen_years == 2050)[0][0]
idx_end = len(scen_years) - 1
for name in scenarios:
print(f"\n{name}:")
print(f" 2050: {scen_total[name][idx_2050]:>12,.0f} ({scen_anteil[name][idx_2050]:.1f}% ausländisch)")
print(f" {scen_years[idx_end]}: {scen_total[name][idx_end]:>12,.0f} ({scen_anteil[name][idx_end]:.1f}% ausländisch)")