Inhalt des Skriptums seit dem Sommersemester 2025
- Startseite
- Allgemeine Informationen zur Lehrveranstaltung
- Einfaches Python Setup und Wichtige Informationen
- 01 Einführung in algorithmisches Denken & LLMs
- 02 Algorithmenanalyse und Komplexitätstheorie
- 03 Vektoren, Matrizen und Vektorisierung in Python
- 04 Differenzieren und Integrieren in Python: Analytische und Numerische Methoden
- 05 Arbeiten mit Daten: Vorbereitung, Visualisierung, Analyse
- 06 Graphalgorithmen: Darstellungen, Traversierungen, Kürzeste Wege und Spannbäume
Hier finden Sie die Kapitel aus den Vergangenen Jahren (legacy):
- 0. Python Einführung und Checkup
- 1. Einführung und einfache Algorithmen
- 2. Differenzieren und Integrieren
- 3. Vektoren, Matrizen und Vektorisierung in Python
- 4. Datenanalyse bzw. Datenauswertung
- 5. Grundlagen der Optimierung und Gradient Descent
- 6. Stochastische Optimierung und Genetische Algorithmen
- 7. Monte-Carlo-Methoden – Simulation und Integration
- 8. Monte-Carlo-Methoden, Teil 2 – Monte-Carlo-Integration, Teil 2 und Random Walk
- 9. Unsupervised Machine Learning: Clustering von Daten
- 10. Supervised Machine Learning: Grundlagen
- 11. Einführung in künstliche neuronale Netzwerke
Die Jupyter-Notebooks zur Lehrveranstaltung finden Sie im zugehörigen GitHub-Repository.
5 Arbeiten mit Daten: Vorbereitung, Visualisierung, Analyse¶
In dieser Einheit werden wir uns mit einem zentralen Aspekt Ihrer Arbeit und unser aller Gegenwart und Zukunft befassen: Der Arbeit mit realen Datensätzen. Die Fähigkeit, Daten effizient einzulesen, zu analysieren und zu visualisieren, ist heute in nahezu allen wissenschaftlichen und wirtschaftlichen Bereichen von grundlegender Bedeutung. Ganz zu schweigen davon, dass die Arbeit mit Daten unglaublich viel Spaß macht. Lassen Sie uns also gleich damit beginnen.
Nach dem Studium dieser Einheit sollten Sie zumindest folgende drei der wichtigsten Punkte hier verstehen:¶
Die gute Vor- und Aufbereitung der Daten ist essentiell. In der Praxis steht und fällt Ihre spätere Analyse eines Datensatzes mit der Sorgfalt der Vorbereitung. Ebenso ist dieser Teil oft der Arbeits-intensivste.
Die Macht der Visualisierung wird Ihnen helfen, mögliche Zusammenhänge und Muster in Daten zu erkennen, die man in einer Tabelle mit Zahlen niemals sehen würde. Die Welt der Visualisierung ist vielfältig und faszinierend.
Kombinierte Analysemethoden helfen Ihnen dabei, auch komplexe Datensätze umfassend zu verstehen. Dabei fügt man vor der Visualisierung noch geeignete spezielle Analyseschritte ein, die helfen, mögliche Strukturen ans Licht zu bringen.
In dieser Einheit werden wir mit einem realen Datensatz zu Erdbeben arbeiten. Es handelt sich dabei um Ort und Zeit sowie Magnitude (und noch einige Informationen mehr) der Erdbeben mit $M>5.5$ von 1965 bis 2016. Die Quelle für diesen Datensatz ist eine Plattform namens Kaggle, auf der Machine-Learning-Wettbewerbe ausgetragen werden. Der spezielle Datensatz kommt von hier: https://www.kaggle.com/datasets/usgs/earthquake-database. Für uns sind diese Daten eine hervorragende Grundlage, um verschiedene Datenverarbeitungs- und Visualisierungstechniken zu demonstrieren.
Die Code-Snippets und Beispiele in dieser Einheit stammen in bewährter Manier von Claude 3.7 Sonnet, wenn auch mit etwas mehr zwischenzeitlicher und nachträglicher menschlicher Intervention als bisher.
5.1 Daten Einlesen und erste Analyse und Aufbereitung¶
Der erste Schritt bei jeder Datenanalyse besteht darin, die Daten einzulesen und sich einen Überblick über ihre Struktur und Qualität zu verschaffen. Dieser Prozess ist entscheidend, da er die Grundlage für alle weiteren Analyseschritte bildet.
5.1.1 Einlesen von csv Dateien in Python mit Pandas¶
Comma-Separated Values (CSV) ist eines der am häufigsten verwendeten Formate für den Austausch tabellarischer Daten. Die Python-Bibliothek Pandas bietet leistungsstarke und hochoptimierte Funktionen zum Einlesen, Verarbeiten und Analysieren solcher Daten, in weiterer Folge auch als NumPy-Array.
Zunächst importieren wir die benötigten Bibliotheken:
# Import der grundlegenden Bibliotheken für Datenanalyse und Visualisierung
import pandas as pd # Für Datenverarbeitung und -analyse
import numpy as np # Für numerische Berechnungen
import matplotlib.pyplot as plt # Für Visualisierungen
import seaborn as sns # Für erweiterte statistische Visualisierungen
import os # Für Zugriff auf das Dateisystem
# Anpassung der Darstellung
sns.set_theme(style="whitegrid") # Setzt den seaborn whitegrid-Stil
plt.rcParams['figure.figsize'] = (12, 8) # Setzt die Standardgröße für Plots
plt.rcParams['font.size'] = 12 # Setzt die Standardschriftgröße
Nun können wir den Erdbeben-Datensatz einlesen. Wir verwenden die read_csv()
-Funktion von Pandas, die im Prinzip auch eine Vielzahl von Parametern bietet, um den Einleseprozess anzupassen, die wir hier aber erst einmal nicht nutzen, sondern den Datensatz einfach so einlesen, wie er ist:
# Pfad zur CSV-Datei
file_path = os.path.join('data','earthquake_database.csv') # Pfad anpassen, falls nötig
# Einlesen der Daten mit Pandas (ohne Datum/Zeit zu kombinieren)
earthquakes = pd.read_csv(file_path)
# Kombinieren von Datum und Zeit nach dem Einlesen
if 'Date' in earthquakes.columns and 'Time' in earthquakes.columns:
# Datum und Zeit zu einem String kombinieren und dann in datetime umwandeln
earthquakes['Date_Time'] = pd.to_datetime(earthquakes['Date'] + ' ' + earthquakes['Time'], errors='coerce')
# Überprüfen, ob die Daten korrekt eingelesen wurden
print(f"Datensatz erfolgreich eingelesen: {len(earthquakes)} Einträge gefunden.")
Datensatz erfolgreich eingelesen: 23412 Einträge gefunden.
5.1.2 Anzeigen der Datenstruktur und von Teilen der Daten¶
Nachdem wir den Datensatz eingelesen haben, ist es hilfreich, einen ersten Überblick über dessen Struktur und Inhalt zu gewinnen. Pandas bietet dafür verschiedene Methoden:
head(n)
: Zeigt die ersten n Zeilen des Datensatzes, Standardwert ist 5tail(n)
: Zeigt die letzten n Zeilen des Datensatzes, Standardwert ist wieder 5info()
: Gibt Informationen über die Datentypen und fehlende Wertedescribe()
: Liefert grundlegende statistische Kennzahlen für numerische Spalten
# Anzeige der ersten Zeilen des Datensatzes
print("Die ersten 5 Einträge des Datensatzes:")
display(earthquakes.head())
# Anzeige der letzten Zeilen des Datensatzes
print("\nDie letzten 5 Einträge des Datensatzes:")
display(earthquakes.tail())
# Informationen über den Datensatz
print("\nInformationen zur Struktur des Datensatzes:")
earthquakes.info()
# Grundlegende statistische Kennzahlen
print("\nStatistische Übersicht der numerischen Spalten:")
display(earthquakes.describe())
# Anzeigen aller Spaltennamen
print("\nVerfügbare Spalten im Datensatz:")
for i, column in enumerate(earthquakes.columns):
print(f"{i+1}. {column}")
Die ersten 5 Einträge des Datensatzes:
Date | Time | Latitude | Longitude | Type | Depth | Depth Error | Depth Seismic Stations | Magnitude | Magnitude Type | … | Azimuthal Gap | Horizontal Distance | Horizontal Error | Root Mean Square | ID | Source | Location Source | Magnitude Source | Status | Date_Time | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 01/02/1965 | 13:44:18 | 19.246 | 145.616 | Earthquake | 131.6 | NaN | NaN | 6.0 | MW | … | NaN | NaN | NaN | NaN | ISCGEM860706 | ISCGEM | ISCGEM | ISCGEM | Automatic | 1965-01-02 13:44:18 |
1 | 01/04/1965 | 11:29:49 | 1.863 | 127.352 | Earthquake | 80.0 | NaN | NaN | 5.8 | MW | … | NaN | NaN | NaN | NaN | ISCGEM860737 | ISCGEM | ISCGEM | ISCGEM | Automatic | 1965-01-04 11:29:49 |
2 | 01/05/1965 | 18:05:58 | -20.579 | -173.972 | Earthquake | 20.0 | NaN | NaN | 6.2 | MW | … | NaN | NaN | NaN | NaN | ISCGEM860762 | ISCGEM | ISCGEM | ISCGEM | Automatic | 1965-01-05 18:05:58 |
3 | 01/08/1965 | 18:49:43 | -59.076 | -23.557 | Earthquake | 15.0 | NaN | NaN | 5.8 | MW | … | NaN | NaN | NaN | NaN | ISCGEM860856 | ISCGEM | ISCGEM | ISCGEM | Automatic | 1965-01-08 18:49:43 |
4 | 01/09/1965 | 13:32:50 | 11.938 | 126.427 | Earthquake | 15.0 | NaN | NaN | 5.8 | MW | … | NaN | NaN | NaN | NaN | ISCGEM860890 | ISCGEM | ISCGEM | ISCGEM | Automatic | 1965-01-09 13:32:50 |
5 rows × 22 columns
Die letzten 5 Einträge des Datensatzes:
Date | Time | Latitude | Longitude | Type | Depth | Depth Error | Depth Seismic Stations | Magnitude | Magnitude Type | … | Azimuthal Gap | Horizontal Distance | Horizontal Error | Root Mean Square | ID | Source | Location Source | Magnitude Source | Status | Date_Time | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
23407 | 12/28/2016 | 08:22:12 | 38.3917 | -118.8941 | Earthquake | 12.30 | 1.2 | 40.0 | 5.6 | ML | … | 42.47 | 0.120 | NaN | 0.1898 | NN00570710 | NN | NN | NN | Reviewed | 2016-12-28 08:22:12 |
23408 | 12/28/2016 | 09:13:47 | 38.3777 | -118.8957 | Earthquake | 8.80 | 2.0 | 33.0 | 5.5 | ML | … | 48.58 | 0.129 | NaN | 0.2187 | NN00570744 | NN | NN | NN | Reviewed | 2016-12-28 09:13:47 |
23409 | 12/28/2016 | 12:38:51 | 36.9179 | 140.4262 | Earthquake | 10.00 | 1.8 | NaN | 5.9 | MWW | … | 91.00 | 0.992 | 4.8 | 1.5200 | US10007NAF | US | US | US | Reviewed | 2016-12-28 12:38:51 |
23410 | 12/29/2016 | 22:30:19 | -9.0283 | 118.6639 | Earthquake | 79.00 | 1.8 | NaN | 6.3 | MWW | … | 26.00 | 3.553 | 6.0 | 1.4300 | US10007NL0 | US | US | US | Reviewed | 2016-12-29 22:30:19 |
23411 | 12/30/2016 | 20:08:28 | 37.3973 | 141.4103 | Earthquake | 11.94 | 2.2 | NaN | 5.5 | MB | … | 97.00 | 0.681 | 4.5 | 0.9100 | US10007NTD | US | US | US | Reviewed | 2016-12-30 20:08:28 |
5 rows × 22 columns
Informationen zur Struktur des Datensatzes: <class 'pandas.core.frame.DataFrame'> RangeIndex: 23412 entries, 0 to 23411 Data columns (total 22 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Date 23412 non-null object 1 Time 23412 non-null object 2 Latitude 23412 non-null float64 3 Longitude 23412 non-null float64 4 Type 23412 non-null object 5 Depth 23412 non-null float64 6 Depth Error 4461 non-null float64 7 Depth Seismic Stations 7097 non-null float64 8 Magnitude 23412 non-null float64 9 Magnitude Type 23409 non-null object 10 Magnitude Error 327 non-null float64 11 Magnitude Seismic Stations 2564 non-null float64 12 Azimuthal Gap 7299 non-null float64 13 Horizontal Distance 1604 non-null float64 14 Horizontal Error 1156 non-null float64 15 Root Mean Square 17352 non-null float64 16 ID 23412 non-null object 17 Source 23412 non-null object 18 Location Source 23412 non-null object 19 Magnitude Source 23412 non-null object 20 Status 23412 non-null object 21 Date_Time 23412 non-null datetime64[ns] dtypes: datetime64[ns](1), float64(12), object(9) memory usage: 3.9+ MB Statistische Übersicht der numerischen Spalten:
Latitude | Longitude | Depth | Depth Error | Depth Seismic Stations | Magnitude | Magnitude Error | Magnitude Seismic Stations | Azimuthal Gap | Horizontal Distance | Horizontal Error | Root Mean Square | Date_Time | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 23412.000000 | 23412.000000 | 23412.000000 | 4461.000000 | 7097.000000 | 23412.000000 | 327.000000 | 2564.000000 | 7299.000000 | 1604.000000 | 1156.000000 | 17352.000000 | 23412 |
mean | 1.679033 | 39.639961 | 70.767911 | 4.993115 | 275.364098 | 5.882531 | 0.071820 | 48.944618 | 44.163532 | 3.992660 | 7.662759 | 1.022784 | 1993-02-18 09:25:46.351700096 |
min | -77.080000 | -179.997000 | -1.100000 | 0.000000 | 0.000000 | 5.500000 | 0.000000 | 0.000000 | 0.000000 | 0.004505 | 0.085000 | 0.000000 | 1965-01-02 13:44:18 |
25% | -18.653000 | -76.349750 | 14.522500 | 1.800000 | 146.000000 | 5.600000 | 0.046000 | 10.000000 | 24.100000 | 0.968750 | 5.300000 | 0.900000 | 1981-04-11 02:19:14.250000 |
50% | -3.568500 | 103.982000 | 33.000000 | 3.500000 | 255.000000 | 5.700000 | 0.059000 | 28.000000 | 36.000000 | 2.319500 | 6.700000 | 1.000000 | 1993-11-30 20:40:43 |
75% | 26.190750 | 145.026250 | 54.000000 | 6.300000 | 384.000000 | 6.000000 | 0.075500 | 66.000000 | 54.000000 | 4.724500 | 8.100000 | 1.130000 | 2005-09-09 21:17:39.500000 |
max | 86.005000 | 179.998000 | 700.000000 | 91.295000 | 934.000000 | 9.100000 | 0.410000 | 821.000000 | 360.000000 | 37.874000 | 99.000000 | 3.440000 | 2016-12-30 20:08:28 |
std | 30.113183 | 125.511959 | 122.651898 | 4.875184 | 162.141631 | 0.423066 | 0.051466 | 62.943106 | 32.141486 | 5.377262 | 10.430396 | 0.188545 | NaN |
Verfügbare Spalten im Datensatz: 1. Date 2. Time 3. Latitude 4. Longitude 5. Type 6. Depth 7. Depth Error 8. Depth Seismic Stations 9. Magnitude 10. Magnitude Type 11. Magnitude Error 12. Magnitude Seismic Stations 13. Azimuthal Gap 14. Horizontal Distance 15. Horizontal Error 16. Root Mean Square 17. ID 18. Source 19. Location Source 20. Magnitude Source 21. Status 22. Date_Time
5.1.3 Checken der Einträge, Auflistung der Werte und Überprüfung auf Lücken¶
Ein entscheidender Schritt bei der Datenaufbereitung ist die Identifikation und Behandlung fehlender oder ungültiger Werte. Unbehandelte Datenlücken können zu fehlerhaften Analysen und Schlussfolgerungen führen. Zunächst überprüfen wir, ob und in welchem Umfang fehlende Werte im Datensatz vorhanden sind:
# Überprüfung auf fehlende Werte in jeder Spalte
missing_values = earthquakes.isnull().sum()
# Berechnung des prozentualen Anteils fehlender Werte
missing_percentage = (missing_values / len(earthquakes)) * 100
# Zusammenfassung in einem DataFrame
missing_data = pd.DataFrame({
'Fehlende Werte': missing_values,
'Prozent fehlend': missing_percentage.round(2)
})
# Sortieren nach der Anzahl fehlender Werte (absteigend)
missing_data = missing_data.sort_values('Fehlende Werte', ascending=False)
print("Überblick über fehlende Werte im Datensatz:")
display(missing_data)
# Visualisierung fehlender Werte (nur für die Top-10-Spalten mit den meisten fehlenden Werten)
plt.figure(figsize=(12, 6))
sns.barplot(x=missing_data.index[:10], y='Prozent fehlend', data=missing_data[:10])
plt.title('Prozentsatz fehlender Werte nach Spalte (Top 10)')
plt.xticks(rotation=45, ha='right')
plt.ylabel('Prozent fehlend')
plt.tight_layout()
plt.show()
# Überprüfung der Verteilung von kategorialen Werten in der Spalte "Type"
if 'Type' in earthquakes.columns:
print("\nVerteilung der Ereignistypen:")
display(earthquakes['Type'].value_counts())
# Überprüfung der Verteilung von kategorialen Werten in der Spalte "Magnitude Type"
if 'Magnitude Type' in earthquakes.columns:
print("\nVerteilung der Magnitude-Typen:")
display(earthquakes['Magnitude Type'].value_counts())
# Überprüfung der Verteilung von kategorialen Werten in der Spalte "Status"
if 'Status' in earthquakes.columns:
print("\nVerteilung der Status-Werte:")
display(earthquakes['Status'].value_counts())
Überblick über fehlende Werte im Datensatz:
Fehlende Werte | Prozent fehlend | |
---|---|---|
Magnitude Error | 23085 | 98.60 |
Horizontal Error | 22256 | 95.06 |
Horizontal Distance | 21808 | 93.15 |
Magnitude Seismic Stations | 20848 | 89.05 |
Depth Error | 18951 | 80.95 |
Depth Seismic Stations | 16315 | 69.69 |
Azimuthal Gap | 16113 | 68.82 |
Root Mean Square | 6060 | 25.88 |
Magnitude Type | 3 | 0.01 |
Status | 0 | 0.00 |
Magnitude Source | 0 | 0.00 |
Location Source | 0 | 0.00 |
Source | 0 | 0.00 |
ID | 0 | 0.00 |
Date | 0 | 0.00 |
Time | 0 | 0.00 |
Magnitude | 0 | 0.00 |
Depth | 0 | 0.00 |
Type | 0 | 0.00 |
Longitude | 0 | 0.00 |
Latitude | 0 | 0.00 |
Date_Time | 0 | 0.00 |

Verteilung der Ereignistypen:
Type Earthquake 23232 Nuclear Explosion 175 Explosion 4 Rock Burst 1 Name: count, dtype: int64
Verteilung der Magnitude-Typen:
Magnitude Type MW 7722 MWC 5669 MB 3761 MWB 2458 MWW 1983 MS 1702 ML 77 MWR 26 MD 6 MH 5 Name: count, dtype: int64
Verteilung der Status-Werte:
Status Reviewed 20773 Automatic 2639 Name: count, dtype: int64
5.1.4 Statistische Plots für numerische Daten, Häufigkeitsplots für andere Datentypen¶
Visualisierungen helfen uns, die Verteilung und Struktur der Daten besser zu verstehen. Wir erstellen verschiedene Plots, um einen tieferen Einblick in die Eigenschaften der Erdbebendaten zu gewinnen:
- Histogramme der wichtigsten numerischen Variablen: Diese zeigen die Häufigkeitsverteilung von Werten wie Magnitude und Tiefe.
- Box-Plots: Diese verdeutlichen die Verteilung und identifizieren Ausreißer.
- Zeitreihenanalyse: Die Verteilung der Erdbeben über die Zeit.
# 1. Histogramm der Magnitude
plt.figure(figsize=(12, 6))
sns.histplot(earthquakes['Magnitude'].dropna(), bins=30, kde=True)
plt.title('Verteilung der Erdbebenmagnitude')
plt.xlabel('Magnitude')
plt.ylabel('Anzahl')
plt.grid(True, alpha=0.3)
plt.show()
# 2. Histogramm der Tiefe
plt.figure(figsize=(12, 6))
# Logarithmische Skala für bessere Sichtbarkeit
sns.histplot(earthquakes['Depth'].dropna(), bins=30, kde=True, log_scale=(False, True))
plt.title('Verteilung der Erdbebenteife (logarithmische Skala)')
plt.xlabel('Tiefe (km)')
plt.ylabel('Anzahl (log)')
plt.grid(True, alpha=0.3)
plt.show()
# 3. Box-Plots für Magnitude und Tiefe
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
sns.boxplot(y=earthquakes['Magnitude'].dropna())
plt.title('Box-Plot der Magnitude')
plt.ylabel('Magnitude')
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
sns.boxplot(y=earthquakes['Depth'].dropna())
plt.title('Box-Plot der Tiefe')
plt.ylabel('Tiefe (km)')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 4. Zeitreihenanalyse - Erdbeben pro Jahr
# Überprüfen, ob die Date_Time-Spalte existiert und im richtigen Format ist
if 'Date_Time' in earthquakes.columns:
try:
# Extrahieren des Jahres aus dem Datum
earthquakes['Year'] = earthquakes['Date_Time'].dt.year
# Zählen der Erdbeben pro Jahr
yearly_counts = earthquakes['Year'].value_counts().sort_index()
plt.figure(figsize=(14, 6))
yearly_counts.plot(kind='bar')
plt.title('Anzahl der Erdbeben pro Jahr')
plt.xlabel('Jahr')
plt.ylabel('Anzahl der Erdbeben')
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
except Exception as e:
print(f"Fehler bei der Zeitreihenanalyse: {e}")
print("Überprüfen Sie das Format der Date_Time-Spalte.")
else:
print("Die Spalte 'Date_Time' wurde nicht gefunden. Zeitreihenanalyse nicht möglich.")
# 5. Scatter-Plot: Magnitude vs. Tiefe
plt.figure(figsize=(10, 8))
sns.scatterplot(x='Depth', y='Magnitude', data=earthquakes, alpha=0.5)
plt.title('Beziehung zwischen Erdbebenteife und Magnitude')
plt.xlabel('Tiefe (km)')
plt.ylabel('Magnitude')
plt.grid(True, alpha=0.3)
plt.show()
# 6. Korrelationsmatrix für die numerischen Spalten
# Auswahl der numerischen Spalten
numeric_columns = earthquakes.select_dtypes(include=[np.number]).columns.tolist()
if len(numeric_columns) > 1: # Nur ausführen, wenn mindestens zwei numerische Spalten vorhanden sind
correlation_matrix = earthquakes[numeric_columns].corr()
plt.figure(figsize=(12, 10))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', linewidths=0.5)
plt.title('Korrelationsmatrix der numerischen Variablen')
plt.tight_layout()
plt.show()






Hier wird man gleich mit Informationen versorgt, die verschiedene weitere Analyseschritte auslösen oder anregen können. Aber bevor wir weitermachen, werden wir den Datensatz auf jenen Teil beschränken, der kaum Löcher hat und sinnvoll verwendet werden kann. In unserem fall schränken wir zunächst auf die beinahe vollständigen Spalten ein und geben dann die Zeilen aus, für die es Löcher in den Werten gibt.
# Auswahl der relevanten Spalten
selected_columns = ['Date_Time', 'Latitude', 'Longitude', 'Magnitude', 'Depth', 'Type', 'Magnitude Type']
earthquakes_subset = earthquakes[selected_columns]
# Überblick über das reduzierte Dataset
print(f"Reduzierter Datensatz: {earthquakes_subset.shape[0]} Zeilen und {earthquakes_subset.shape[1]} Spalten")
# Überprüfung auf fehlende Werte im reduzierten Datensatz
missing_values = earthquakes_subset.isnull().sum()
print("\nFehlende Werte im reduzierten Datensatz:")
display(missing_values)
# Identifikation der Zeilen mit fehlenden Magnitude Type-Werten
rows_missing_magtype = earthquakes_subset[earthquakes_subset['Magnitude Type'].isnull()]
print("\nZeilen mit fehlenden Magnitude Type-Werten:")
display(rows_missing_magtype)
# Häufigkeitsverteilung der Magnitude Type-Werte
mag_type_counts = earthquakes_subset['Magnitude Type'].value_counts()
print("\nHäufigkeitsverteilung der Magnitude-Typen:")
display(mag_type_counts)
# Häufigkeitsverteilung der Type-Werte
type_counts = earthquakes_subset['Type'].value_counts()
print("\nHäufigkeitsverteilung der Ereignistypen:")
display(type_counts)
Reduzierter Datensatz: 23412 Zeilen und 7 Spalten Fehlende Werte im reduzierten Datensatz:
Date_Time 0 Latitude 0 Longitude 0 Magnitude 0 Depth 0 Type 0 Magnitude Type 3 dtype: int64
Zeilen mit fehlenden Magnitude Type-Werten:
Date_Time | Latitude | Longitude | Magnitude | Depth | Type | Magnitude Type | |
---|---|---|---|---|---|---|---|
6703 | 1983-08-24 13:36:00 | 40.3732 | -124.9227 | 5.70 | 11.93 | Earthquake | NaN |
7294 | 1984-11-23 18:08:00 | 37.4600 | -118.5900 | 5.82 | 9.00 | Earthquake | NaN |
7919 | 1986-03-31 11:55:00 | 37.4788 | -121.6858 | 5.60 | 9.17 | Earthquake | NaN |
Häufigkeitsverteilung der Magnitude-Typen:
Magnitude Type MW 7722 MWC 5669 MB 3761 MWB 2458 MWW 1983 MS 1702 ML 77 MWR 26 MD 6 MH 5 Name: count, dtype: int64
Häufigkeitsverteilung der Ereignistypen:
Type Earthquake 23232 Nuclear Explosion 175 Explosion 4 Rock Burst 1 Name: count, dtype: int64
Hier sieht man die Kontrolle der Lücken und die fehlenden Werte. Aufgrund der Verteilung der Magnitude-Typen ist es vernünftig, die drei fehlenden Werte durch den häufigsten Wert zu ersetzen. Das ist eine gängige Praxis, die wir hier übernehmen, weil wir dadurch auch jene drei Zeilen komplett in alle Analysen einbinden können. Eine Alternative wäre, einen neue Bezeichnung (unknown) einzuführen und so diese Kategorie extra mitzunehmen. Wir belassen es aber der Einfachheit halber dabei, einen vorhandenen Wert zu übernehmen.
# Explizites Erstellen einer Kopie des DataFrames
earthquakes_subset = earthquakes[selected_columns].copy()
# Füllen der fehlenden Magnitude Type-Werte mit 'MW'
earthquakes_subset.loc[:, 'Magnitude Type'] = earthquakes_subset['Magnitude Type'].fillna('MW')
# Überprüfung, ob alle fehlenden Werte gefüllt wurden
missing_values_after = earthquakes_subset.isnull().sum()
print("Fehlende Werte nach der Bereinigung:")
display(missing_values_after)
# Finale Überprüfung der Datenstruktur
print("\nStruktur des bereinigten Datensatzes:")
display(earthquakes_subset.head())
print(f"Der bereinigte Datensatz enthält {len(earthquakes_subset)} Einträge und ist bereit für die weitere Analyse.")
Fehlende Werte nach der Bereinigung:
Date_Time 0 Latitude 0 Longitude 0 Magnitude 0 Depth 0 Type 0 Magnitude Type 0 dtype: int64
Struktur des bereinigten Datensatzes:
Date_Time | Latitude | Longitude | Magnitude | Depth | Type | Magnitude Type | |
---|---|---|---|---|---|---|---|
0 | 1965-01-02 13:44:18 | 19.246 | 145.616 | 6.0 | 131.6 | Earthquake | MW |
1 | 1965-01-04 11:29:49 | 1.863 | 127.352 | 5.8 | 80.0 | Earthquake | MW |
2 | 1965-01-05 18:05:58 | -20.579 | -173.972 | 6.2 | 20.0 | Earthquake | MW |
3 | 1965-01-08 18:49:43 | -59.076 | -23.557 | 5.8 | 15.0 | Earthquake | MW |
4 | 1965-01-09 13:32:50 | 11.938 | 126.427 | 5.8 | 15.0 | Earthquake | MW |
Der bereinigte Datensatz enthält 23412 Einträge und ist bereit für die weitere Analyse.
5.2 Geographische Visualisierung von Erdbebendaten¶
Nach der Bereinigung und Aufbereitung unserer Daten wenden wir uns nun der geographischen Analyse zu. Erdbeben sind ein globales Phänomen, das eng mit der tektonischen Struktur unseres Planeten verbunden ist. Durch geographische Visualisierungen können wir Muster und Häufungen erkennen, die uns tiefere Einblicke in diese Naturereignisse ermöglichen.
5.2.1 Geographische Verteilung der Erdbeben auf der Erdoberfläche¶
Die Verteilung von Erdbeben auf der Erdoberfläche ist nicht zufällig. Vielmehr folgt sie den Grenzen tektonischer Platten, die als zusammenhängende Stücke der Erdkruste und des oberen Erdmantels unseren Planeten bedecken. An diesen Plattengrenzen entstehen die meisten Erdbeben, wenn Platten aneinander reiben, unter- oder übereinander gleiten.
Um diese Zusammenhänge zu visualisieren, erstellen wir verschiedene Darstellungen der globalen Erdbebenverteilung. Dabei werden wir die Erdbeben nach Magnitude und anderen Eigenschaften differenzieren, um zusätzliche Informationsebenen in unsere Visualisierungen einzubringen.
# Import der benötigten Bibliotheken
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
# Stil für verbesserte Ästhetik setzen
sns.set_theme(style="whitegrid")
# Welt-Plot der Erdbeben erstellen
plt.figure(figsize=(14, 8))
# Hintergrund einfügen für visuellen Kontext
# Dies ist ein vereinfachter Ansatz, da wir keine Basemap verwenden können
# Wir erstellen ein transparent blaues Rechteck als "Ozean"
plt.axhspan(-90, 90, facecolor='lightblue', alpha=0.3, zorder=0)
# Scatterplot der Erdbeben
scatter = plt.scatter(
earthquakes_subset['Longitude'],
earthquakes_subset['Latitude'],
c=earthquakes_subset['Magnitude'],
s=earthquakes_subset['Magnitude']**1.5, # Größe basierend auf Magnitude
alpha=0.6,
cmap='YlOrRd',
edgecolor='k',
linewidth=0.2,
zorder=2
)
# Grenzen und Beschriftungen
plt.xlim(-180, 180)
plt.ylim(-90, 90)
plt.grid(True, alpha=0.3, zorder=1)
plt.xlabel('Längengrad', fontsize=12)
plt.ylabel('Breitengrad', fontsize=12)
plt.title('Globale Verteilung von Erdbeben', fontsize=16)
# Farbbalken hinzufügen
cbar = plt.colorbar(scatter, extend='both', pad=0.01)
cbar.set_label('Magnitude (Richter-Skala)', fontsize=12)
# Anmerkung zur Datensatzgröße
plt.annotate(f'Datensatz umfasst {len(earthquakes_subset)} Erdbebenereignisse',
xy=(0.01, 0.01), xycoords='figure fraction', fontsize=10)
plt.tight_layout()
plt.show()

# Erstellen einer Heatmap der Erdbebenintensität mit Matplotlib
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
import seaborn as sns
# Für bessere Ästhetik
sns.set_theme(style="white")
plt.figure(figsize=(14, 8))
# Hintergrund einfügen für visuellen Kontext
plt.axhspan(-90, 90, facecolor='lightblue', alpha=0.3, zorder=0)
# Daten vorbereiten
# Wir verwenden nur eine Stichprobe für eine effizientere Berechnung der KDE
sample_size = min(5000, len(earthquakes_subset))
sampled_data = earthquakes_subset.sample(sample_size, random_state=42)
# Daten für die heatmap
x = sampled_data['Longitude']
y = sampled_data['Latitude']
weights = sampled_data['Magnitude'] ** 2 # Quadrierung für stärkere Betonung höherer Magnituden
# Punkte für das Hintergrundraster erstellen
x_grid, y_grid = np.mgrid[-180:180:360j, -90:90:180j]
positions = np.vstack([x_grid.ravel(), y_grid.ravel()])
# Kerneldichteberechnung mit Gewichten
kde = gaussian_kde(np.vstack([x, y]), weights=weights)
z = kde(positions)
# Umformen für Plotting
z = z.reshape(x_grid.shape)
# Heatmap zeichnen
plt.contourf(x_grid, y_grid, z, levels=50, cmap='inferno', alpha=0.7, zorder=1)
# Grenzen und Beschriftungen
plt.xlim(-180, 180)
plt.ylim(-90, 90)
plt.grid(True, alpha=0.3, zorder=0)
plt.xlabel('Längengrad', fontsize=12)
plt.ylabel('Breitengrad', fontsize=12)
plt.title('Heatmap der globalen Erdbebenintensität', fontsize=16)
# Beschreibung hinzufügen
plt.annotate('Höhere Intensität zeigt Regionen mit stärkerer seismischer Aktivität',
xy=(0.5, 0.02), xycoords='figure fraction',
ha='center', fontsize=11, bbox=dict(facecolor='white', alpha=0.7))
plt.tight_layout()
plt.show()

5.2.2 Dreidimensionale Visualisierung unter Berücksichtigung der Bebentiefe¶
Während die zweidimensionale Darstellung der Erdbeben auf der Erdoberfläche bereits wertvolle Erkenntnisse liefert, bietet eine dreidimensionale Betrachtung zusätzliche Einblicke. Die Tiefe, in der ein Erdbeben stattfindet, ist ein entscheidender Parameter, der Hinweise auf die tektonischen Prozesse gibt, die das Beben verursacht haben.
Flache Erdbeben (weniger als 70 km Tiefe) treten typischerweise an Spreizungszonen oder Transformstörungen auf. Mittlere Erdbeben (70-300 km) und tiefe Erdbeben (300-700 km) sind hingegen charakteristisch für Subduktionszonen, wo eine tektonische Platte unter eine andere abtaucht.
Im Folgenden erstellen wir verschiedene dreidimensionale Visualisierungen, um diese räumliche Dimension der Erdbebendaten zu erfassen. Dies ermöglicht uns, die tektonischen Strukturen unterhalb der Erdoberfläche besser zu verstehen.
# 3D-Visualisierung mit Standard-Matplotlib
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as mcolors
import pandas as pd
# Filtern der Daten für bessere Sichtbarkeit (nur stärkere Erdbeben)
strong_earthquakes = earthquakes_subset[earthquakes_subset['Magnitude'] > 5.5].copy()
# Falls der Datensatz sehr groß ist, Sample nehmen
if len(strong_earthquakes) > 2000:
strong_earthquakes = strong_earthquakes.sample(2000, random_state=42)
# Jahr aus dem Date_Time-Feld extrahieren für Farbcodierung
strong_earthquakes['Year'] = strong_earthquakes['Date_Time'].dt.year
# Farbskala vorbereiten
min_year = strong_earthquakes['Year'].min()
max_year = strong_earthquakes['Year'].max()
norm = mcolors.Normalize(min_year, max_year)
cmap = plt.cm.viridis
# 3D-Plot erstellen
fig = plt.figure(figsize=(12, 10))
ax = fig.add_subplot(111, projection='3d')
# Daten plotten
scatter = ax.scatter(
strong_earthquakes['Longitude'],
strong_earthquakes['Latitude'],
-strong_earthquakes['Depth'], # Minus für korrekte Darstellung unter der Oberfläche
c=strong_earthquakes['Year'],
cmap=cmap,
norm=norm,
s=strong_earthquakes['Magnitude']**2 * 0.5,
alpha=0.7,
edgecolor='k',
linewidth=0.2
)
# Achsenbeschriftung
ax.set_xlabel('Längengrad', fontsize=12)
ax.set_ylabel('Breitengrad', fontsize=12)
ax.set_zlabel('Tiefe (km)', fontsize=12)
# Farbbalken für das Jahr
cbar = fig.colorbar(scatter, shrink=0.6, aspect=20)
cbar.set_label('Jahr des Erdbebens', fontsize=12)
# Legende für Magnitudenskala erstellen
from matplotlib.lines import Line2D
legend_elements = [
Line2D([0], [0], marker='o', color='w', markerfacecolor='gray',
label=f'Magnitude {mag}', markersize=np.sqrt(mag**2 * 0.5),
markeredgecolor='k', markeredgewidth=0.2, alpha=0.7)
for mag in [6.0, 7.0, 8.0]
]
ax.legend(handles=legend_elements, loc='upper right', title='Magnitude')
# Tiefenskala anpassen und Orientierung verbessern
ax.set_zlim(-700, 0) # Typische Erdbebengrenze bei ca. 700 km
ax.view_init(elev=30, azim=45) # Perspektive anpassen
# Einfache "Erdoberfläche" als Referenz hinzufügen
# Wir verwenden ein vereinfachtes Gitter
max_lon, min_lon = 180, -180
max_lat, min_lat = 90, -90
xx, yy = np.meshgrid([min_lon, max_lon], [min_lat, max_lat])
zz = np.zeros(xx.shape)
ax.plot_surface(xx, yy, zz, alpha=0.1, color='lightblue')
# Titel
ax.set_title('3D-Visualisierung von Erdbeben (Magnitude > 5.5)', fontsize=14)
plt.tight_layout()
plt.show()

# Funktion für die regionale Analyse von Erdbeben mit Matplotlib
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as mcolors
import pandas as pd
def visualize_region(data, region_name, lon_range, lat_range, min_magnitude=5.0):
"""
Erstellt eine 2D- und 3D-Visualisierung für eine spezifische tektonische Region.
Parameters:
-----------
data : DataFrame
Erdbebendaten
region_name : str
Name der Region für den Titel
lon_range : tuple
(min_lon, max_lon) für die Region
lat_range : tuple
(min_lat, max_lat) für die Region
min_magnitude : float
Minimale Magnitude zur Filterung der Daten
"""
# Filtern der relevanten Daten
mask = ((data['Longitude'] >= lon_range[0]) &
(data['Longitude'] <= lon_range[1]) &
(data['Latitude'] >= lat_range[0]) &
(data['Latitude'] <= lat_range[1]) &
(data['Magnitude'] >= min_magnitude))
region_data = data[mask].copy()
# Prüfen, ob Daten vorhanden sind
if len(region_data) == 0:
print(f"Keine Daten für die Region {region_name} gefunden.")
return
print(f"Analysiere {len(region_data)} Erdbeben in der Region {region_name}.")
# Kategorie der Tiefe hinzufügen
region_data['Depth_Category'] = pd.cut(
region_data['Depth'],
bins=[0, 70, 300, 700],
labels=['Flach (0-70 km)', 'Mittel (70-300 km)', 'Tief (>300 km)']
)
# Erstellen einer Figur mit zwei Subplots (2D und 3D)
fig = plt.figure(figsize=(14, 10))
# 2D-Plot (oben)
ax1 = fig.add_subplot(211)
# Hintergrund für Meer
ax1.axhspan(lat_range[0], lat_range[1], facecolor='lightblue', alpha=0.3, zorder=0)
# Plotten nach Tiefenkategorie
categories = region_data['Depth_Category'].unique()
# Farbpalette für Tiefenkategorien
colors = {'Flach (0-70 km)': 'yellow',
'Mittel (70-300 km)': 'orange',
'Tief (>300 km)': 'red'}
# Nach Kategorie plotten
for category in categories:
subset = region_data[region_data['Depth_Category'] == category]
ax1.scatter(
subset['Longitude'],
subset['Latitude'],
c=colors[category],
s=subset['Magnitude']**1.5,
alpha=0.7,
edgecolor='k',
linewidth=0.2,
label=category
)
# Achsenbeschriftung und Titel
ax1.set_xlim(lon_range)
ax1.set_ylim(lat_range)
ax1.set_xlabel('Längengrad', fontsize=12)
ax1.set_ylabel('Breitengrad', fontsize=12)
ax1.set_title(f'2D-Ansicht: {region_name}', fontsize=14)
ax1.grid(True, alpha=0.3)
ax1.legend()
# 3D-Plot (unten)
ax2 = fig.add_subplot(212, projection='3d')
# Nach Kategorie plotten
for category in categories:
subset = region_data[region_data['Depth_Category'] == category]
ax2.scatter(
subset['Longitude'],
subset['Latitude'],
-subset['Depth'],
c=colors[category],
s=subset['Magnitude']**1.5,
alpha=0.7,
edgecolor='k',
linewidth=0.2,
label=category
)
# Einfache "Erdoberfläche" hinzufügen
xx, yy = np.meshgrid([lon_range[0], lon_range[1]], [lat_range[0], lat_range[1]])
zz = np.zeros(xx.shape)
ax2.plot_surface(xx, yy, zz, alpha=0.1, color='lightblue')
# Achsenbeschriftung und Konfiguration
ax2.set_xlim(lon_range)
ax2.set_ylim(lat_range)
ax2.set_zlim(-max(region_data['Depth'].max() * 1.1, 200), 0)
ax2.set_xlabel('Längengrad', fontsize=12)
ax2.set_ylabel('Breitengrad', fontsize=12)
ax2.set_zlabel('Tiefe (km)', fontsize=12)
ax2.set_title(f'3D-Ansicht: {region_name}', fontsize=14)
# Perspektive anpassen
ax2.view_init(elev=30, azim=225)
# Haupttitel für die gesamte Figur
plt.suptitle(f'Erdbebenanalyse: {region_name} (Magnitude ≥ {min_magnitude})',
fontsize=16, y=0.98)
plt.tight_layout()
plt.subplots_adjust(top=0.9)
plt.show()
# Analyse verschiedener tektonischer Regionen
# 1. Japan und Umgebung (Subduktionszone)
visualize_region(earthquakes_subset, "Japan und Umgebung",
lon_range=(125, 150), lat_range=(25, 45), min_magnitude=5.5)
# 2. Anden (Südamerika) - Subduktionszone
visualize_region(earthquakes_subset, "Anden (Südamerika)",
lon_range=(-85, -65), lat_range=(-45, 0), min_magnitude=5.5)
Analysiere 2014 Erdbeben in der Region Japan und Umgebung.

Analysiere 1525 Erdbeben in der Region Anden (Südamerika).

Soviel also zur geographischen Visualisierung. Wie Sie sich inzwischen vorstellen können, ist so eine Analyse nach fast beliebigen Gesichtspunkten und Kriterien möglich.
5.3 Zeitliche Visualisierung der globalen Erdbebentätigkeit seit 1965¶
Neben der räumlichen Verteilung ist die zeitliche Dimension von Erdbeben ein faszinierender Aspekt, der uns erlaubt, die dynamische Natur seismischer Aktivität zu erfassen. Durch Animationen können wir die Entwicklung der Erdbebentätigkeit über Jahrzehnte hinweg verfolgen und möglicherweise Muster oder Veränderungen in der globalen seismischen Aktivität identifizieren.
In diesem Abschnitt erstellen wir Animationen, die die Erdbeben seit 1965 chronologisch darstellen. Dabei verwenden wir monatliche Zusammenfassungen, um die Datenmenge handhabbar zu halten und gleichzeitig aussagekräftige Visualisierungen zu ermöglichen.
5.3.1 2D-Animation der auftretenden Erdbeben auf der Erdoberfläche¶
Eine zweidimensionale Animation auf der Weltkarte erlaubt es uns, die globale Verteilung der Erdbeben im Zeitverlauf zu betrachten. Dabei können wir erkennen, wie sich die seismische Aktivität entlang der tektonischen Plattengrenzen entwickelt und wie bestimmte Regionen in verschiedenen Zeiträumen aktiver oder weniger aktiv sind.
# Import der benötigten Bibliotheken
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.animation as animation
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
import matplotlib.colors as mcolors
# Erhöhung des Animationslimits (setzen auf 100 MB statt der Standard 20 MB)
plt.rcParams['animation.embed_limit'] = 100 # in MB
# Stil für bessere Ästhetik
sns.set_theme(style="whitegrid")
# Daten vorbereiten: Gruppieren nach Jahr und Monat
earthquakes_subset['year_month'] = earthquakes_subset['Date_Time'].dt.to_period('M')
# Sortieren nach Datum für die Animation
earthquakes_subset = earthquakes_subset.sort_values('Date_Time')
# Liste der einzigartigen Jahr-Monat-Kombinationen
unique_periods = earthquakes_subset['year_month'].unique()
# Funktion zum Erstellen der Animation
def create_2d_animation():
# Figur und Achse erstellen
fig, ax = plt.subplots(figsize=(12, 8))
# Hintergrund für Meer
ax.axhspan(-90, 90, facecolor='lightblue', alpha=0.3, zorder=0)
# Grenzen und Beschriftungen
ax.set_xlim(-180, 180)
ax.set_ylim(-90, 90)
ax.grid(True, alpha=0.3, zorder=0)
ax.set_xlabel('Longitude', fontsize=12)
ax.set_ylabel('Latitude', fontsize=12)
# Titel vorbereiten (wird in der Animation aktualisiert)
title = ax.set_title('Earthquakes over time', fontsize=14)
# Scatter-Plot erstellen (leer zu Beginn)
scatter = ax.scatter([], [], c=[], s=[], alpha=0.7, edgecolor='k', linewidth=0.2, cmap='YlOrRd', zorder=2)
# Colorbar für die Magnitude
cbar = plt.colorbar(scatter, ax=ax, pad=0.01)
cbar.set_label('Magnitude', fontsize=12)
# Counter für die Anzahl der dargestellten Monate
counter_text = ax.text(0.01, 0.01, '', transform=ax.transAxes, fontsize=10,
bbox=dict(facecolor='white', alpha=0.7))
# Initialisierung für die Animation
def init():
scatter.set_offsets(np.empty((0, 2)))
title.set_text('')
counter_text.set_text('')
return scatter, title, counter_text
# Haltezeit für jeden Frame in Millisekunden (für langsamere Animation)
frame_interval = 200 # ms
# Variable für die kumulierten Daten - HIER außerhalb definieren
all_data = pd.DataFrame()
# Animationsfunktion für jeden Frame
def update(frame):
# Zugriff auf die äußere Variable ohne global-Deklaration
nonlocal all_data
# Daten für den aktuellen Monat und alle vorherigen Monate
current_period_str = str(unique_periods[frame])
current_period = pd.Period(current_period_str)
# Aktuellen Monat zum kumulierten Datensatz hinzufügen
month_data = earthquakes_subset[earthquakes_subset['year_month'] == unique_periods[frame]]
all_data = pd.concat([all_data, month_data])
# Visualisiere die letzten x Monate oder alle bisherigen Daten, je nachdem was weniger ist
display_window = 12 # Monate
if frame >= display_window:
# Letzte x Monate anzeigen
display_periods = unique_periods[frame-display_window+1:frame+1]
display_data = earthquakes_subset[earthquakes_subset['year_month'].isin(display_periods)]
else:
# Alle bisherigen Daten anzeigen
display_data = all_data.copy()
# Skalierungsfaktor für die Größe, abhängig von der Menge der Daten
size_factor = max(0.5, min(2.0, 1000 / len(display_data)))
# Daten aktualisieren
scatter.set_offsets(np.vstack((display_data['Longitude'], display_data['Latitude'])).T)
scatter.set_array(display_data['Magnitude'])
# Größe aktualisieren (basierend auf Magnitude)
sizes = display_data['Magnitude']**1.5 * size_factor
scatter.set_sizes(sizes)
# Titel und Counter aktualisieren
title.set_text(f'Global earthquakes above M 5.5 during 12 month up to {current_period_str}')
counter_text.set_text(f'Number of earthquakes: {len(display_data)}')
# Farbbalken-Grenzen aktualisieren
scatter.set_clim(
vmin=earthquakes_subset['Magnitude'].min(),
vmax=earthquakes_subset['Magnitude'].max()
)
return scatter, title, counter_text
# Animation erstellen
anim = FuncAnimation(
fig, update, frames=len(unique_periods),
init_func=init, blit=True, interval=frame_interval
)
plt.tight_layout()
return anim
# Animation erstellen
anim_2d = create_2d_animation()
# Animation als HTML anzeigen
# HTML(anim_2d.to_jshtml())
# Optional: Animation als Datei speichern
# anim_2d.save('erdbeben_2d_animation_12_monate.mp4', writer='ffmpeg', fps=5, dpi=300)
5.3.2 3D-Animation der auftretenden Erdbeben auf der Erdkugel¶
Eine dreidimensionale Animation auf einer Erdkugel bietet eine noch realistischere Darstellung der globalen Erdbebentätigkeit. Durch die Einbeziehung der Tiefe und die sphärische Darstellung können wir ein umfassenderes Bild der seismischen Aktivität gewinnen, insbesondere in Subduktionszonen, wo Erdbeben in unterschiedlichen Tiefen auftreten.
In der folgenden Visualisierung modellieren wir die Erde als Kugel und platzieren die Erdbeben entsprechend ihrer geografischen Koordinaten und Tiefe. Dabei skalieren wir die Tiefe für eine bessere visuelle Wirkung und lassen die Kugel zur besseren räumlichen Wahrnehmung rotieren.
# Erhöhung des Animationslimits auf 100 MB
plt.rcParams['animation.embed_limit'] = 100 # in MB
# Vereinfachte Funktion zur Umrechnung von Längen-/Breitengrad in 3D-Koordinaten
def lat_lon_to_cartesian(lat, lon, depth=0, earth_radius=6371):
"""
Konvertiert Längen- und Breitengrad in 3D-Koordinaten.
Parameters:
-----------
lat : float
Breitengrad in Grad (-90 bis 90)
lon : float
Längengrad in Grad (-180 bis 180)
depth : float
Tiefe in km unter der Oberfläche (positive Werte)
earth_radius : float
Erdradius in km (Standardwert: 6371 km)
Returns:
--------
x, y, z : float
3D-Koordinaten
"""
# Umrechnung in Radianten
lat_rad = np.radians(lat)
lon_rad = np.radians(lon)
# Angepasster Radius (Erdradius minus Tiefe mal 5, für bessere Sichtbarkeit)
radius = earth_radius - depth * 5
# Umrechnung in kartesische Koordinaten
x = radius * np.cos(lat_rad) * np.cos(lon_rad)
y = radius * np.cos(lat_rad) * np.sin(lon_rad)
z = radius * np.sin(lat_rad)
return x, y, z
# Funktion zum Erstellen der vereinfachten 3D-Animation
def create_simplified_3d_animation():
# Figur und Achse erstellen
fig = plt.figure(figsize=(12, 6))
ax = fig.add_subplot(111, projection='3d')
ax.set_axis_off()
# Normalisierter Radius für die Visualisierung (echter Erdradius = 6371 km)
# Wir normalisieren auf einen Wert von 1 für bessere Darstellung
visualization_radius = 1.0
scale_factor = visualization_radius / 6371
# Gitter für die Erdoberfläche (nur Gitterlinien, keine Fläche)
u = np.linspace(0, 2 * np.pi, 30)
v = np.linspace(0, np.pi, 30)
# Zeichnen der Längen- und Breitengrade als einfaches Gitter
# Breitengrade
for lat in np.arange(-90, 91, 30):
lat_rad = np.radians(lat)
circle_radius = visualization_radius * np.cos(lat_rad)
z_value = visualization_radius * np.sin(lat_rad)
theta = np.linspace(0, 2 * np.pi, 100)
x = circle_radius * np.cos(theta)
y = circle_radius * np.sin(theta)
z = np.ones_like(theta) * z_value
if lat == 0: # Äquator hervorheben
ax.plot(x, y, z, color='blue', linewidth=1.0, alpha=0.7)
else:
ax.plot(x, y, z, color='gray', linewidth=0.5, alpha=0.3)
# Längengrade
for lon in np.arange(-180, 181, 30):
lon_rad = np.radians(lon)
theta = np.linspace(-np.pi/2, np.pi/2, 100)
x = visualization_radius * np.cos(theta) * np.cos(lon_rad)
y = visualization_radius * np.cos(theta) * np.sin(lon_rad)
z = visualization_radius * np.sin(theta)
if lon == 0: # Nullmeridian hervorheben
ax.plot(x, y, z, color='green', linewidth=1.0, alpha=0.7)
else:
ax.plot(x, y, z, color='gray', linewidth=0.5, alpha=0.3)
# Grenzen und Beschriftungen
ax.set_xlim(-visualization_radius*1.2, visualization_radius*1.2)
ax.set_ylim(-visualization_radius*1.2, visualization_radius*1.2)
ax.set_zlim(-visualization_radius*1.2, visualization_radius*1.2)
# Achsen mit Beschriftungen anzeigen
ax.set_xlabel('X', fontsize=10)
ax.set_ylabel('Y', fontsize=10)
ax.set_zlabel('Z', fontsize=10)
# Titel vorbereiten
title = ax.set_title('Earthquakes on the globe', fontsize=14)
# Counter für die Anzahl der dargestellten Monate
counter_text = ax.text2D(0.01, 0.01, '', transform=ax.transAxes, fontsize=10,
bbox=dict(facecolor='white', alpha=0.7))
# Scatter-Plot erstellen (leer zu Beginn)
scatter = ax.scatter([], [], [], c=[], s=[], alpha=0.7, edgecolor='k',
linewidth=0.2, cmap='YlOrRd', depthshade=True)
# Farbbalken für die Magnitude
cbar = plt.colorbar(scatter, ax=ax, shrink=0.7, aspect=20, pad=0.1)
cbar.set_label('Magnitude', fontsize=12)
# Liste der einzigartigen Jahr-Monat-Kombinationen
unique_periods = earthquakes_subset['year_month'].unique()
# Anzahl der Frames reduzieren (jeden zweiten Monat anzeigen)
frame_step = 1
selected_periods = unique_periods[::frame_step]
# Rotationswinkel für langsamere Rotation
rotation_angle = 2
# Initialisierung der Animation
def init():
scatter._offsets3d = (np.array([]), np.array([]), np.array([]))
title.set_text('')
counter_text.set_text('')
return scatter, title, counter_text
# Variable für die kumulierten Daten
all_data = pd.DataFrame()
# Animationsfunktion für jeden Frame
def update(frame_idx):
# Zugriff auf die äußere Variable
nonlocal all_data
# Index im Original-Array
frame = frame_idx * frame_step
if frame >= len(unique_periods):
frame = len(unique_periods) - 1
# Aktuelle Rotationsposition
elev = 20
azim = (frame_idx * rotation_angle) % 360
ax.view_init(elev=elev, azim=azim)
# Daten für den aktuellen Monat und alle vorherigen Monate
current_period = unique_periods[frame]
current_period_str = str(current_period)
# Alle Daten bis zum aktuellen Frame sammeln
all_periods = unique_periods[:frame+1]
all_data = earthquakes_subset[earthquakes_subset['year_month'].isin(all_periods)]
# Visualisiere die letzten x Monate oder alle bisherigen Daten, je nachdem was weniger ist
display_window = 3 # Monate
if frame >= display_window:
# Letzte x Monate anzeigen
display_periods = unique_periods[frame-display_window+1:frame+1]
display_data = earthquakes_subset[earthquakes_subset['year_month'].isin(display_periods)]
else:
# Alle bisherigen Daten anzeigen
display_data = all_data.copy()
# Bei vielen Daten: Sample nehmen für bessere Performance
max_points = 3000
if len(display_data) > max_points:
display_data = display_data.sample(max_points, random_state=42)
# Koordinaten berechnen und auf die Visualisierungsskala skalieren
xs, ys, zs = [], [], []
for idx, quake in display_data.iterrows():
x, y, z = lat_lon_to_cartesian(
quake['Latitude'],
quake['Longitude'],
depth=quake['Depth']
)
# Skalieren auf Visualisierungsmaßstab
xs.append(x * scale_factor)
ys.append(y * scale_factor)
zs.append(z * scale_factor)
# Skalierungsfaktor für die Größe
size_factor = max(0.5, min(2.0, 1000 / len(display_data)))
# Daten aktualisieren
scatter._offsets3d = (xs, ys, zs)
scatter.set_array(display_data['Magnitude'].values)
# Größe aktualisieren (basierend auf Magnitude)
sizes = display_data['Magnitude'].values**1.5 * size_factor
scatter.set_sizes(sizes)
# Titel und Counter aktualisieren
title.set_text(f'Global earthquakes above M 5.5 during 3 month up to {current_period_str}')
counter_text.set_text(f'Number of earthquakes: {len(display_data)}')
# Farbbalken-Grenzen aktualisieren
scatter.set_clim(
vmin=earthquakes_subset['Magnitude'].min(),
vmax=earthquakes_subset['Magnitude'].max()
)
return scatter, title, counter_text
# Animation erstellen
anim = FuncAnimation(
fig, update, frames=len(selected_periods),
init_func=init, blit=False, interval=250
)
plt.tight_layout()
return anim
# Vereinfachte Animation erstellen
anim_3d_simplified = create_simplified_3d_animation()
# Animation als HTML anzeigen
# HTML(anim_3d_simplified.to_jshtml())
# Optional: Animation als Datei speichern
# anim_3d_simplified.save('erdbeben_3d_animation_3_monate.mp4', writer='ffmpeg', fps=5, dpi=300)
Die Animationen der globalen Erdbebentätigkeit seit 1965 ermöglichen es uns, einen Einblick in die globale Dynamik dieser Phänomene zu bekommen. Weitere Einsichten kann man durch genauere Aufschlüsselung von zusammenhängen erreichen.
5.4 Einfache Datenanalyse¶
Nach der Visualisierung der räumlichen und zeitlichen Verteilung von Erdbeben wenden wir uns nun einigen Arten der detaillierteren Analyse unserer Daten zu. In diesem Abschnitt werden wir einige grundlegende Analysetechniken anwenden, um Muster und Zusammenhänge in den Erdbebendaten zu identifizieren, oder das zumindest zu versuchen.
5.4.1 Zusammenhang bzw. Korrelation zwischen Magnitude und Tiefe¶
Eine interessante Frage in der Seismologie ist, ob es einen Zusammenhang zwischen der Tiefe eines Erdbebens und seiner Magnitude gibt. Intuitiv könnte man vermuten, dass tiefere Erdbeben möglicherweise mit höheren Magnituden verbunden sind, da der Druck in größerer Tiefe höher ist. Andererseits könnten die Gesteinsstrukturen in tieferen Erdschichten anders auf Spannungen reagieren.
Wir werden zunächst die grundlegende Korrelation zwischen diesen Variablen untersuchen und dann verschiedene statistische Methoden anwenden, um etwaige Muster zu erkennen.
# Import der benötigten Bibliotheken
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from scipy import stats
from sklearn.linear_model import LinearRegression
# Für bessere Ästhetik
sns.set_theme(style="whitegrid")
# Scatterplot von Magnitude gegen Tiefe
plt.figure(figsize=(12, 6))
plt.scatter(
earthquakes_subset['Depth'],
earthquakes_subset['Magnitude'],
alpha=0.3,
edgecolor='none',
c=earthquakes_subset['Magnitude'],
cmap='viridis'
)
plt.colorbar(label='Magnitude')
plt.xlabel('Tiefe (km)')
plt.ylabel('Magnitude')
plt.title('Beziehung zwischen Erdbebenmagitude und -tiefe')
plt.grid(True, alpha=0.3)
plt.show()
# Berechnen des Pearson-Korrelationskoeffizienten
correlation, p_value = stats.pearsonr(
earthquakes_subset['Depth'],
earthquakes_subset['Magnitude']
)
print(f"Pearson-Korrelationskoeffizient: {correlation:.4f}")
print(f"P-Wert: {p_value:.4e}")
# Interpretation der Korrelation
if abs(correlation) < 0.1:
print("Interpretation: Sehr schwache oder keine Korrelation")
elif abs(correlation) < 0.3:
print("Interpretation: Schwache Korrelation")
elif abs(correlation) < 0.5:
print("Interpretation: Moderate Korrelation")
elif abs(correlation) < 0.7:
print("Interpretation: Starke Korrelation")
else:
print("Interpretation: Sehr starke Korrelation")
if p_value < 0.05:
print("Die Korrelation ist statistisch signifikant (p < 0.05).")
else:
print("Die Korrelation ist statistisch nicht signifikant (p ≥ 0.05).")
# Untersuchung der Beziehung mit verschiedenen Visualisierungen
plt.figure(figsize=(16, 12))
# 1. Scatterplot mit Hexbin für bessere Sichtbarkeit bei großen Datenmengen
plt.subplot(2, 2, 1)
plt.hexbin(
earthquakes_subset['Depth'],
earthquakes_subset['Magnitude'],
gridsize=40,
cmap='viridis',
mincnt=1
)
plt.colorbar(label='Anzahl der Erdbeben')
plt.xlabel('Tiefe (km)')
plt.ylabel('Magnitude')
plt.title('Hexbin-Plot: Magnitude vs. Tiefe')
plt.grid(True, alpha=0.3)
# 2. Box-Plot der Magnitude pro Tiefenbereich
plt.subplot(2, 2, 2)
# Erstellen von Tiefenkategorien
earthquakes_subset['Depth_Category'] = pd.cut(
earthquakes_subset['Depth'],
bins=[0, 30, 70, 150, 300, 500, 800],
labels=['0-30', '30-70', '70-150', '150-300', '300-500', '500+']
)
sns.boxplot(x='Depth_Category', y='Magnitude', data=earthquakes_subset)
plt.xlabel('Tiefenbereich (km)')
plt.ylabel('Magnitude')
plt.title('Magnitude nach Tiefenbereich')
plt.grid(True, alpha=0.3)
# 3. Violin-Plot für detailliertere Verteilung
plt.subplot(2, 2, 3)
sns.violinplot(x='Depth_Category', y='Magnitude', data=earthquakes_subset)
plt.xlabel('Tiefenbereich (km)')
plt.ylabel('Magnitude')
plt.title('Verteilung der Magnitude nach Tiefenbereich')
plt.grid(True, alpha=0.3)
# 4. Durchschnittliche Magnitude pro Tiefenbereich
plt.subplot(2, 2, 4)
depth_mag_avg = earthquakes_subset.groupby('Depth_Category')['Magnitude'].mean().reset_index()
depth_counts = earthquakes_subset['Depth_Category'].value_counts().sort_index()
plt.bar(
depth_mag_avg['Depth_Category'],
depth_mag_avg['Magnitude'],
alpha=0.7
)
plt.xlabel('Tiefenbereich (km)')
plt.ylabel('Durchschnittliche Magnitude')
plt.title('Durchschnittliche Magnitude nach Tiefenbereich')
for i, (idx, count) in enumerate(depth_counts.items()):
plt.text(i, depth_mag_avg['Magnitude'].iloc[i] + 0.05,
f'n={count}', ha='center')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Analyse nach Magnitude-Typen
plt.figure(figsize=(14, 6))
# Korrelation für jeden Magnitude-Typ
mag_types = earthquakes_subset['Magnitude Type'].unique()
correlations = {}
plt.subplot(1, 2, 1)
for mag_type in mag_types:
subset = earthquakes_subset[earthquakes_subset['Magnitude Type'] == mag_type]
if len(subset) > 30: # Mindestanzahl für signifikante Korrelation
corr, p = stats.pearsonr(subset['Depth'], subset['Magnitude'])
correlations[mag_type] = (corr, p, len(subset))
# Separate Farbe für jeden Magnitude-Typ
plt.scatter(
subset['Depth'],
subset['Magnitude'],
alpha=0.5,
label=f'{mag_type} (r={corr:.2f}, n={len(subset)})'
)
plt.xlabel('Tiefe (km)')
plt.ylabel('Magnitude')
plt.title('Magnitude vs. Tiefe nach Magnitude-Typ')
plt.legend(loc='upper left', bbox_to_anchor=(1, 1))
plt.grid(True, alpha=0.3)
# Zusammenfassung der Korrelationen
plt.subplot(1, 2, 2)
mag_types = [k for k, v in sorted(correlations.items(), key=lambda item: item[1][0])]
corr_values = [correlations[k][0] for k in mag_types]
counts = [correlations[k][2] for k in mag_types]
# Farbe basierend auf p-Wert (grün wenn stark, rot wenn nicht)
colors = ['green' if correlations[k][1] < 0.5 else 'red' for k in mag_types]
y_pos = np.arange(len(mag_types))
plt.barh(y_pos, corr_values, color=colors, alpha=0.7)
plt.yticks(y_pos, mag_types)
for i, count in enumerate(counts):
plt.text(correlations[mag_types[i]][0] * 1.05, i, f'n={count}')
plt.axvline(x=0, color='k', linestyle='-', alpha=0.3)
plt.xlabel('Korrelationskoeffizient')
plt.title('Korrelation zwischen Tiefe und Magnitude nach Magnitude-Typ')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

Pearson-Korrelationskoeffizient: 0.0235 P-Wert: 3.3131e-04 Interpretation: Sehr schwache oder keine Korrelation Die Korrelation ist statistisch signifikant (p < 0.05).
/var/folders/3y/8zcxbyzx0g14cyfs7kqt0x680000gn/T/ipykernel_77586/984988547.py:97: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning. depth_mag_avg = earthquakes_subset.groupby('Depth_Category')['Magnitude'].mean().reset_index()


Hier sieht man allerhand verschiedene Aufbereitungen der Datenpunkte in Abhängigkeit von Tiefe und Magnitude. Die resultierende generelle Korrelation und auch die $r$-Koeffizienten für verschiedene Teilmengen wie die verschiedenen Magnitude-Typen sind sehr nahe an null. Eine derart schwache bzw. nicht-Korrelation bedeutet also, dass es vermutlich keinen klaren linearen Zusammenhang zwischen der Tiefe und der Magnitude bei Erdbeben gibt.
Andererseits wird eine statistische Signifikanz über den kleinen $p$-Wert ausgegeben. Dieser kleine Wert ist jedoch durch die riesige Stichprobe bedingt, denn $p$ gibt einfach nur die Wahrscheinlichkeit dafür an, dass man die in unseren Daten gemessene Korrelation sehen würde, wenn bei keiner vorhandenen Korrelation zufällige Werte genommen würden. Je größer dabei die Stichprobe ist, desto kleiner wird diese Wahrscheinlichkeit.
Insgesamt bedeutet das also, dass wir hier keine nennenswerten Zusammenhänge finden konnten.
5.4.2 Inverser Zusammenhang der Häufigkeit in Abhängigkeit von der Magnitude¶
Eine der bemerkenswertesten Beobachtungen in der Seismologie ist die Beziehung zwischen der Häufigkeit von Erdbeben und ihrer Magnitude. Dieses Verhältnis folgt einem inversen Zusammenhang, der als Gutenberg-Richter-Gesetz bekannt ist. Es besagt, dass die Häufigkeit der Erdbeben exponentiell mit zunehmender Magnitude abnimmt. Mathematisch ausgedrückt hat man: $$\log_{10}(N) = a – b·M$$
wobei:
- $N$ die Häufigkeit der Erdbeben mit einer Magnitude $\gt M$ ist
- $a$ und $b$ sind Konstanten, wobei $b$ typischerweise Werte um 1 annimmt
Diese Beziehung bedeutet, dass für jede Erhöhung der Magnitude um eine Einheit die Anzahl der Erdbeben auf etwa ein Zehntel sinkt. So gibt es beispielsweise etwa zehnmal so viele Erdbeben der Magnitude 5 wie der Magnitude 6, und etwa hundertmal so viele wie der Magnitude 7.
Im Folgenden werden wir diese Beziehung in unseren Daten untersuchen und visualisieren. Wir machen das allerdings nicht für die kumulativen Anzahlen von Erdbeben $\gt M$, sondern verwenden ein einfaches Histogram. Das ist so etwas wie eine “differenzielle” Version des Gesetzes und ebenfalls exponentiell mit ähnlichen Parametern.
# Import der benötigten Bibliotheken
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from scipy import stats
from scipy.optimize import curve_fit
# Für bessere Ästhetik
sns.set_theme(style="whitegrid")
# Histogramm der Erdbebenmagnitude mit logarithmischer Y-Achse
plt.figure(figsize=(12, 6))
# Histogramm erstellen
bins = np.arange(4.0, 10.0, 0.1) # Bins von 4.0 bis 10.0 in 0.1-Schritten
counts, edges = np.histogram(earthquakes_subset['Magnitude'], bins=bins)
centers = (edges[:-1] + edges[1:]) / 2
# Plot mit logarithmischer Y-Achse
plt.bar(centers, counts, width=0.08, alpha=0.7)
plt.yscale('log')
plt.xlabel('Magnitude')
plt.ylabel('Häufigkeit der Erdbeben (log-Skala)')
plt.title('Häufigkeitsverteilung der Erdbebenmagnitude')
plt.grid(True, alpha=0.3)
plt.show()
# Kumulatives Histogramm (Anzahl der Erdbeben ≥ Magnitude)
plt.figure(figsize=(12, 6))
# Sortieren der Daten nach Magnitude
sorted_magnitudes = np.sort(earthquakes_subset['Magnitude'])
# Umkehren der Sortierung (absteigend)
sorted_magnitudes = sorted_magnitudes[::-1]
# Kumulierte Anzahl (von größter zu kleinster Magnitude)
y = np.arange(1, len(sorted_magnitudes) + 1)
plt.plot(sorted_magnitudes, y, '-o', markersize=2, alpha=0.7)
plt.yscale('log')
plt.xlabel('Magnitude')
plt.ylabel('Anzahl der Erdbeben ≥ Magnitude (log-Skala)')
plt.title('Kumulatives Histogramm der Erdbebenmagnitude (Gutenberg-Richter-Gesetz)')
plt.grid(True, alpha=0.3)
# Funktion für das Gutenberg-Richter-Gesetz
def gutenberg_richter(M, a, b):
"""
Gutenberg-Richter-Gesetz: log_{10}(N) = a - b·M
oder N = 10^(a - b·M)
"""
return 10**(a - b*M)
# Fitparameter für das kumulative Histogramm finden
# Wir betrachten nur Magnituden über 5.0 für einen besseren Fit
mask = sorted_magnitudes >= 5.0
p0 = [10, 1] # Anfangsschätzung für a und b
try:
params, covariance = curve_fit(gutenberg_richter, sorted_magnitudes[mask], y[mask], p0=p0)
a, b = params
# Fitkurve berechnen
magnitude_range = np.linspace(5.0, sorted_magnitudes.max(), 100)
fitted_values = gutenberg_richter(magnitude_range, a, b)
# Fitkurve plotten
plt.plot(magnitude_range, fitted_values, 'r-', linewidth=2,
label=r'Gutenberg-Richter: $\log_{10}(N) =$ '+f'{a:.2f} - {b:.2f}·M')
plt.legend()
print(f"Gutenberg-Richter-Parameter: a = {a:.2f}, b = {b:.2f}")
# Interpretation des b-Wertes
if b < 0.8:
print("Interpretation: Niedriger b-Wert (< 0.8) - Relativ mehr starke Erdbeben, typisch für Gebiete mit homogenen Spannungen.")
elif b > 1.2:
print("Interpretation: Hoher b-Wert (> 1.2) - Relativ mehr schwache Erdbeben, typisch für Regionen mit heterogenen Spannungen oder vulkanischen Aktivitäten.")
else:
print("Interpretation: Durchschnittlicher b-Wert (0.8-1.2) - Typisch für die meisten tektonischen Regionen weltweit.")
except RuntimeError:
print("Fehler beim Fitten der Kurve.")
plt.show()
# Analyse des Potenzgesetzes für verschiedene Regionen
plt.figure(figsize=(14, 10))
# Funktion zur Berechnung des kumulativen Histogramms
def calculate_cumulative_histogram(magnitudes):
sorted_mags = np.sort(magnitudes)[::-1]
y = np.arange(1, len(sorted_mags) + 1)
return sorted_mags, y
# Funktion zum Fitten des Gutenberg-Richter-Gesetzes
def fit_gutenberg_richter(magnitudes, min_magnitude=5.0):
sorted_mags, counts = calculate_cumulative_histogram(magnitudes)
mask = sorted_mags >= min_magnitude
if np.sum(mask) < 10: # Mindestens 10 Datenpunkte für einen vernünftigen Fit
return None, None, sorted_mags, counts
p0 = [10, 1] # Anfangsschätzung für a und b
try:
params, _ = curve_fit(gutenberg_richter, sorted_mags[mask], counts[mask], p0=p0)
return params[0], params[1], sorted_mags, counts
except:
return None, None, sorted_mags, counts
# Regionen definieren und analysieren
regions = {
'Pazifischer Feuerring': {
'filter': ((earthquakes_subset['Longitude'] >= 120) & (earthquakes_subset['Longitude'] <= 180) &
(earthquakes_subset['Latitude'] >= -60) & (earthquakes_subset['Latitude'] <= 60)) |
((earthquakes_subset['Longitude'] >= -180) & (earthquakes_subset['Longitude'] <= -60) &
(earthquakes_subset['Latitude'] >= -60) & (earthquakes_subset['Latitude'] <= 60)),
'color': 'red'
},
'Mittelatlantischer Rücken': {
'filter': (earthquakes_subset['Longitude'] >= -60) & (earthquakes_subset['Longitude'] <= 20) &
(earthquakes_subset['Latitude'] >= -60) & (earthquakes_subset['Latitude'] <= 80),
'color': 'blue'
},
'Alpen-Himalaya-Gürtel': {
'filter': (earthquakes_subset['Longitude'] >= 20) & (earthquakes_subset['Longitude'] <= 120) &
(earthquakes_subset['Latitude'] >= 0) & (earthquakes_subset['Latitude'] <= 60),
'color': 'green'
}
}
results = {}
for i, (region_name, region_info) in enumerate(regions.items()):
regional_data = earthquakes_subset[region_info['filter']]
# Fitten des Gutenberg-Richter-Gesetzes für diese Region
a, b, sorted_mags, counts = fit_gutenberg_richter(regional_data['Magnitude'].values)
results[region_name] = {'a': a, 'b': b, 'count': len(regional_data)}
# Plot
plt.subplot(2, 2, i+1)
plt.plot(sorted_mags, counts, 'o', markersize=2, alpha=0.7, color=region_info['color'],
label=f'{region_name} (n={len(regional_data)})')
# Fitkurve, falls erfolgreich
if a is not None and b is not None:
magnitude_range = np.linspace(5.0, sorted_mags.max(), 100)
fitted_values = gutenberg_richter(magnitude_range, a, b)
plt.plot(magnitude_range, fitted_values, '-', linewidth=2, color='black',
label=f'a={a:.2f}, b={b:.2f}')
plt.yscale('log')
plt.xlabel('Magnitude')
plt.ylabel('Anzahl der Erdbeben ≥ Magnitude')
plt.title(f'Gutenberg-Richter-Gesetz: {region_name}')
plt.legend()
plt.grid(True, alpha=0.3)
# Vergleich der b-Werte für verschiedene Regionen
plt.subplot(2, 2, 4)
region_names = []
b_values = []
colors = []
for region_name, result in results.items():
if result['b'] is not None:
region_names.append(region_name)
b_values.append(result['b'])
colors.append(regions[region_name]['color'])
if b_values:
plt.bar(region_names, b_values, color=colors, alpha=0.7)
plt.axhline(y=1.0, color='k', linestyle='--', alpha=0.5, label='Globaler Durchschnitt ≈ 1.0')
plt.ylabel('b-Wert')
plt.title('Vergleich der b-Werte verschiedener Regionen')
plt.xticks(rotation=45, ha='right')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

Gutenberg-Richter-Parameter: a = 9.89, b = 1.01 Interpretation: Durchschnittlicher b-Wert (0.8-1.2) - Typisch für die meisten tektonischen Regionen weltweit.


Man sieht die erwartete Abhängigkeit sehr schön, jedenfalls für kleine $M$. Für größere $M$ wird die Häufigkeit so klein, dass statistische Fluktuationen eine immer größere Rolle spielen und manche Bins unter- bzw. überrepräsentiert sind.
5.4.3 Ein erstes einfaches Beispiel für Dimensionsreduktion¶
Dimensionsreduktion ist eine mächtige Technik in der Datenanalyse, die es uns ermöglicht, hochdimensionale Daten in einer niedrigeren Dimension zu visualisieren und zu analysieren. Sie kann dabei helfen, verborgene Strukturen und Muster in komplexen Datensätzen zu entdecken.
In unserem Fall können wir die Erdbebendaten, die mehrere Dimensionen haben (Längengrad, Breitengrad, Tiefe, Magnitude, usw.), auf weniger Dimensionen reduzieren, um möglicherweise neue Einsichten zu gewinnen.
Wir werden die Hauptkomponentenanalyse oder Principal Component Analysis (PCA) verwenden, eine der grundlegendsten und am häufigsten eingesetzten Methoden zur Dimensionsreduktion. PCA transformiert die Daten so, dass die erste Komponente die größtmögliche Varianz erfasst, die zweite Komponente die zweitgrößte Varianz und so weiter.
# Import der benötigten Bibliotheken
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.pipeline import Pipeline
# Für bessere Ästhetik
sns.set_theme(style="whitegrid")
# Daten für die PCA vorbereiten
# Wir verwenden nur numerische Spalten
features = ['Latitude', 'Longitude', 'Depth', 'Magnitude']
X = earthquakes_subset[features].copy()
# Fehlende Werte überprüfen und behandeln
print("Fehlende Werte vor der Vorverarbeitung:")
print(X.isnull().sum())
# Transformation-Pipeline erstellen
pca_pipeline = Pipeline([
('scaler', StandardScaler()), # Standardisierung (wichtig für PCA)
('pca', PCA(n_components=2)) # Reduktion auf 2 Dimensionen
])
# PCA anwenden
X_pca = pca_pipeline.fit_transform(X)
# PCA-Komponenten extrahieren
pca = pca_pipeline.named_steps['pca']
components = pca.components_
explained_variance = pca.explained_variance_ratio_
# Visualisierung der Ergebnisse
plt.figure(figsize=(14, 6))
# Scatterplot der ersten zwei PCA-Komponenten
plt.subplot(1, 2, 1)
scatter = plt.scatter(
X_pca[:, 0],
X_pca[:, 1],
c=earthquakes_subset['Magnitude'],
cmap='viridis',
alpha=0.5,
edgecolor='none'
)
plt.colorbar(scatter, label='Magnitude')
plt.xlabel(f'Hauptkomponente 1 ({explained_variance[0]:.1%} Varianz)')
plt.ylabel(f'Hauptkomponente 2 ({explained_variance[1]:.1%} Varianz)')
plt.title('PCA der Erdbebendaten')
plt.grid(True, alpha=0.3)
# Visualisierung der Feature-Wichtigkeit (Loadings) in den ersten beiden Komponenten
plt.subplot(1, 2, 2)
loading_vectors = np.transpose(components)
n_features = len(features)
# Begrenzungen für den Plot
plt.xlim(-1, 1)
plt.ylim(-1, 1)
# Einheitskreis zeichnen
circle = plt.Circle((0, 0), 1, fill=False, color='gray', linestyle='--')
plt.gca().add_patch(circle)
# Loading-Vektoren zeichnen
for i, feature in enumerate(features):
plt.arrow(0, 0, loading_vectors[i, 0], loading_vectors[i, 1],
head_width=0.05, head_length=0.05, fc='blue', ec='blue')
plt.text(loading_vectors[i, 0] * 1.15, loading_vectors[i, 1] * 1.15,
feature, color='blue', ha='center', va='center')
plt.xlabel('Hauptkomponente 1')
plt.ylabel('Hauptkomponente 2')
plt.title('Feature-Loadings der ersten zwei Hauptkomponenten')
plt.grid(True, alpha=0.3)
plt.axhline(y=0, color='k', linestyle='-', alpha=0.3)
plt.axvline(x=0, color='k', linestyle='-', alpha=0.3)
plt.tight_layout()
plt.show()
# Einfache Clusteranalyse basierend auf den PCA-Ergebnissen
# Verwende KMeans für automatisches Clustering
n_clusters = 15 # Anzahl der Cluster
kmeans = KMeans(n_clusters=n_clusters, n_init='auto', random_state=42)
cluster_labels = kmeans.fit_predict(X_pca)
# Visualisierung der Cluster
plt.figure(figsize=(14, 10))
# 1. PCA-Raum mit Clustern
plt.subplot(2, 2, 1)
for i in range(n_clusters):
mask = cluster_labels == i
plt.scatter(
X_pca[mask, 0],
X_pca[mask, 1],
label=f'Cluster {i+1}',
alpha=0.7,
edgecolor='none'
)
plt.xlabel(f'Hauptkomponente 1 ({explained_variance[0]:.1%} Varianz)')
plt.ylabel(f'Hauptkomponente 2 ({explained_variance[1]:.1%} Varianz)')
plt.title('Clustering der PCA-Ergebnisse')
plt.legend()
plt.grid(True, alpha=0.3)
# 2. Geografische Verteilung der Cluster
plt.subplot(2, 2, 2)
for i in range(n_clusters):
mask = cluster_labels == i
plt.scatter(
earthquakes_subset[mask]['Longitude'],
earthquakes_subset[mask]['Latitude'],
label=f'Cluster {i+1}',
alpha=0.3,
edgecolor='none'
)
plt.xlabel('Längengrad')
plt.ylabel('Breitengrad')
plt.title('Geografische Verteilung der Cluster')
plt.legend()
plt.grid(True, alpha=0.3)
# 3. Tiefe vs. Magnitude nach Cluster
plt.subplot(2, 2, 3)
for i in range(n_clusters):
mask = cluster_labels == i
plt.scatter(
earthquakes_subset[mask]['Depth'],
earthquakes_subset[mask]['Magnitude'],
label=f'Cluster {i+1}',
alpha=0.5,
edgecolor='none'
)
plt.xlabel('Tiefe (km)')
plt.ylabel('Magnitude')
plt.title('Tiefe vs. Magnitude nach Cluster')
plt.legend()
plt.grid(True, alpha=0.3)
# 4. Statistische Zusammenfassung der Cluster
plt.subplot(2, 2, 4)
earthquakes_subset['Cluster'] = cluster_labels
cluster_stats = earthquakes_subset.groupby('Cluster').agg({
'Magnitude': ['mean', 'min', 'max', 'count'],
'Depth': ['mean', 'min', 'max']
})
# Anzeigen der Statistiken als Text
plt.axis('off')
plt.text(0.05, 0.95, 'Cluster-Statistiken:', fontsize=12, fontweight='bold')
y_pos = 0.85
for i in range(n_clusters):
stats = cluster_stats.loc[i]
text = (
f"Cluster {i+1} (n={stats[('Magnitude', 'count')]})\n"
f" Magnitude: Ø {stats[('Magnitude', 'mean')]:.2f} "
f"(Min: {stats[('Magnitude', 'min')]:.1f}, Max: {stats[('Magnitude', 'max')]:.1f})\n"
f" Tiefe: Ø {stats[('Depth', 'mean')]:.2f} km "
f"(Min: {stats[('Depth', 'min')]:.1f}, Max: {stats[('Depth', 'max')]:.1f})"
)
plt.text(0.05, y_pos, text, fontsize=10, va='top')
y_pos -= 0.25
# plt.tight_layout()
plt.show()
# Interpretation der PCA-Ergebnisse
print("Interpretation der PCA-Ergebnisse:")
print("---------------------------------")
print(f"Die erste Hauptkomponente erklärt {explained_variance[0]:.1%} der Varianz.")
print(f"Die zweite Hauptkomponente erklärt {explained_variance[1]:.1%} der Varianz.")
print(f"Insgesamt werden {explained_variance[0] + explained_variance[1]:.1%} der Varianz durch die ersten beiden Komponenten erklärt.")
print("\nBeiträge der Variablen zu den Hauptkomponenten:")
for i, feature in enumerate(features):
print(f"{feature}: PC1 = {loading_vectors[i, 0]:.3f}, PC2 = {loading_vectors[i, 1]:.3f}")
print("\nCluster-Charakteristiken:")
for i in range(n_clusters):
stats = cluster_stats.loc[i]
print(f"Cluster {i+1} (n={stats[('Magnitude', 'count')]})")
print(f" Magnitude: Durchschnitt = {stats[('Magnitude', 'mean')]:.2f}, Bereich = [{stats[('Magnitude', 'min')]:.1f}, {stats[('Magnitude', 'max')]:.1f}]")
print(f" Tiefe: Durchschnitt = {stats[('Depth', 'mean')]:.2f} km, Bereich = [{stats[('Depth', 'min')]:.1f}, {stats[('Depth', 'max')]:.1f}] km")
Fehlende Werte vor der Vorverarbeitung: Latitude 0 Longitude 0 Depth 0 Magnitude 0 dtype: int64


Interpretation der PCA-Ergebnisse: --------------------------------- Die erste Hauptkomponente erklärt 31.6% der Varianz. Die zweite Hauptkomponente erklärt 25.5% der Varianz. Insgesamt werden 57.1% der Varianz durch die ersten beiden Komponenten erklärt. Beiträge der Variablen zu den Hauptkomponenten: Latitude: PC1 = -0.639, PC2 = 0.060 Longitude: PC1 = -0.644, PC2 = 0.062 Depth: PC1 = 0.394, PC2 = 0.512 Magnitude: PC1 = -0.144, PC2 = 0.855 Cluster-Charakteristiken: Cluster 1 (n=1662.0) Magnitude: Durchschnitt = 6.31, Bereich = [5.5, 6.8] Tiefe: Durchschnitt = 77.92 km, Bereich = [0.0, 580.2] km Cluster 2 (n=1820.0) Magnitude: Durchschnitt = 5.61, Bereich = [5.5, 5.9] Tiefe: Durchschnitt = 25.71 km, Bereich = [1.3, 164.0] km Cluster 3 (n=4645.0) Magnitude: Durchschnitt = 5.59, Bereich = [5.5, 5.8] Tiefe: Durchschnitt = 34.82 km, Bereich = [-1.1, 158.6] km Cluster 4 (n=791.0) Magnitude: Durchschnitt = 6.19, Bereich = [5.5, 6.7] Tiefe: Durchschnitt = 109.30 km, Bereich = [5.0, 569.4] km Cluster 5 (n=343.0) Magnitude: Durchschnitt = 7.46, Bereich = [6.1, 9.1] Tiefe: Durchschnitt = 131.77 km, Bereich = [4.9, 656.2] km Cluster 6 (n=2420.0) Magnitude: Durchschnitt = 5.64, Bereich = [5.5, 5.9] Tiefe: Durchschnitt = 31.45 km, Bereich = [0.0, 171.8] km Cluster 7 (n=2177.0) Magnitude: Durchschnitt = 5.58, Bereich = [5.5, 5.9] Tiefe: Durchschnitt = 33.14 km, Bereich = [0.0, 182.5] km Cluster 8 (n=924.0) Magnitude: Durchschnitt = 6.82, Bereich = [5.8, 7.3] Tiefe: Durchschnitt = 60.93 km, Bereich = [-0.1, 576.4] km Cluster 9 (n=574.0) Magnitude: Durchschnitt = 6.26, Bereich = [5.5, 7.4] Tiefe: Durchschnitt = 339.10 km, Bereich = [4.0, 691.6] km Cluster 10 (n=1307.0) Magnitude: Durchschnitt = 6.15, Bereich = [5.6, 6.7] Tiefe: Durchschnitt = 32.58 km, Bereich = [0.0, 285.0] km Cluster 11 (n=582.0) Magnitude: Durchschnitt = 5.78, Bereich = [5.5, 6.5] Tiefe: Durchschnitt = 548.10 km, Bereich = [215.3, 700.0] km Cluster 12 (n=189.0) Magnitude: Durchschnitt = 6.96, Bereich = [6.1, 8.8] Tiefe: Durchschnitt = 479.00 km, Bereich = [6.0, 677.4] km Cluster 13 (n=1592.0) Magnitude: Durchschnitt = 5.89, Bereich = [5.5, 6.4] Tiefe: Durchschnitt = 74.16 km, Bereich = [1.0, 344.3] km Cluster 14 (n=1536.0) Magnitude: Durchschnitt = 5.85, Bereich = [5.5, 6.2] Tiefe: Durchschnitt = 62.25 km, Bereich = [0.0, 379.6] km Cluster 15 (n=2850.0) Magnitude: Durchschnitt = 5.91, Bereich = [5.5, 6.2] Tiefe: Durchschnitt = 42.86 km, Bereich = [1.0, 284.8] km
Die PCA ist hier vielleicht etwas künstlich, aber man sieht tatsächlich auch, wie sich bestimmte Cluster (also in der PCA ähnliche Datenpunkte) in der geographischen Verteilung in lokalisierter Form wiederfinden. Man kann Daten zwar auch immer ohne vorherige PCA clustern, aber zugrundeliegende oder versteckte Strukturen findet man so eher selten.
5.4.4 Vergleich der globalen Verteilung von Atombombentests¶
Bisher haben wir uns ausschließlich mit natürlichen Erdbeben beschäftigt. Unser Datensatz enthält jedoch auch Aufzeichnungen von anthropogenen seismischen Ereignissen, insbesondere Kernwaffentests. Diese Tests erzeugen seismische Wellen, die von den globalen Messstationen ähnlich wie natürliche Erdbeben erfasst werden.
Die Analyse der Verteilung dieser Tests kann interessante historische und geopolitische Einblicke liefern. Wir werden die Daten filtern, um nur Ereignisse vom Typ “Nuclear Explosion” zu betrachten, und deren geografische Verteilung visualisieren.
Es ist wichtig zu beachten, dass unser Datensatz möglicherweise nicht alle historischen Kernwaffentests enthält, da einige Tests möglicherweise nicht als solche identifiziert oder aufgezeichnet wurden, insbesondere in den frühen Jahren der Kernwaffenentwicklung.
# Import der benötigten Bibliotheken
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.cm as cm
from matplotlib.colors import Normalize
import matplotlib.dates as mdates
# Stil für bessere Ästhetik
sns.set_theme(style="whitegrid")
# Filtern der Daten nach Ereignistyp "Nuclear Explosion"
nuclear_tests = earthquakes_subset[earthquakes_subset['Type'] == 'Nuclear Explosion'].copy()
# Überprüfung, wie viele Nukleartests in den Daten enthalten sind
print(f"Anzahl der Nukleartests im Datensatz: {len(nuclear_tests)}")
# Übersicht über die Verteilung der Magnitude und Tiefe
if len(nuclear_tests) > 0:
print("\nStatistiken der Nukleartests:")
print(f"Magnitude: Min = {nuclear_tests['Magnitude'].min():.1f}, Max = {nuclear_tests['Magnitude'].max():.1f}, Durchschnitt = {nuclear_tests['Magnitude'].mean():.2f}")
print(f"Tiefe: Min = {nuclear_tests['Depth'].min():.1f} km, Max = {nuclear_tests['Depth'].max():.1f} km, Durchschnitt = {nuclear_tests['Depth'].mean():.2f} km")
# Einige Beispieldaten anzeigen
print("\nBeispieldaten von Nukleartests:")
display(nuclear_tests[['Date_Time', 'Latitude', 'Longitude', 'Depth', 'Magnitude']].head())
# Geografische Verteilung der Nukleartests visualisieren
plt.figure(figsize=(14, 8))
# Hintergrund für Meer
plt.axhspan(-90, 90, facecolor='lightblue', alpha=0.3, zorder=0)
# Scatterplot der Nukleartests
scatter = plt.scatter(
nuclear_tests['Longitude'],
nuclear_tests['Latitude'],
c=nuclear_tests['Magnitude'],
s=nuclear_tests['Magnitude']**2 * 2, # Größe basierend auf Magnitude
cmap='YlOrRd',
alpha=0.7,
edgecolor='k',
linewidth=0.5,
zorder=2
)
# Colorbar für die Magnitude
cbar = plt.colorbar(scatter, label='Magnitude')
# Grenzen und Beschriftungen
plt.xlim(-180, 180)
plt.ylim(-90, 90)
plt.xlabel('Längengrad', fontsize=12)
plt.ylabel('Breitengrad', fontsize=12)
plt.title('Geografische Verteilung von Nukleartests', fontsize=14)
plt.grid(True, alpha=0.3, zorder=1)
# Anmerkung zur Gesamtzahl der Tests
plt.annotate(f'Gesamtzahl der Nukleartests: {len(nuclear_tests)}',
xy=(0.02, 0.02), xycoords='figure fraction', fontsize=12,
bbox=dict(facecolor='white', alpha=0.7))
plt.tight_layout()
plt.show()
# Analyse der zeitlichen Verteilung der Nukleartests
if 'Date_Time' in nuclear_tests.columns:
plt.figure(figsize=(14, 6))
# Zählen der Tests pro Jahr
nuclear_tests['Year'] = nuclear_tests['Date_Time'].dt.year
tests_per_year = nuclear_tests.groupby('Year').size()
# Balkendiagramm der Tests pro Jahr
plt.bar(tests_per_year.index, tests_per_year.values, alpha=0.7)
plt.xlabel('Jahr', fontsize=12)
plt.ylabel('Anzahl der Nukleartests', fontsize=12)
plt.title('Zeitliche Verteilung von Nukleartests', fontsize=14)
plt.grid(True, alpha=0.3, axis='y')
# Beschriftung für wichtige historische Ereignisse
historic_events = {
1963: 'Teilweiser Teststoppvertrag',
1996: 'Umfassender Teststoppvertrag'
}
for year, event in historic_events.items():
if year >= tests_per_year.index.min() and year <= tests_per_year.index.max():
plt.axvline(x=year, color='r', linestyle='--', alpha=0.7)
plt.text(year, tests_per_year.max() * 0.9, event, rotation=90,
ha='right', va='top', fontsize=10, color='r')
plt.tight_layout()
plt.show()
# Analyse der Tests nach Ländern/Regionen (basierend auf geografischen Clustern)
# Definieren bekannter Testgebiete mit ungefähren Koordinaten
test_sites = {
'USA (Nevada)': {'lat_range': (35, 40), 'lon_range': (-117, -112)},
'UdSSR/Russland (Semipalatinsk)': {'lat_range': (47, 52), 'lon_range': (77, 82)},
'UdSSR/Russland (Nowaja Semlja)': {'lat_range': (70, 77), 'lon_range': (50, 60)},
'China (Lop Nor)': {'lat_range': (40, 43), 'lon_range': (87, 90)},
'Frankreich (Pazifik)': {'lat_range': (-25, -20), 'lon_range': (-145, -135)},
'Indien': {'lat_range': (25, 30), 'lon_range': (70, 75)},
'Pakistan': {'lat_range': (25, 30), 'lon_range': (63, 68)},
'Nordkorea': {'lat_range': (40, 42), 'lon_range': (128, 130)}
}
# Tests nach Standorten zählen
site_counts = {}
for site_name, coords in test_sites.items():
mask = ((nuclear_tests['Latitude'] >= coords['lat_range'][0]) &
(nuclear_tests['Latitude'] <= coords['lat_range'][1]) &
(nuclear_tests['Longitude'] >= coords['lon_range'][0]) &
(nuclear_tests['Longitude'] <= coords['lon_range'][1]))
site_counts[site_name] = mask.sum()
# Andere Tests (nicht in bekannten Testgebieten)
total_matched = sum(site_counts.values())
if total_matched < len(nuclear_tests):
site_counts['Andere/Unbekannt'] = len(nuclear_tests) - total_matched
# Balkendiagramm der Tests nach Standort
plt.figure(figsize=(14, 6))
sites = list(site_counts.keys())
counts = list(site_counts.values())
# Sortieren nach Anzahl der Tests (absteigend)
sorted_indices = np.argsort(counts)[::-1]
sorted_sites = [sites[i] for i in sorted_indices]
sorted_counts = [counts[i] for i in sorted_indices]
plt.bar(sorted_sites, sorted_counts, alpha=0.7)
plt.xlabel('Testgebiet', fontsize=12)
plt.ylabel('Anzahl der Nukleartests', fontsize=12)
plt.title('Verteilung von Nukleartests nach Testgebieten', fontsize=14)
plt.xticks(rotation=45, ha='right')
plt.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()
else:
print("Keine Nukleartests im Datensatz gefunden.")
Anzahl der Nukleartests im Datensatz: 175 Statistiken der Nukleartests: Magnitude: Min = 5.5, Max = 6.9, Durchschnitt = 5.85 Tiefe: Min = 0.0 km, Max = 33.0 km, Durchschnitt = 0.30 km Beispieldaten von Nukleartests:
Date_Time | Latitude | Longitude | Depth | Magnitude | |
---|---|---|---|---|---|
565 | 1966-12-20 15:30:01 | 37.302167 | -116.408333 | 1.2 | 5.62 |
897 | 1968-04-26 15:00:02 | 37.295333 | -116.455667 | 1.2 | 5.63 |
1129 | 1968-12-19 16:30:01 | 37.231500 | -116.473667 | 1.4 | 5.52 |
1380 | 1969-09-16 14:30:01 | 37.314167 | -116.460667 | 1.2 | 5.82 |
1532 | 1970-03-26 19:00:01 | 37.300500 | -116.534167 | 1.2 | 5.54 |



Diese Daten sind, wie schon gesagt, unvollständig, aber der hier gezeigte Ansatz ist grundsätzlich interessant: Oft hat man die Möglichkeit, einfach bestimmte Ereignisse herausfiltern und nur deren Details untersuchen.
5.5 Übungsaufgabe¶
Als Übungsaufgabe möchte ich Sie zu einer Untersuchung des folgenden Datensatzes einladen: World Happiness Index and Inflation Dataset, wieder von der Kaggle-Plattform. Hier finden Sie die folgenden Daten (übernommen von der Kaggle-Beschreibung des Datensatzes):
- Country: The name of the country where the data was recorded
- Year: The year in which the data was collected.
- Continent/Region: The continent or region in which the country is located.
- Score: The World Happiness Index score, where higher values indicate greater happiness.
- Headline Consumer Price Inflation: The overall inflation rate based on the Consumer Price Index.
- Energy Consumer Price Inflation: The inflation rate of energy products based on the Consumer Price Index.
- Food Consumer Price Inflation: The inflation rate of food products based on the Consumer Price Index.
- Official Core Consumer Price Inflation: The core inflation rate excluding energy and food products.
- Producer Price Inflation: The inflation rate based on the Producer Price Index.
- GDP deflator Index growth rate: The growth rate of the GDP deflator, which measures price changes in the economy.
Nehmen Sie diese Daten und toben Sie sich aus, äh, finden Sie interessante Zusammenhänge oder zumindest Hinweise darauf über mögliche Korrelationen oder eigene Erkenntnisse aus Ihrer kreativen Visualisierung. Ihrer Fantasie sind keine Grenzen gesetzt.
# Pfad zur CSV-Datei
file_path = os.path.join('data','WHI_Inflation.csv') # Pfad anpassen, falls nötig
# Einlesen der Daten mit Pandas
whi_data = pd.read_csv(file_path)
# Was steht in der Datei?
print(whi_data.head())
Country Year Headline Consumer Price Inflation \ 0 Afghanistan 2015 -0.660 1 Afghanistan 2016 4.380 2 Afghanistan 2017 4.976 3 Afghanistan 2018 0.630 4 Afghanistan 2019 2.302 Energy Consumer Price Inflation Food Consumer Price Inflation \ 0 -4.250000 -0.840000 1 2.070000 5.670000 2 4.440000 6.940000 3 1.474185 -1.045952 4 -2.494359 3.794770 Official Core Consumer Price Inflation Producer Price Inflation \ 0 0.219999 NaN 1 5.192760 NaN 2 5.423228 NaN 3 -0.126033 NaN 4 NaN NaN GDP deflator Index growth rate Continent/Region Score GDP per Capita \ 0 2.665090 South Asia 3.575 0.319820 1 -2.409509 South Asia 3.360 0.382270 2 2.404000 South Asia 3.794 0.401477 3 2.071208 South Asia 3.632 0.332000 4 6.520928 South Asia 3.203 0.350000 Social support Healthy life expectancy at birth \ 0 0.302850 0.303350 1 0.110370 0.173440 2 0.581543 0.180747 3 0.537000 0.255000 4 0.517000 0.361000 Freedom to make life choices Generosity Perceptions of corruption 0 0.23414 0.365100 0.097190 1 0.16430 0.312680 0.071120 2 0.10618 0.311871 0.061158 3 0.08500 0.191000 0.036000 4 0.00000 0.158000 0.025000
5.6 Zusammenfassung der wichtigsten Punkte dieser Einheit¶
Wir haben in dieser Einheit einige Techniken im Umgang mit Daten gesehen. Insbesondere haben wir festgestellt, wie wichtig die anfängliche Kontrolle, Bereinigung und Aufbereitung der Daten vor der weiteren Analyse ist. Für die Auswertung selbst ist natürlich die Visualisierung sehr interessant, aber nicht nur.
Datenaufbereitung und -analyse: Einlesen von CSV-Dateien, Erkennen und Behandeln fehlender Werte, statistische Grundauswertungen mit Pandas und NumPy
Geografische Visualisierung: Darstellung der globalen Erdbebenverteilung im 2D- und 3D-Raum, Visualisierung seismischer Aktivität in verschiedenen tektonischen Regionen
Zeitliche Visualisierung: Erstellung von 2D- und 3D-Animationen zur Visualisierung der Erdbebentätigkeit über mehrere Jahrzehnte
Korrelationsanalyse: Untersuchung des Zusammenhangs zwischen Teilen der Daten, z.B. Erdbebenparametern wie Magnitude und Tiefe, mit statistischen Methoden
Potenzgesetze in Daten: Nachweis und Visualisierung vermuteter Abhängigkeiten in den Daten, z.B. des Gutenberg-Richter-Gesetzes (inverses Potenzgesetz der Erdbebenhäufigkeit)
Dimensionsreduktion: Anwendung der Hauptkomponentenanalyse (PCA) als typisches Beispiel für die Vereinfachung mehrdimensionaler Daten und die nachfolgende Erkennung von Mustern
Vergleichende Analyse: Untersuchung verschiedener Aspekte von Datensegmenten, wie z.B. der Vergleich natürlicher vs. anthropogener seismischer Ereignisse (Erdbeben vs. Nukleartests)
Datenvisualisierungstechniken: Einsatz verschiedener Diagrammtypen für unterschiedliche Aspekte der Datenanalyse (Streudiagramme, Heatmaps, 3D-Plots, Animationen)