Skip to content
Snippets Groups Projects
Commit b0999122 authored by Christof Kaufmann's avatar Christof Kaufmann
Browse files

Notebooks from applied-cs/data-science@6e9d1b0d

parent 6e2530ec
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id:0003-dee3387c12c6d1f91e4d3d5045948c33001f8302d6baf05ef61af4c6dc4 tags:
# Medizindaten anonymisieren
Dies stellt nur den Code zu den Folien bereit, die das Mini-Beispiel zur
$k$-Anonymisierung zeigen.
## Personenbezogene Daten
Wir starten aber mit den Daten:
%% Cell type:code id:0004-7ef426945d280c8440bbe0ed26aac8ae84a363aa16b154e1277cf508e61 tags:
```
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.preprocessing import robust_scale
columns = ['Name', 'Alter', 'Geschlecht', 'PLZ', 'Größe', 'Gewicht', 'Krankheit']
records = [
('Anna' , 21, 'Weiblich', 76189, 165, 72, 'Grippe'),
('Louis' , 35, 'Männlich', 77021, 170, 75, 'Krebs'),
('Holger' , 39, 'Männlich', 63092, 160, 69, 'Haarausfall'),
('Frederic' , 23, 'Männlich', 63331, 167, 85, 'Muskelzerrung'),
('Anika' , 24, 'Weiblich', 76121, 162, 70, 'Grippe'),
('Peter' , 31, 'Männlich', 77462, 180, 81, 'Vergiftung'),
('Tobias' , 38, 'Männlich', 77109, 175, 79, 'Demenz'),
('Charlotte', 19, 'Weiblich', 83133, 170, 68, 'Akne'),
('Sarah' , 27, 'Weiblich', 89777, 165, 71, 'Akne'),
]
df = pd.DataFrame.from_records(records, columns=columns)
df['Geschlecht'] = df['Geschlecht'].astype('category')
df
```
%% Cell type:markdown id:0006-e4a6b7a2a6ccd44f368ae315d92902b5fa8de3e5f051eeb42c5498cfe35 tags:
## Uneindeutige Werte
Nun wählen wir geeignete Bereiche für jedes Merkmal, sodass hoffentlich
keine eindeutigen Werte mehr heraus kommen. Wir zielen hier auf $i = 2$:
%% Cell type:code id:0007-386867d3d79e0deec0c6fc1b9fa194424379452dc0c086fe987e16cc1e6 tags:
```
dfi = df.drop(columns='Name')
dfi['PLZ'] = dfi['PLZ'] // 1000
dfi['Größe'] = pd.cut(dfi['Größe'], [159, 164, 169, 174, 200], labels=['160 – 164', '165 – 169', '170 – 174', '175 – 180'])
dfi['Gewicht'] = pd.cut(dfi['Gewicht'], [64, 69, 74, 79, 85], labels=['65 – 69', '70 – 74', '75 – 79', '80 – 85'])
dfi['Alter'] = pd.cut(dfi['Alter'], [0, 21, 30, 40], labels=['< 22', '22 – 30', '> 30'])
qids = ['Alter', 'Geschlecht', 'PLZ', 'Größe', 'Gewicht']
```
%% Cell type:markdown id:0008-05c91d6bb7f95eb4a538679b112f797e8bcf94eed053ddc85311239da40 tags:
und prüfen die Häufigkeiten für jedes Merkmal:
%% Cell type:code id:0009-97d1dbc0b4fbc74367262d17987df25bfa2768a249288f7ac095cccab89 tags:
```
print('Mindesthäufigkeiten der Werte für jedes Feature (i = 2):'); \
print(dfi[qids].apply(lambda col:col.value_counts().min()))
```
%% Output
Mindesthäufigkeiten der Werte für jedes Feature (i = 2):
Alter 2
Geschlecht 4
PLZ 1
Größe 2
Gewicht 2
dtype: int64
%% Cell type:markdown id:0010-cce9a11cba1b2ad8fb05c761fd70af7cdf5b3a6927d291695b52d8e40f0 tags:
Hmm… die PLZ hat eine Mindesthäufigkeit von 1. Wir schauen, woran das
liegt:
%% Cell type:code id:0011-3dc0b3f7a194d1d35a9b76000f1f7a5f88116194561bce476e8614cdf73 tags:
```
dfi['PLZ'].value_counts()
```
%% Output
PLZ
77 3
76 2
63 2
83 1
89 1
Name: count, dtype: int64
%% Cell type:markdown id:0012-15f1791204b3e3b60671637844ac1d0742204b81fdce703f99431724496 tags:
OK, wir bessern nach indem wir die PLZen, die mit 8 beginnen weiter
abschneiden:
%% Cell type:code id:0013-dd1f0362dec9837cb9021ce4da7929123e7425f25b44f7116079d56076f tags:
```
plz_1d = dfi['PLZ'] // 10
dfi.loc[plz_1d == 8, 'PLZ'] = plz_1d[plz_1d == 8]
dfi['PLZ'].value_counts()
```
%% Output
PLZ
77 3
76 2
63 2
8 2
Name: count, dtype: int64
%% Cell type:markdown id:0014-07cf3c336b349d34b1d672dab7e45e01599209f2ea393cc63fb8a595e81 tags:
Nun passt es. Der DataFrame sieht nun so aus:
%% Cell type:code id:0015-1bd3f9992c275ed397a515e77b98b07ad16f732207fd88b9f05f4660ce2 tags:
```
dfi
```
%% Output
Alter Geschlecht PLZ Größe Gewicht Krankheit
0 < 22 Weiblich 76 165 – 169 70 – 74 Grippe
1 > 30 Männlich 77 170 – 174 75 – 79 Krebs
2 > 30 Männlich 63 160 – 164 65 – 69 Haarausfall
3 22 – 30 Männlich 63 165 – 169 80 – 85 Muskelzerrung
4 22 – 30 Weiblich 76 160 – 164 70 – 74 Grippe
5 > 30 Männlich 77 175 – 180 80 – 85 Vergiftung
6 > 30 Männlich 77 175 – 180 75 – 79 Demenz
7 < 22 Weiblich 8 170 – 174 65 – 69 Akne
8 22 – 30 Weiblich 8 165 – 169 70 – 74 Akne
%% Cell type:markdown id:0016-9a3e4040911dc8fbfeceeb683b5f7df49e98d6a03f8a83ff1415147d428 tags:
Die Kombinationen sind allerdings noch eindeutig:
%% Cell type:code id:0017-aacadffcf8a75c69bddd41396fa48e1c5b6a5377d7bf18f3b331d9c0fd1 tags:
```
dfi[qids].value_counts().min()
```
%% Output
1
%% Cell type:markdown id:0019-5618810b7f82ebbabcbead0b185b007294bf6a193d007ba9bcb32fd16e9 tags:
## k-Anonymisierung
Wir zielen auf $k = 2$
%% Cell type:code id:0020-dd714bb86a726ab44415ba3d2ec2b831ad856c015571b2fb327b2372189 tags:
```
dfk = df.drop(columns='Name')
X = dfk[qids].drop(columns='Geschlecht')
X_scaled = robust_scale(X)
kmeans = KMeans(n_clusters=3, random_state=42, n_init=5)
labels = kmeans.fit_predict(X_scaled)
```
%% Cell type:markdown id:0021-3b265300550256206ee10db37a47a7fe8a26d00c9f4b889dcadce922865 tags:
Wir schauen auf die kleinste Clustergröße (sollte größer oder gleich $k$
sein):
%% Cell type:code id:0022-682a647ff3ced3a5853b905af6a11364b00d5a5b63ee2768b9b350eedc9 tags:
```
np.unique(labels, return_counts=True)[1].min()
```
%% Output
2
%% Cell type:markdown id:0023-118d58a6f1615a893016c0bd9d61fcc650c8ad9f5f446adc10d99f377f6 tags:
Das passt. Dann können wir die Intervalle von min bis max jedes Clusters
erhalten und die ursprünglichen Daten ersetzen:
%% Cell type:code id:0024-df791042ec3a51c2569004a8edc2f32992946921bc7508459d5ef2ed4c8 tags:
```
g = X.groupby(labels)
mins, maxs = g.transform('min'), g.transform('max')
mins['PLZ'] = mins['PLZ'] // 1000
maxs['PLZ'] = maxs['PLZ'] // 1000
ints = mins.astype(str) + ' – ' + maxs.astype(str)
dfk[ints.columns] = ints
```
%% Cell type:markdown id:0025-d5a10999a95091b501a47a23f0b385654ad1956eed656e5271545f482e1 tags:
Jetzt sind die Kombinationen nicht mehr eindeutig:
%% Cell type:code id:0026-e9330560a49eb54da8ecf72439d755a0dc6591441a28f05b87d6ceb4d2d tags:
```
print(dfk[qids].value_counts().min())
```
%% Output
2
%% Cell type:markdown id:0027-0cbd55253e5d55d5fc7b81b286c6931e98dd173db8c520e36e021eb0463 tags:
Der Datensatz sieht nun so aus:
%% Cell type:code id:0028-9cf26794f86b8444987f2f06396085cd427fdb5dae8d7eb21c6b581a20a tags:
```
dfk
```
%% Output
Alter Geschlecht PLZ Größe Gewicht Krankheit
0 29.8 Weiblich 76780.4 170.4 75.4 Grippe
1 29.8 Männlich 76780.4 170.4 75.4 Krebs
2 31.0 Männlich 63211.5 163.5 77.0 Haarausfall
3 31.0 Männlich 63211.5 163.5 77.0 Muskelzerrung
4 29.8 Weiblich 76780.4 170.4 75.4 Grippe
5 29.8 Männlich 76780.4 170.4 75.4 Vergiftung
6 29.8 Männlich 76780.4 170.4 75.4 Demenz
7 23.0 Weiblich 86455.0 167.5 69.5 Akne
8 23.0 Weiblich 86455.0 167.5 69.5 Akne
%% Cell type:markdown id:0029-e6815f3094ce50cde54681a281519fe62647aba791b2637484b16fe1968 tags:
Alternativ kann man auch Mittelwerte statt Intervalle verwenden:
%% Cell type:code id:0030-f123500ca3c9aad864acf5d88c4fa1183608f14199af1246255029c2a7e tags:
```
means = g.transform('mean')
dfk[means.columns] = means
dfk
```
%% Cell type:markdown id:0014-5250011056ff5724f9be1f73922489ac25747bd60219961556539182e36 tags:
%% Cell type:markdown id:0014-2acead481ecc6c0d537ae8febd1f3a0d4664a97dccfbc92444c634eb396 tags:
# Book-Crossing
In dieser Aufgabe ist ein echter Datensatz aus dem Jahr 2004 gegeben,
bestehend aus:
- `books.csv.zst`
- `ratings.csv.zst`
- `users.csv.zst`
Er stammt von – einer Community um Bücher weiterzugeben und zu tracken.
Dabei können Bücher auch bewertet werden.
*Hinweis: Die Bewertungen haben eine skala von 1 – 10. Eine 0 meint
*implizite Bewertung*, also es wurde keine Bewertung abgegeben. Der
Begriff *implizite Bewertung\* steht dafür, dass man aus anderen Daten
*Hinweis: Die Bewertungen haben eine Skala von 1 – 10. Eine 0 meint
**implizite Bewertung**, also es wurde keine Bewertung abgegeben. Der
Begriff **implizite Bewertung** steht dafür, dass man aus anderen Daten
eine Bewertung erahnen kann – hier z. B., dass es überhaupt gelesen
wurde.\*
wurde.*
Laden Sie die Daten, säubern sie ein bisschen und versuchen Sie zwei
Fragen zu beantworten:
1. Welches ist das meistbewertetste Buch (implizit und explizit) von
deutschen Nutzern und welche Durchschnittsbewertung (explizit) hat
es von deutschen Nutzern?
2. Ermitteln Sie die 20 populärsten Bücher von deutschen Nutzern.
Popularität definieren wir einfach als Summe von Bewertung eines
Buches (ohne, wie beim Mittelwert, durch die Anzahl zu teilen).
Plotten Sie Popularität gegen mittlere Bewertung (von deutschen
Nutzern) als Scatter-Plot und überlegen, was es bedeutet, wenn
Bücher eine große Popularität, aber eine relativ geringe mittlere
Bewertung haben. Welches ist das bestbewertetste Buch und welches
das schlechtbewertetste Buch der 20 populärsten Bücher (von
deutschen Nutzern)?
## Lösung
Die CSV-Dateien sind mit
[ZStandard](https://de.wikipedia.org/wiki/Zstandard) komprimiert. Die
Dateigrößen betragen
- `books.csv.zst`: 6.3 MiB (unkomprimiert: 22.5 MiB)
- `ratings.csv.zst`: 4.5 MiB (unkomprimiert: 21.6 MiB)
- `users.csv.zst`: 1.8 MiB (unkomprimiert: 10.5 MiB)
Komprimieren lohnt sich also und kann, falls das conda-forge `zstd`
installiert ist, einfach über die Kommandozeile erfolgen:
``` bash
zstd -19 -T8 DATEI
```
wobei `-19` für den höchsten Kompressionsgrad steht und `-T8` für acht
Threads. Aus Python heraus, kann man einen DataFrame beim Schreiben
einfach komprimieren:
``` bash
df.to_csv('dateiname.csv.zst', compression='zstd')
```
Aber nun zur eigentlichen Aufgabe. Wir laden zunächst die Bücherdaten
und weil wir das vorher schon mal ausprobiert haben, wissen wir aus
`books.Year.value_counts().sort_index()`, dass viele 0en vorkommen und
einige Werte nach 2004, obwohl der Datensatz 2004 heraus kam. Diese
setzen wir auf NaN.
%% Cell type:code id:0015-bb9fc0c5122136aaf4c40cf8da1b7b7b7d57f276031fcfa306591d1b6f4 tags:
```
import numpy as np
import pandas as pd
books = pd.read_csv('books.csv.zst', na_values={'Year': 0}, index_col='ISBN')
books.loc[books.Year > 2004, 'Year'] = np.nan
```
%% Cell type:markdown id:0016-b5d50ad15c785ba6ec147e8b66e53bad3fb11b1ec557c188c12d75fc900 tags:
Leider scheint die Datei seltsam encodiert zu sein, was man z. B. an
folgendem Eintrag sehen kann:
%% Cell type:code id:0017-7f35cbf4ac608cf4068a67cc564d9d59f3dce233a983752cdf947a38399 tags:
```
books.loc['3404148665']
```
%% Output
Title Illuminati.
Author Dan Brown
Year 2003.0
Publisher L�¼bbe
Name: 3404148665, dtype: object
%% Cell type:markdown id:0019-e0511a78a997280f3a22641267ff7cbbd003003b3c0d98437eca200bf13 tags:
Es hat keine bekannte Kodierung funktioniert. Aber das ist auch nicht so
schlimm. Wir lassen es so.
Beim Laden der Bewertungen setzen wir den Wert für die implizite
Bewertung auf `np.nan`. Für das Alter löschen wir Werte für 0 – 12
Jahren und ab 100 Jahren. Dann fügen wir die drei DataFrames – genau wie
in den Folien – mit `pd.concat` zusammen.
%% Cell type:code id:0020-6275e339c7956156b66d6323bf00d5910e0b902ea22ec80ce5c396e6573 tags:
```
ratings = pd.read_csv('ratings.csv.zst', na_values={'Rating': 0}, index_col=['User-ID', 'ISBN'])
users = pd.read_csv('users.csv.zst', index_col='User-ID')
users.loc[(users.Age > 100) | (users.Age < 13), 'Age'] = np.nan
data = users.join(ratings).join(books)
```
%% Cell type:markdown id:0021-6ff1d925c5b700acad8db0743933296e31411e8c394b4144209423af064 tags:
Dann versuchen wir aus der Location das Land heraus zu suchen, aber wenn
man sich die Länderdaten dann mal mit `country.unique()` anschaut, sieht
man, dass viel Handarbeit für die Bereinigung notwendig ist:
%% Cell type:code id:0022-f7b3a4ca4a0003853f39293d35833e1bf166f7aae392bae05bf3575686b tags:
```
country = data.Location.str.rsplit(n=1, expand=True)[1]
country = country.str.replace('"', '').str.replace(',', '').str.replace('.', '').str.replace('>', '').str.replace('!', '')
country[(country == '\\n/a\\') | (country == 'n/a') | (country == 'na') | (country == 'away') | (country == 'tomorrow') | (country == 'space') | (country == 'universe') | (country == 'a') | (country == 'x') | (country == '')] = np.nan
country[(country == 'baden-württemberg') | (country == 'baden-wuerttemberg') | (country == 'nrw') | (country == 'deutschland') | (country == 'hamburg') | (country == 'hessen') | (country == 'sachsen') | (country == 'berlin') | (country == 'brandenburg') | (country == 'bremen') | (country == 'nordrhein-westfalen') | (country == 'niedersachsen')] = 'germany'
```
%% Cell type:markdown id:0023-0088f97deeddb19f8bc2cf72898870feab39ee266525f24e27c44cc4076 tags:
Damit können wir jetzt nach deutschen Nutzern filtern. Dann gruppieren
wir nach ISBN und ermitteln die Größen der Gruppen, die angeben, wie
häufig ein Buch bewertet wurde. Die sortieren wir und nehmen den ersten
Eintrag. Für die mittlere Bewertung des Buchs holen wir uns die Spalte
`Rating` von der Gruppe mit der ISBN-Nummer und mitteln sie.
%% Cell type:code id:0024-8bab088c1940b6a912779d502778c79f14d8902171357762f57ea7fd065 tags:
```
data_de = data[country == 'germany']
group_de = data_de.groupby('ISBN')
most_frequent = group_de.size().sort_values(ascending=False).iloc[[0]] # keep Series, we need index and count value
mean_rating = group_de.get_group(most_frequent.index[0])['Rating'].mean()
```
%% Cell type:code id:0025-1967d62696af602038f7ecd1265eccc02956bc05db5cc44e9786086b1fc tags:
```
print('Das meistbewertetste Buch unter deutschen Nutzern ist:'); \
print(books.loc[most_frequent.index[0]]); \
print(f'Es wurde {most_frequent.squeeze()}x bewertet und hat eine Durchschnittsbewertung von {mean_rating:.2f}.')
```
%% Output
Das meistbewertetste Buch unter deutschen Nutzern ist:
Title Wild Animus
Author Rich Shapero
Year 2004.0
Publisher Too Far
Name: 0971880107, dtype: object
Es wurde 141x bewertet und hat eine Durchschnittsbewertung von 4.43.
%% Cell type:markdown id:0026-0088f97deeddb19f8bc2cf72898870feab39ee266525f24e27c44cc4076 tags:
Damit können wir jetzt nach deutschen Nutzern filtern. Dann gruppieren
wir nach ISBN und ermitteln die Größen der Gruppen, die angeben, wie
häufig ein Buch bewertet wurde. Die sortieren wir und nehmen den ersten
Eintrag. Für die mittlere Bewertung des Buchs holen wir uns die Spalte
`Rating` von der Gruppe mit der ISBN-Nummer und mitteln sie.
%% Cell type:code id:0027-23a74603aa2626099b2936113ce6587a3a5a7168ff44a103851ccea35f3 tags:
```
popular_de = group_de['Rating'].sum().sort_values(ascending=False)
popular_de.name = 'Popularity'
rating_pop_de = data_de.groupby('ISBN')['Rating'].mean()
feat_de = pd.concat((rating_pop_de.loc[popular_de.index[:20]], popular_de.iloc[:20]), axis='columns')
# books.loc['3442541751']
```
%% Cell type:code id:0028-4a411135507534cc54b452dce68626a99de75677db3e96939e4faa1b4d2 tags:
```
print('Das Buch unter den 20 populärsten Bücher, das die schlechteste Bewertung ist:'); \
print(books.loc[feat_de.Rating.idxmin()])
```
%% Output
Das Buch unter den 20 populärsten Bücher, das die schlechteste Bewertung ist:
Title Wild Animus
Author Rich Shapero
Year 2004.0
Publisher Too Far
Name: 0971880107, dtype: object
%% Cell type:markdown id:0029-1178d06cf897baa5f235115d007e2639d4fb153fad1328d9c4b4864d05e tags:
Das ist dasselbe Buch wie vorhin!
%% Cell type:code id:0030-95bc5af9bae3ad8fa6b4a79f6e9eba8c9481ad7c454089221a212cdb18f tags:
```
print('Das am besten bewertete Buch ist leider nicht in den Daten enthalten:', feat_de.Rating.idxmax() in books.index)
```
%% Output
Das am besten bewertete Buch ist leider nicht in den Daten enthalten: False
%% Cell type:markdown id:0032-c19f1102d9cbccc06ab8668796de51df8be368acff958b01e0028cd267f tags:
Eine kurze Internetrecherche kommt zum Schluss, dass sich bei dem am
besten bewerteten Buch um Faust handelt.
Und hier der Plot:
%% Cell type:code id:0033-6dd9ba2730f89dd049b0c9f06e394371d71c60eb5987b4bec46c56867da tags:
```
feat_de.plot.scatter('Rating', 'Popularity')
```
......
%% Cell type:markdown id:0007-0eae7603b6d19844fc278f91526d3cdaf0b09213b80d7564d3b1c6d164d tags:
%% Cell type:markdown id:0007-ef5ca2d1fed4295c92f0dde89f1e3844ce22c8da501ff51f16b0c78b514 tags:
# Book-Crossing
In dieser Aufgabe ist ein echter Datensatz aus dem Jahr 2004 gegeben,
bestehend aus:
- `books.csv.zst`
- `ratings.csv.zst`
- `users.csv.zst`
Er stammt von – einer Community um Bücher weiterzugeben und zu tracken.
Dabei können Bücher auch bewertet werden.
*Hinweis: Die Bewertungen haben eine skala von 1 – 10. Eine 0 meint
*implizite Bewertung*, also es wurde keine Bewertung abgegeben. Der
Begriff *implizite Bewertung\* steht dafür, dass man aus anderen Daten
*Hinweis: Die Bewertungen haben eine Skala von 1 – 10. Eine 0 meint
**implizite Bewertung**, also es wurde keine Bewertung abgegeben. Der
Begriff **implizite Bewertung** steht dafür, dass man aus anderen Daten
eine Bewertung erahnen kann – hier z. B., dass es überhaupt gelesen
wurde.\*
wurde.*
Laden Sie die Daten, säubern sie ein bisschen und versuchen Sie zwei
Fragen zu beantworten:
1. Welches ist das meistbewertetste Buch (implizit und explizit) von
deutschen Nutzern und welche Durchschnittsbewertung (explizit) hat
es von deutschen Nutzern?
2. Ermitteln Sie die 20 populärsten Bücher von deutschen Nutzern.
Popularität definieren wir einfach als Summe von Bewertung eines
Buches (ohne, wie beim Mittelwert, durch die Anzahl zu teilen).
Plotten Sie Popularität gegen mittlere Bewertung (von deutschen
Nutzern) als Scatter-Plot und überlegen, was es bedeutet, wenn
Bücher eine große Popularität, aber eine relativ geringe mittlere
Bewertung haben. Welches ist das bestbewertetste Buch und welches
das schlechtbewertetste Buch der 20 populärsten Bücher (von
deutschen Nutzern)?
Hier Ihr Code:
%% Cell type:code id:0008-44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 tags:
```
```
......
%% Cell type:markdown id:0002-a8f53080980ed874d387cc3bfb8c5d5ed65d7fdb4139eb4ed15105d383c tags:
# Flächenverzerrung bei Projektionen
In dieser Demo kann man sich die Flächenverzerrungen bei verschiedenen
Projektionen auf einer interaktiven Karte anschauen.
Zunächst laden wir eine GeoJSON-Datei mit groben Ländergrenzen herunter
und speichern sie lokal. Und wir laden auch eine mehr oder weniger gute
Datei mit den Referenzgrößen. Mit etwas Vorverarbeitung können wir die
Datensätze in `countries` zusammenführen, was wir aber erst im nächsten
Schritt machen.
%% Cell type:code id:0003-4c6fa2e8089a613e6a3300cf4bd6fe01d88ed16280b83e1e378065b75a9 tags:
```
from sklearn.datasets import fetch_openml
import geopandas as gpd
url = 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_admin_0_countries.geojson'
geo_countries = gpd.read_file(url)
path = 'geo_countries.geojson'
geo_countries.to_file(path)
geo_countries = geo_countries[['name', 'iso_a3', 'geometry']].set_index('iso_a3')
countries_df, _ = fetch_openml('Countries-of-the-World', as_frame=True, return_X_y=True)
countries_df = countries_df.rename(columns={'Area_(sq._mi.)': 'ref_area', 'Country': 'name'}) # unit was km² already
countries_df = countries_df[['name', 'ref_area']]
countries_df['name'] = countries_df.name.str.strip()
```
%% Cell type:markdown id:0004-5231db90b71a4c73fd2da36a3ca092775410c9eb4e21d28e7de691474ef tags:
Hier kann ein EPSG-Code gewählt werden, der die Projektion definiert.
Damit bestimmt sich wie groß die projizierte Fläche ist.
%% Cell type:code id:0005-b64aa750773482384137dd8fa7e5b5b069b9c692b3395c5c211650bf153 tags:
```
countries = geo_countries.merge(countries_df, on='name')
# epsg = 25832 # UTM Zone 32N, good for germany, does not work on global scale
# epsg = 4087
# epsg = 3395
epsg = 3857
# epsg = 4326
# ...
countries = countries.to_crs(f'EPSG:{epsg}')
countries['proj_area'] = countries.area / 1e6 # in km²
countries['rel_diff_area'] = ((countries['proj_area'] / countries['ref_area'] - 1).round(2) * 100).astype(int) # in percent
vmax = countries['rel_diff_area'].abs().quantile(0.97)
countries.explore(column='rel_diff_area', tooltip=['name', 'rel_diff_area', 'proj_area', 'ref_area'], popup=True, tiles="CartoDB voyager", vmin=-vmax, vmax=vmax, cmap='coolwarm', attribution=countries.crs.name)
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment