In einer explorativen Datenanalyse (EDA) betrachtet man verschiedene deskriptive Statistiken und bi-variate Zusammenhänge, um sich zum einen mit den Daten vertraut zu machen und zum anderen weitere statistische Modellierungen vorzubereiten. In diesem ersten Teil liegt der Fokus darauf, einen Überblick über die Daten zu gewinnen und sie neu zu zuschneiden (Re-Kodierung).
In diesem Bespiel nutzen wir Daten vom World Happiness Report (2019). Sie können hier abgerufen werden: https://worldhappiness.report/ed/2019/
Wir benötigen die folgenden Bibliotheken:
## Bibliotheken importieren
import os
import numpy as np
import pandas as pd
import warnings
## Generelle Einstellungen
warnings.filterwarnings('ignore')
Für CSV- und Excel-Dateien stellt pandas Funktionen zum Einlesen bereit. Ein so eingelesener Datensatz wird in einem Pandas Dataframe gespeichert.
Mit dem Befehl chdir aus dem OS-Modul kann das Arbeitsverzeichnis gewechselt werden. Die Daten liegen in einem Unterverzeichnis "Data".
## Daten einlesen
source_path=".\Data"
source_filename="WHR2019_Chapter2OnlineData.xls"
os.chdir(source_path)
# Default für read_excel ist 1. Worksheet (sheet_name=0) und
# 1. Reihe als Spaltenname (header=0)
df = pd.read_excel(source_filename)
Eine Überblick über den Datensatz geben
shape
(Dimensionen)columns
(Spaltennamen)info()
(Anzahl Beobachtungen, Spaltennamen und Datentypen)head()
(erste Zeilen)# Anzahl Zeilen und Spalten
df.shape
(1704, 26)
# Spaltennamen
df.columns
Index(['Country name', 'Year', 'Life Ladder', 'Log GDP per capita', 'Social support', 'Healthy life expectancy at birth', 'Freedom to make life choices', 'Generosity', 'Perceptions of corruption', 'Positive affect', 'Negative affect', 'Confidence in national government', 'Democratic Quality', 'Delivery Quality', 'Standard deviation of ladder by country-year', 'Standard deviation/Mean of ladder by country-year', 'GINI index (World Bank estimate)', 'GINI index (World Bank estimate), average 2000-16', 'gini of household income reported in Gallup, by wp5-year', 'Most people can be trusted, Gallup', 'Most people can be trusted, WVS round 1981-1984', 'Most people can be trusted, WVS round 1989-1993', 'Most people can be trusted, WVS round 1994-1998', 'Most people can be trusted, WVS round 1999-2004', 'Most people can be trusted, WVS round 2005-2009', 'Most people can be trusted, WVS round 2010-2014'], dtype='object')
# Spalten, Datentypen und fehlende Werte
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 1704 entries, 0 to 1703 Data columns (total 26 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Country name 1704 non-null object 1 Year 1704 non-null int64 2 Life Ladder 1704 non-null float64 3 Log GDP per capita 1676 non-null float64 4 Social support 1691 non-null float64 5 Healthy life expectancy at birth 1676 non-null float64 6 Freedom to make life choices 1675 non-null float64 7 Generosity 1622 non-null float64 8 Perceptions of corruption 1608 non-null float64 9 Positive affect 1685 non-null float64 10 Negative affect 1691 non-null float64 11 Confidence in national government 1530 non-null float64 12 Democratic Quality 1558 non-null float64 13 Delivery Quality 1559 non-null float64 14 Standard deviation of ladder by country-year 1704 non-null float64 15 Standard deviation/Mean of ladder by country-year 1704 non-null float64 16 GINI index (World Bank estimate) 643 non-null float64 17 GINI index (World Bank estimate), average 2000-16 1502 non-null float64 18 gini of household income reported in Gallup, by wp5-year 1335 non-null float64 19 Most people can be trusted, Gallup 180 non-null float64 20 Most people can be trusted, WVS round 1981-1984 125 non-null float64 21 Most people can be trusted, WVS round 1989-1993 220 non-null float64 22 Most people can be trusted, WVS round 1994-1998 618 non-null float64 23 Most people can be trusted, WVS round 1999-2004 491 non-null float64 24 Most people can be trusted, WVS round 2005-2009 630 non-null float64 25 Most people can be trusted, WVS round 2010-2014 671 non-null float64 dtypes: float64(24), int64(1), object(1) memory usage: 346.2+ KB
Mit Hilfe von describe()
können wir uns die Verteilung numerischer Variablen ausgeben lassen. Die Funktion kann auf den gesamten Datensatz (DataFrame) angewendet werden, eine Auswahl oder auf eine einzelne Spalte.
# Zusammenfassung aller numerischen Variablen
df.describe()
Year | Life Ladder | Log GDP per capita | Social support | Healthy life expectancy at birth | Freedom to make life choices | Generosity | Perceptions of corruption | Positive affect | Negative affect | ... | GINI index (World Bank estimate) | GINI index (World Bank estimate), average 2000-16 | gini of household income reported in Gallup, by wp5-year | Most people can be trusted, Gallup | Most people can be trusted, WVS round 1981-1984 | Most people can be trusted, WVS round 1989-1993 | Most people can be trusted, WVS round 1994-1998 | Most people can be trusted, WVS round 1999-2004 | Most people can be trusted, WVS round 2005-2009 | Most people can be trusted, WVS round 2010-2014 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 1704.000000 | 1704.000000 | 1676.000000 | 1691.000000 | 1676.000000 | 1675.000000 | 1622.000000 | 1608.000000 | 1685.000000 | 1691.000000 | ... | 643.000000 | 1502.000000 | 1335.000000 | 180.000000 | 125.000000 | 220.000000 | 618.000000 | 491.000000 | 630.000000 | 671.000000 |
mean | 2012.332160 | 5.437155 | 9.222456 | 0.810570 | 63.111971 | 0.733829 | 0.000079 | 0.751315 | 0.709368 | 0.265679 | ... | 0.370000 | 0.385438 | 0.447771 | 0.226295 | 0.390480 | 0.283925 | 0.249574 | 0.268070 | 0.264336 | 0.237493 |
std | 3.688072 | 1.121149 | 1.185794 | 0.119210 | 7.583622 | 0.144115 | 0.163365 | 0.186074 | 0.107984 | 0.084707 | ... | 0.083232 | 0.082396 | 0.108505 | 0.119079 | 0.123309 | 0.113226 | 0.118126 | 0.145120 | 0.160169 | 0.157482 |
min | 2005.000000 | 2.661718 | 6.457201 | 0.290184 | 32.299999 | 0.257534 | -0.336385 | 0.035198 | 0.362498 | 0.083426 | ... | 0.240000 | 0.211000 | 0.200969 | 0.066618 | 0.176535 | 0.066020 | 0.048720 | 0.075872 | 0.038242 | 0.031518 |
25% | 2009.000000 | 4.610970 | 8.304428 | 0.747512 | 58.299999 | 0.638436 | -0.115534 | 0.696083 | 0.621855 | 0.205414 | ... | 0.305000 | 0.321429 | 0.368424 | 0.139773 | 0.290300 | 0.223553 | 0.176876 | 0.155833 | 0.144976 | 0.118725 |
50% | 2012.000000 | 5.339557 | 9.406206 | 0.833098 | 65.000000 | 0.752731 | -0.022080 | 0.805775 | 0.718541 | 0.254544 | ... | 0.352000 | 0.371000 | 0.426541 | 0.198450 | 0.380174 | 0.292383 | 0.229924 | 0.232000 | 0.198380 | 0.193531 |
75% | 2015.000000 | 6.273522 | 10.193060 | 0.904432 | 68.300003 | 0.848155 | 0.093522 | 0.876458 | 0.801530 | 0.314896 | ... | 0.428000 | 0.432200 | 0.514803 | 0.281627 | 0.478149 | 0.341741 | 0.294242 | 0.385469 | 0.391370 | 0.335000 |
max | 2018.000000 | 8.018934 | 11.770276 | 0.987343 | 76.800003 | 0.985178 | 0.677743 | 0.983276 | 0.943621 | 0.704590 | ... | 0.634000 | 0.626000 | 0.961435 | 0.640332 | 0.571719 | 0.594595 | 0.647737 | 0.637185 | 0.737305 | 0.661757 |
8 rows × 25 columns
# Auswahl
df[["Life Ladder", "Generosity"]].describe()
Life Ladder | Generosity | |
---|---|---|
count | 1704.000000 | 1622.000000 |
mean | 5.437155 | 0.000079 |
std | 1.121149 | 0.163365 |
min | 2.661718 | -0.336385 |
25% | 4.610970 | -0.115534 |
50% | 5.339557 | -0.022080 |
75% | 6.273522 | 0.093522 |
max | 8.018934 | 0.677743 |
# Einzelne Spalte
df["Life Ladder"].describe()
count 1704.000000 mean 5.437155 std 1.121149 min 2.661718 25% 4.610970 50% 5.339557 75% 6.273522 max 8.018934 Name: Life Ladder, dtype: float64
Um einen Eindruck von der Verteilung numerischer Variablen zu bekommen, können wir sie mit Hilfe von plot()
visualisieren. Die Methode plot()
für Pandas DataFrames greift im Hintergrund auf die Bibliothek matplotlib zurück, die sehr umfangreiche Visualisierungsmöglichkeiten liefert. Mit Hilfe von plot()
können wir direkt auf die Hauptfunktionen von matplotlib zugreifen. Über den Parameter kind
wird die Art der Grafik festgelegt. Für ein Histogram benötigen wir kind="hist
.
df["Life Ladder"].plot(kind="hist")
<AxesSubplot:ylabel='Frequency'>
Mit bins
kann die Anzahl der Bins vorgeben werden.
df["Life Ladder"].plot(kind="hist", bins=100)
<AxesSubplot:ylabel='Frequency'>
Eine glattere Verteilung ergibt die Einstellung kind="density"
.
df["Life Ladder"].plot(kind="density")
<AxesSubplot:ylabel='Density'>
Der Datensatz hat insgesamt 1704 Zeilen. Die Anzahl der non-null Werte unterscheidet sich stark zwischen den Spalten. Es gibt viele Spalten mit fehlenden Werten, wie etwa "Most people can be trusted, WVS round 2005-2009". Die Methode isnull()
hilft, fehlende Werte zu identifizieren und das Muster fehlender Werte weiter zu analysieren. Mit Hilfe von any()
oder sum()
kann eine Liste der Spalten mit fehlenden Werte erstellt bzw. die Anzahl der fehlenden Werte pro Spalte ermittelt werden.
# Welche Spalten enhalten fehlende Werte
df.isnull().any()
Country name False Year False Life Ladder False Log GDP per capita True Social support True Healthy life expectancy at birth True Freedom to make life choices True Generosity True Perceptions of corruption True Positive affect True Negative affect True Confidence in national government True Democratic Quality True Delivery Quality True Standard deviation of ladder by country-year False Standard deviation/Mean of ladder by country-year False GINI index (World Bank estimate) True GINI index (World Bank estimate), average 2000-16 True gini of household income reported in Gallup, by wp5-year True Most people can be trusted, Gallup True Most people can be trusted, WVS round 1981-1984 True Most people can be trusted, WVS round 1989-1993 True Most people can be trusted, WVS round 1994-1998 True Most people can be trusted, WVS round 1999-2004 True Most people can be trusted, WVS round 2005-2009 True Most people can be trusted, WVS round 2010-2014 True dtype: bool
# Anzahl der fehlenden Werte pro Spalte
df.isnull().sum()
Country name 0 Year 0 Life Ladder 0 Log GDP per capita 28 Social support 13 Healthy life expectancy at birth 28 Freedom to make life choices 29 Generosity 82 Perceptions of corruption 96 Positive affect 19 Negative affect 13 Confidence in national government 174 Democratic Quality 146 Delivery Quality 145 Standard deviation of ladder by country-year 0 Standard deviation/Mean of ladder by country-year 0 GINI index (World Bank estimate) 1061 GINI index (World Bank estimate), average 2000-16 202 gini of household income reported in Gallup, by wp5-year 369 Most people can be trusted, Gallup 1524 Most people can be trusted, WVS round 1981-1984 1579 Most people can be trusted, WVS round 1989-1993 1484 Most people can be trusted, WVS round 1994-1998 1086 Most people can be trusted, WVS round 1999-2004 1213 Most people can be trusted, WVS round 2005-2009 1074 Most people can be trusted, WVS round 2010-2014 1033 dtype: int64
Um aus der Abfrage eine Liste der Spaltennamen zu generieren, lassen wir nur die Spaltennamen, die hier den Index bilden, ausgeben und konvertieren das Ergebnis in eine Liste.
# Liste der Spalten mit fehlenden Werten
df.isnull().any().index.to_list()
['Country name', 'Year', 'Life Ladder', 'Log GDP per capita', 'Social support', 'Healthy life expectancy at birth', 'Freedom to make life choices', 'Generosity', 'Perceptions of corruption', 'Positive affect', 'Negative affect', 'Confidence in national government', 'Democratic Quality', 'Delivery Quality', 'Standard deviation of ladder by country-year', 'Standard deviation/Mean of ladder by country-year', 'GINI index (World Bank estimate)', 'GINI index (World Bank estimate), average 2000-16', 'gini of household income reported in Gallup, by wp5-year', 'Most people can be trusted, Gallup', 'Most people can be trusted, WVS round 1981-1984', 'Most people can be trusted, WVS round 1989-1993', 'Most people can be trusted, WVS round 1994-1998', 'Most people can be trusted, WVS round 1999-2004', 'Most people can be trusted, WVS round 2005-2009', 'Most people can be trusted, WVS round 2010-2014']
Einzelne Elemente eines Datensatzes können ausgewählt bzw. gefiltert werden.
# Die Spalte Country name
df["Country name"]
0 Afghanistan 1 Afghanistan 2 Afghanistan 3 Afghanistan 4 Afghanistan ... 1699 Zimbabwe 1700 Zimbabwe 1701 Zimbabwe 1702 Zimbabwe 1703 Zimbabwe Name: Country name, Length: 1704, dtype: object
# Die Spalten Country Name und Year
# (mehrere Spalten müssen als Liste übergeben werden)
df[["Country name", "Year"]]
Country name | Year | |
---|---|---|
0 | Afghanistan | 2008 |
1 | Afghanistan | 2009 |
2 | Afghanistan | 2010 |
3 | Afghanistan | 2011 |
4 | Afghanistan | 2012 |
... | ... | ... |
1699 | Zimbabwe | 2014 |
1700 | Zimbabwe | 2015 |
1701 | Zimbabwe | 2016 |
1702 | Zimbabwe | 2017 |
1703 | Zimbabwe | 2018 |
1704 rows × 2 columns
Der Datensatz erhält pro Land mehrere Einträge, da es für jedes Land für mehrere Jahre Daten gibt. Mit der Funktion unique()
können wir eine Liste der Länder erstellen.
# Liste unterschiedlicher Einträge in der Spalte Country Name
df["Country name"].unique()
array(['Afghanistan', 'Albania', 'Algeria', 'Angola', 'Argentina', 'Armenia', 'Australia', 'Austria', 'Azerbaijan', 'Bahrain', 'Bangladesh', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bhutan', 'Bolivia', 'Bosnia and Herzegovina', 'Botswana', 'Brazil', 'Bulgaria', 'Burkina Faso', 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Central African Republic', 'Chad', 'Chile', 'China', 'Colombia', 'Comoros', 'Congo (Brazzaville)', 'Congo (Kinshasa)', 'Costa Rica', 'Croatia', 'Cuba', 'Cyprus', 'Czech Republic', 'Denmark', 'Djibouti', 'Dominican Republic', 'Ecuador', 'Egypt', 'El Salvador', 'Estonia', 'Ethiopia', 'Finland', 'France', 'Gabon', 'Gambia', 'Georgia', 'Germany', 'Ghana', 'Greece', 'Guatemala', 'Guinea', 'Guyana', 'Haiti', 'Honduras', 'Hong Kong S.A.R. of China', 'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran', 'Iraq', 'Ireland', 'Israel', 'Italy', 'Ivory Coast', 'Jamaica', 'Japan', 'Jordan', 'Kazakhstan', 'Kenya', 'Kosovo', 'Kuwait', 'Kyrgyzstan', 'Laos', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', 'Lithuania', 'Luxembourg', 'Macedonia', 'Madagascar', 'Malawi', 'Malaysia', 'Mali', 'Malta', 'Mauritania', 'Mauritius', 'Mexico', 'Moldova', 'Mongolia', 'Montenegro', 'Morocco', 'Mozambique', 'Myanmar', 'Namibia', 'Nepal', 'Netherlands', 'New Zealand', 'Nicaragua', 'Niger', 'Nigeria', 'North Cyprus', 'Norway', 'Oman', 'Pakistan', 'Palestinian Territories', 'Panama', 'Paraguay', 'Peru', 'Philippines', 'Poland', 'Portugal', 'Qatar', 'Romania', 'Russia', 'Rwanda', 'Saudi Arabia', 'Senegal', 'Serbia', 'Sierra Leone', 'Singapore', 'Slovakia', 'Slovenia', 'Somalia', 'Somaliland region', 'South Africa', 'South Korea', 'South Sudan', 'Spain', 'Sri Lanka', 'Sudan', 'Suriname', 'Swaziland', 'Sweden', 'Switzerland', 'Syria', 'Taiwan Province of China', 'Tajikistan', 'Tanzania', 'Thailand', 'Togo', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'United States', 'Uruguay', 'Uzbekistan', 'Venezuela', 'Vietnam', 'Yemen', 'Zambia', 'Zimbabwe'], dtype=object)
# Anzahl unterschiedlicher Einträge in der Spalte Country Name
df["Country name"].nunique()
165
Wir können auf Bedingungen filtern. Die Bedingung steht in eckigen Klammern. Sie muss den vollständigen Namen (also Datensatz und Spaltennamen) beinhalten.
df[df["Year"]>=2015]
Country name | Year | Life Ladder | Log GDP per capita | Social support | Healthy life expectancy at birth | Freedom to make life choices | Generosity | Perceptions of corruption | Positive affect | ... | GINI index (World Bank estimate) | GINI index (World Bank estimate), average 2000-16 | gini of household income reported in Gallup, by wp5-year | Most people can be trusted, Gallup | Most people can be trusted, WVS round 1981-1984 | Most people can be trusted, WVS round 1989-1993 | Most people can be trusted, WVS round 1994-1998 | Most people can be trusted, WVS round 1999-2004 | Most people can be trusted, WVS round 2005-2009 | Most people can be trusted, WVS round 2010-2014 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
7 | Afghanistan | 2015 | 3.982855 | 7.500539 | 0.528597 | 53.200001 | 0.388928 | 0.089091 | 0.880638 | 0.553553 | ... | NaN | NaN | 0.596918 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
8 | Afghanistan | 2016 | 4.220169 | 7.497038 | 0.559072 | 53.000000 | 0.522566 | 0.051365 | 0.793246 | 0.564953 | ... | NaN | NaN | 0.418629 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
9 | Afghanistan | 2017 | 2.661718 | 7.497755 | 0.490880 | 52.799999 | 0.427011 | -0.112198 | 0.954393 | 0.496349 | ... | NaN | NaN | 0.286599 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
10 | Afghanistan | 2018 | 2.694303 | 7.494588 | 0.507516 | 52.599998 | 0.373536 | -0.084888 | 0.927606 | 0.424125 | ... | NaN | NaN | 0.290681 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
18 | Albania | 2015 | 4.606651 | 9.302960 | 0.639356 | 67.800003 | 0.703851 | -0.084411 | 0.884793 | 0.688370 | ... | NaN | 0.30325 | 0.422627 | NaN | NaN | NaN | 0.243243 | 0.232000 | NaN | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1690 | Zambia | 2018 | 4.041488 | 8.223958 | 0.717720 | 55.299999 | 0.790626 | 0.036644 | 0.810731 | 0.702698 | ... | NaN | 0.52740 | 0.619443 | NaN | NaN | NaN | NaN | NaN | 0.110429 | NaN |
1700 | Zimbabwe | 2015 | 3.703191 | 7.556052 | 0.735800 | 53.799999 | 0.667193 | -0.097354 | 0.810457 | 0.715079 | ... | NaN | 0.43200 | 0.655137 | NaN | NaN | NaN | NaN | 0.116683 | NaN | 0.082942 |
1701 | Zimbabwe | 2016 | 3.735400 | 7.538829 | 0.768425 | 54.400002 | 0.732971 | -0.068105 | 0.723612 | 0.737636 | ... | NaN | 0.43200 | 0.596690 | NaN | NaN | NaN | NaN | 0.116683 | NaN | 0.082942 |
1702 | Zimbabwe | 2017 | 3.638300 | 7.549491 | 0.754147 | 55.000000 | 0.752826 | -0.069670 | 0.751208 | 0.806428 | ... | NaN | 0.43200 | 0.581484 | NaN | NaN | NaN | NaN | 0.116683 | NaN | 0.082942 |
1703 | Zimbabwe | 2018 | 3.616480 | 7.553395 | 0.775388 | 55.599998 | 0.762675 | -0.038384 | 0.844209 | 0.710119 | ... | NaN | 0.43200 | 0.541772 | NaN | NaN | NaN | NaN | 0.116683 | NaN | 0.082942 |
568 rows × 26 columns
Es können auch mehrere Bedingungen kombiniert werden (|= OR, & = AND, ~ = Negation). Mehrere Bedingungen werden jeweils in runde Klammern eingeschlossen. Eine Auswahl der Spalten kann zusätzlich erfolgen. Da dies unübersichtlich werden kann, empfiehlt es sich die Bedingung separat zu definieren.
# Filter definieren
DK_from_2015 = (df["Country name"]=="Denmark") & (df["Year"]>=2015)
# Filter anwenden und Spalten auswählen
df[["Country name", "Year", "Life Ladder"]][DK_from_2015]
Country name | Year | Life Ladder | |
---|---|---|---|
406 | Denmark | 2015 | 7.514425 |
407 | Denmark | 2016 | 7.557783 |
408 | Denmark | 2017 | 7.593702 |
409 | Denmark | 2018 | 7.648786 |
Mit isin()
können wir auch Filter erstellen, die prüfen ob ein Element zu einer Gruppe gehört.
# Filter definieren
# Skandinavien
scandinavia = ["Denmark", "Norway", "Sweden"]
only_scandinavia = df["Country name"].isin(scandinavia)
# Nur 2018
only_2018 = df["Year"]==2018
# Filtern anwenden und Spalten auswählen
df[["Country name", "Year", "Life Ladder"]][only_scandinavia & only_2018]
Country name | Year | Life Ladder | |
---|---|---|---|
409 | Denmark | 2018 | 7.648786 |
1139 | Norway | 2018 | 7.444262 |
1444 | Sweden | 2018 | 7.374792 |
Im folgenden wollen wir uns auf einige europäische Länder begrenzen. Wir filtern den Datensatz entsprechend. Dazu erstellen wir zunächst eine neue Variable "Country Group", die verschiedene Länder in Ländergruppen zusammenfasst. Wir bilden also eine Hierarchie. Nachdem wir diese neue kategorische Variable gebildet haben, können wir uns mit value_counts()
die Einträge pro Ausprägung angucken.
# Ländergruppen definieren
western_europe = ["Austria", "Belgium", "Denmark", "Finland", "France", "Germany", "Greece", "Ireland", "Italy", "Luxembourg", "Netherlands", "Norway", "Portugal", "Romania", "Spain", "Sweden", "Switzerland", "United Kingdom"]
eastern_europe = ["Bosnia and Herzegovina", "Croatia", "Cyprus", "Czech Republic", "Estonia", "Hungary", "Latvia", "Lithuania", "Malta", "Poland", "Serbia", "Slovakia", "Slovenia"]
# Filter definieren
western_europe_fltr = df["Country name"].isin(western_europe)
eastern_europe_fltr = df["Country name"].isin(eastern_europe)
# Neue Spalte mit Ländergruppen hinzufügen
df["Country group"]=np.NaN
df["Country group"] [western_europe_fltr] = "Western Europe"
df["Country group"] [eastern_europe_fltr] = "Eastern Europe"
# Anzahl der Reihen pro Kategorie (inkl. fehlender Werte)
df["Country group"].value_counts(dropna=False)
NaN 1349 Western Europe 210 Eastern Europe 145 Name: Country group, dtype: int64
Es gibt 355 Einträge für europäische Ländern.
europe_fltr = df["Country group"].isin(["Western Europe", "Eastern Europe"])
df_europe = df[europe_fltr]
df_europe.shape
(355, 27)
Unser neuer Datensatz df_europe hat 355 Zeilen.
# Länder in dem kleineren Datensatz
df_europe["Country name"].unique()
array(['Austria', 'Belgium', 'Bosnia and Herzegovina', 'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', 'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'Romania', 'Serbia', 'Slovakia', 'Slovenia', 'Spain', 'Sweden', 'Switzerland', 'United Kingdom'], dtype=object)
Pandas hat zwei Methoden, um eine numerische Variable in mehrere Kategorien einzuteilen (binning). Mit Hilfe von cut()
können die Kategorien selbst definiert werden. qcut()
teilt eine numerische Variable in eine vorgegebene Anzahl gleich großer Kategorien auf.
Der Datensatz enthält eine Variable zur Lebenswertung. Mit plot
können wir uns die Verteilung angucken.
df_europe["Healthy life expectancy at birth"].plot(kind="density")
<AxesSubplot:ylabel='Density'>
Die Verteilung ist etwas bi-modal, mit vielen Werten um die 73 und um 68.
Für eine Auswertung sind die spezifischen Werte vielleicht nicht so relevant, so dass man auch eine übersichtliche Anzahl an Kategorien betrachten kann. Wenn man keine inhaltliche Gründe für eine bestimmte Aufteilung kennt, dann bietet es sich an, einfach gleich große Gruppen zu bilden.
df_europe["Healthy life expectancy at birth"].describe()
count 355.000000 mean 70.005746 std 2.708642 min 63.139999 25% 67.689999 50% 71.080002 75% 72.099998 max 74.400002 Name: Healthy life expectancy at birth, dtype: float64
Mit der Methode qcut()
können wir auf Grundlager der numerischen Variable "Healthy life expectancy at birth" eine kategorische Variablen mit einer vorgegebenen Anzahl gleich großer Kategorien bilden. Bei vier Kategorien werden dann Gruppen entsprechend der Quartile gebildet. Die Anzahl der Kategorien wird über den Parameter q
gesteuert.
# Neue kategorische Variablen mit Quartilen der Lebenserwartung bilden
df_europe["Life expectancy (quartiles)"] = pd.qcut(df_europe["Healthy life expectancy at birth"], q=4)
df_europe["Life expectancy (quartiles)"]
71 (67.69, 71.08] 72 (67.69, 71.08] 73 (71.08, 72.1] 74 (71.08, 72.1] 75 (71.08, 72.1] ... 1599 (71.08, 72.1] 1600 (71.08, 72.1] 1601 (71.08, 72.1] 1602 (71.08, 72.1] 1603 (72.1, 74.4] Name: Life expectancy (quartiles), Length: 355, dtype: category Categories (4, interval[float64]): [(63.139, 67.69] < (67.69, 71.08] < (71.08, 72.1] < (72.1, 74.4]]
MIt Hilfe von value_counts()
können wir uns davon überzeugen, dass die Kategorien (fast) genau gleich groß sind.
df_europe["Life expectancy (quartiles)"].value_counts()
(71.08, 72.1] 92 (63.139, 67.69] 89 (67.69, 71.08] 89 (72.1, 74.4] 85 Name: Life expectancy (quartiles), dtype: int64
Die Methode bildet halb-offene Intervalle, die standardmäßig auch als Namen der Kategorien benutzt werden. Man kann mittels des Parameters label
auch eine eigene Liste von Namen vergeben.
life_exp_quart_labs = ["1. Quartile", "2. Quartile", "3. Quartile", "4. Quartile"]
df_europe["Life expectancy (quartiles)"] = pd.qcut(df_europe["Healthy life expectancy at birth"], q=4, labels=life_exp_quart_labs)
df_europe["Life expectancy (quartiles)"]
71 2. Quartile 72 2. Quartile 73 3. Quartile 74 3. Quartile 75 3. Quartile ... 1599 3. Quartile 1600 3. Quartile 1601 3. Quartile 1602 3. Quartile 1603 4. Quartile Name: Life expectancy (quartiles), Length: 355, dtype: category Categories (4, object): ['1. Quartile' < '2. Quartile' < '3. Quartile' < '4. Quartile']
df_europe["Life expectancy (quartiles)"].value_counts()
3. Quartile 92 1. Quartile 89 2. Quartile 89 4. Quartile 85 Name: Life expectancy (quartiles), dtype: int64
df_europe["Life expectancy (quartiles)"].describe(include="category")
count 355 unique 4 top 3. Quartile freq 92 Name: Life expectancy (quartiles), dtype: object
Wir begrenzen den Datensatz zusätzlich noch auf bestimmte Spalten. Die Variable "Life Ladder" bezieht sich auf das subjektive Wohlergehen. Wir ändern den Namen der Spalten dementsprechend.
selected_columns = ["Country name", "Country group", "Year", "Life Ladder", "Positive affect", "Life expectancy (quartiles)", "Log GDP per capita", "Democratic Quality"]
df_europe = df_europe[selected_columns]
df_europe.rename(columns = {'Life Ladder':'Subjective Wellbeing'}, inplace = True)
df_europe.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 355 entries, 71 to 1603 Data columns (total 8 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Country name 355 non-null object 1 Country group 355 non-null object 2 Year 355 non-null int64 3 Subjective Wellbeing 355 non-null float64 4 Positive affect 355 non-null float64 5 Life expectancy (quartiles) 355 non-null category 6 Log GDP per capita 353 non-null float64 7 Democratic Quality 326 non-null float64 dtypes: category(1), float64(4), int64(1), object(2) memory usage: 22.7+ KB
Die Spalte für Democratic Quality hat fehlende Werte. Wir können uns die Ländernamen und Jahre für die Zeilen mit fehlenden Werten ausgeben lassen.
row_with_missing_data = df_europe.isnull().any(axis=1)
df_europe[["Country name", "Year"]][row_with_missing_data]
Country name | Year | |
---|---|---|
81 | Austria | 2018 |
141 | Belgium | 2018 |
180 | Bosnia and Herzegovina | 2018 |
373 | Croatia | 2018 |
385 | Cyprus | 2018 |
396 | Czech Republic | 2018 |
409 | Denmark | 2018 |
477 | Estonia | 2018 |
495 | Finland | 2018 |
508 | France | 2018 |
544 | Germany | 2018 |
568 | Greece | 2018 |
699 | Ireland | 2018 |
725 | Italy | 2018 |
843 | Latvia | 2018 |
885 | Lithuania | 2018 |
895 | Luxembourg | 2018 |
960 | Malta | 2018 |
1076 | Netherlands | 2018 |
1139 | Norway | 2018 |
1239 | Portugal | 2018 |
1256 | Romania | 2018 |
1317 | Serbia | 2018 |
1350 | Slovakia | 2018 |
1361 | Slovenia | 2018 |
1411 | Spain | 2018 |
1444 | Sweden | 2018 |
1452 | Switzerland | 2018 |
1603 | United Kingdom | 2018 |
Anscheinend fehlt diese Information für ein ganzes Jahr. Um diese Vermutung zu bestätigen, lassen wir uns die verschiedenen Werte für 2018 ausgeben.
df_europe[df_europe["Year"] == 2018]["Democratic Quality"].unique()
array([nan])
Zuletzt speichern wir den kleineren und um die Region (Country group) sowie die kategorische Variable Life expectancy (quartiles) angereicherten Datensatz als CSV-Datei ab. Standardmässig wird ein Komma als Trennzeichen verwendet.
df_europe.to_csv("WHR2019_Chapter2OnlineData_Europe.csv")
In dem zweiten Teil arbeiten wir mit den Daten für Europa weiter und betrachten einige (teilweise aggregierte) deskriptive Statistiken. Wir gucken uns auch einige bi-variate Zusammenhänge mit Hilfe von Datenvisualisierungen an. Schließlich rechnen wir eine einfache Regressionsanalyse.