Siehe den Blogeintrag zu Idee und Hintergrund dieses Notebooks
import pandas as pd
import numpy as np
import folium
import json
Daten zur Bevölkerung in Niederösterreich, hier beschrieben. Konkret wird diese CSV Datein verwendet.
FILE_BEVOELKERUNG = 'noe_pop_age_sex_2012_2015_lau2.csv'
bev = pd.read_csv(FILE_BEVOELKERUNG, encoding='latin-1', delimiter=';', decimal=',', skiprows=1)
bev[:3]
Formatieren des DatenFrames: Löschen von unbenötigten Spalten, Umbenennen von Spalten, Umformatieren und Berechnen des durchschnittlichen Alters pro Altersgruppe.
bev = bev.drop(['NUTS1', 'NUTS2', 'NUTS3'], axis=1)
bev = bev.rename(columns={'LAU2_CODE':'iso_gemeinde', 'LAU2_NAME':'name_gemeinde'})
bev['iso_gemeinde'] = bev.iso_gemeinde.astype('str')
bev['iso_bezirk'] = bev.iso_gemeinde.str[0:3]
bev['alter_durchschnitt'] = bev.AGE_GROUP.str.split('_', expand=True).replace('', np.nan).astype('float').mean(axis=1)
bev[:3]
Erzeugen einer Liste aller Gemeinden und Bezirke (jeweils ISO code), für die Daten vorhanden sind.
iso_gemeinde = bev.iso_gemeinde.unique().astype('str').tolist()
iso_bezirk = bev.iso_bezirk.unique().astype('str').tolist()
Daten zur graphischen Darstellung von Bezirken und Gemeinden finden sich hier. Konkret verwende ich aus dieser Datei die Dateien gemeinden.json
und bezirke.json
.
FILE_GEOJSON_GEMEINDE = 'gemeinden.json'
FILE_GEOJSON_BEZIRK = 'bezirke.json'
geo_json_data_gemeinde = json.load(open(FILE_GEOJSON_GEMEINDE, encoding='latin-1'))
geo_json_data_bezirk = json.load(open(FILE_GEOJSON_BEZIRK, encoding='latin-1'))
Die GeoJSON Files enthalten Informationen zu allen Gemeinden und Bezirken in Österreich. Da ich mich hier nur Niederösterreich interessiere, erstelle ich neue Geojson Files, die nur diese Information enthalten.
Zuerst wird der Eintrag type
kopiert, dann die Einträge features
auf Niederösterreich eingeschränkt.
geo_json_data_gemeinde['type']
geo_gemeinde = dict()
geo_bezirk = dict()
geo_gemeinde['type'] = geo_json_data_gemeinde['type']
geo_bezirk['type'] = geo_json_data_bezirk['type']
geo_json_data_gemeinde['features'][1]['properties']
geo_gemeinde['features'] = [data for data in geo_json_data_gemeinde['features']
if data['properties']['iso'] in iso_gemeinde]
geo_bezirk['features'] = [data for data in geo_json_data_bezirk['features']
if data['properties']['iso'] in iso_bezirk]
Um in unserem DataFrame bev
auch den Namen des Bezirks zu haben, wird zuerst, basierend auf der Information in geo_bezirk
ein Mapping zwischen Iso-Code und Bezirksname erstellt. Dieses wird dann auf den Iso-Code des Bezirks in bev
angewendet.
map_iso_bezirk = {data['properties']['iso']: data['properties']['name'] for data in geo_bezirk['features']}
map_iso_bezirk
bev['name_bezirk'] = bev.iso_bezirk.map(map_iso_bezirk)
bev[:3]
Dank folium
ist das recht einfach. Zuerst wird eine Karte erstellt, hier muss man die Koordinaten und den Zoomlevel angeben. Dann wird die Bezirksinformation aus dem GeoJSON File hinzugefügt. Und dann noch die Gemeindeinformation, wobei dort noch eine Formatierung (Farbe, Strichbreite, ..) angegeben wird. Zum Schluss zeige die Karte im Notebook an. Die Karte ist interaktiv, sprich man kann darin zoomen, verschieben, ...
m = folium.Map(location=[48.2,15.8], zoom_start=8)
folium.GeoJson(geo_bezirk).add_to(m)
folium.GeoJson(
geo_gemeinde,
style_function=lambda feature:{
'fillColor': 'red',
'color' : 'black',
'weight' : 2,
'dashArray' : '5, 5'
}
).add_to(m)
m
Nun sollen Daten zu den Karten hinzugefügt werden. Als erstes Beispiel sollen die Bezirke basierend auf der Einwohneranzahl eingefärbt werden. Auch das ist mit folium
recht einfach. Zuerst erzeuge ich einen Datenframe mit der entsprechenden Information pro Bezirk. Mit dem Befehl choropleth
lassen sich die Daten dann mit der Karte verbinden. Am Schluss speichere ich die Karte noch in einer extra html-Datei für meinen Blog.
df = bev[bev.YEAR == 2015].groupby(['iso_bezirk', 'name_bezirk'])[['POP_TOTAL']].sum().reset_index()
df = df.sort_values(by='POP_TOTAL')
df[:3]
df[-3:]
m = folium.Map(location=[48.2, 15.8], zoom_start=8)
m.choropleth(geo_str=geo_bezirk,
data=df,
columns=['iso_bezirk', 'POP_TOTAL'],
key_on='feature.properties.iso',
fill_color='YlOrRd', fill_opacity=0.7, line_opacity=0.3
)
m.save('01.html')
m
In welchem Bezirk wohnen prozentuell am meisten Frauen, wo am wenigsten?
bev[:3]
df = bev[bev.YEAR == 2015].groupby(['iso_bezirk', 'name_bezirk'])[['POP_MALE', 'POP_FEMALE']].sum().reset_index()
df['FEMALE_RATIO'] = df.POP_FEMALE / df.POP_MALE * 100
df = df.sort_values('FEMALE_RATIO')
df[:3]
df[-3:]
Man sieht also, dass in Zwettl auf 100 Männer nur 99.5 Frauen kommen, während das in Mödling 108 sind. Auf einer Landkarte sieht das wie folgt aus.
m = folium.Map(location=[48.2, 15.8], zoom_start=8)
m.choropleth(geo_str=geo_bezirk,
data=df,
columns=['iso_bezirk', 'FEMALE_RATIO'],
key_on='feature.properties.iso',
fill_color='YlOrRd', fill_opacity=0.7, line_opacity=0.3)
m.save('02.html')
m