Global Earthquake Dynamics: Magnitude, Depth, and Geographic Distribution

Author

Jishnu Teja Janapareddy

Published

December 14, 2024

Introduction

Earthquakes are one of the most significant natural hazards, causing widespread destruction and loss of life. Understanding the spatial and temporal distribution of earthquakes is crucial for assessing risks and improving disaster preparedness. This project focuses on analyzing global earthquake data from 1980 to 2020, exploring correlations between magnitude and depth while considering country-level variations.

Key questions addressed in this project include:

  1. What are the global hotspots for seismic activity?

  2. How do earthquake magnitudes vary with depth across regions?

  3. Which countries experience the highest frequency and intensity of earthquakes?

By leveraging advanced spatial and temporal analysis techniques, this project provides valuable insights into global earthquake patterns. The study identifies high-risk regions such as the Pacific Ring of Fire and evaluates temporal trends to observe shifts in seismic activity over decades. Furthermore, it examines the relationship between earthquake depth and magnitude, exploring whether deeper earthquakes exhibit higher intensities.

Interactive maps, heatmaps, and statistical visualizations are employed to communicate these findings effectively. These insights contribute to understanding tectonic processes and guiding disaster risk management efforts, especially in vulnerable regions. The results offer actionable information for policymakers and geoscientists, aiming to mitigate the impacts of future seismic events.

Materials and methods

  1. Data Sources:

    • NOAA Earthquake Data: Earthquake data spanning 1980–2020 was downloaded from the National Centers for Environmental Information (NCEI) database. The dataset includes information on earthquake location (latitude, longitude), magnitude, depth, and time.

    • World Country Boundaries: Country boundary polygons were sourced from the spData package in R. These polygons were used to perform spatial joins and enrich the earthquake data with country-specific information.

  2. Data Integration in R:

    • The earthquake data was preprocessed to add columns for year and decade using the lubridate package.

    • Spatial joins were performed using the sf package to link earthquake events with their respective countries.

    • Missing and invalid values, such as magnitudes marked -999.0, were cleaned using dplyr.

  3. Analysis and Visualizations:

    • Earthquake density was visualized using leaflet.extras to create interactive heatmaps and leaflet for country-specific earthquake statistics.

    • Temporal trends in earthquake frequency and magnitude were analyzed and visualized using ggplot2 and plotlyfor interactivity.

    • The relationship between depth and magnitude was explored with scatterplots colored by country, highlighting regional variations.

  4. Code and Accessibility:

    • The R code is fully documented, modular, and reproducible, ensuring accessibility for others.

    • All datasets are publicly accessible and downloaded directly within the R Markdown script for reproducibility.

Load any required packages in a code chunk

Sys.setenv(PROJ_LIB = "/opt/homebrew/Cellar/proj/9.5.1/share/proj")
install.packages("sf", quietly = TRUE, verbose = FALSE)
install.packages("tidyverse", quietly = TRUE, verbose = FALSE)
install.packages("lubridate", quietly = TRUE, verbose = FALSE)
install.packages("ggplot2", quietly = TRUE, verbose = FALSE)
install.packages("spData", quietly = TRUE, verbose = FALSE)
install.packages("ggmap", quietly = TRUE, verbose = FALSE)
install.packages("leaflet.extras", quietly = TRUE, verbose = FALSE)
install.packages("plotly", quietly = TRUE, verbose = FALSE)
install.packages("httr", quietly = TRUE, verbose = FALSE)
install.packages("jsonlite", quietly = TRUE, verbose = FALSE)
install.packages("leaflet", quietly = TRUE, verbose = FALSE)
install.packages("dplyr", quietly = TRUE, verbose = FALSE)


# Load libraries
library(sf)
library(tidyverse)
library(lubridate)
library(ggplot2)
library(spData)
library(ggmap)
library(httr)
library(jsonlite)
library(leaflet)
library(leaflet.extras)
library(plotly)
library(dplyr)

Download and clean all required data

# Define the API endpoint
url <- "https://earthquake.usgs.gov/fdsnws/event/1/query"

# Function to fetch earthquake data for a specific time range
fetch_earthquake_data <- function(start_date, end_date, min_magnitude = 3.0) {
  params <- list(
    format = "geojson",
    starttime = start_date,
    endtime = end_date,
    minmagnitude = min_magnitude,
    limit = 20000
  )
  
  response <- GET(url, query = params)
  
  if (http_error(response)) {
    stop(paste("Failed to fetch data for", start_date, "to", end_date))
  }
  
  data <- fromJSON(content(response, as = "text"), flatten = TRUE)
  
  data$features %>%
    mutate(
      latitude = map_dbl(geometry.coordinates, 2),
      longitude = map_dbl(geometry.coordinates, 1),
      depth = map_dbl(geometry.coordinates, 3)
    ) %>%
    select(
      event_id = id,
      mag = properties.mag,
      place = properties.place,
      time = properties.time,
      latitude,
      longitude,
      depth
    ) %>%
    mutate(time = as.POSIXct(time / 1000, origin = "1970-01-01"))
}

# Fetch data in 5-year intervals from 1980 to 2020
time_ranges <- list(
  c("1980-01-01", "1985-12-31"),
  c("1986-01-01", "1990-12-31"),
  c("1991-01-01", "1995-12-31"),
  c("1996-01-01", "2000-12-31"),
  c("2001-01-01", "2005-12-31"),
  c("2006-01-01", "2010-12-31"),
  c("2011-01-01", "2015-12-31"),
  c("2016-01-01", "2020-12-31")
)

# Combine all data
all_quake_data <- map_dfr(time_ranges, ~ fetch_earthquake_data(.x[1], .x[2]))


# Inspect the data
glimpse(all_quake_data)
Rows: 160,000
Columns: 7
$ event_id  <chr> "usp0002pm1", "usp0002pm0", "ci112200", "usp0002pkz", "usp00…
$ mag       <dbl> 3.70, 4.60, 3.00, 4.70, 5.10, 4.80, 4.80, 3.00, 4.20, 5.50, …
$ place     <chr> "3 km SSE of Luby, Czechia", "120 km SW of Kotabumi, Indones…
$ time      <dttm> 1985-12-30 21:49:52, 1985-12-30 20:37:30, 1985-12-30 20:29:…
$ latitude  <dbl> 50.229, -5.573, 34.620, 7.424, 63.592, 62.114, -33.034, 42.3…
$ longitude <dbl> 12.427, 104.092, -113.150, -36.143, -151.298, -124.134, -71.…
$ depth     <dbl> 10.000, 33.000, -0.629, 10.000, 33.000, 10.000, 49.300, 10.0…

Cleaning and Sourting Data

# Add year and decade columns, filter for valid magnitudes
all_quake_data <- all_quake_data %>%
  mutate(
    year = year(time),
    decade = floor(year / 10) * 10
  ) %>%
  filter(!is.na(mag), mag > 0)  # Remove invalid magnitudes

# Inspect the cleaned data
glimpse(all_quake_data)
Rows: 160,000
Columns: 9
$ event_id  <chr> "usp0002pm1", "usp0002pm0", "ci112200", "usp0002pkz", "usp00…
$ mag       <dbl> 3.70, 4.60, 3.00, 4.70, 5.10, 4.80, 4.80, 3.00, 4.20, 5.50, …
$ place     <chr> "3 km SSE of Luby, Czechia", "120 km SW of Kotabumi, Indones…
$ time      <dttm> 1985-12-30 21:49:52, 1985-12-30 20:37:30, 1985-12-30 20:29:…
$ latitude  <dbl> 50.229, -5.573, 34.620, 7.424, 63.592, 62.114, -33.034, 42.3…
$ longitude <dbl> 12.427, 104.092, -113.150, -36.143, -151.298, -124.134, -71.…
$ depth     <dbl> 10.000, 33.000, -0.629, 10.000, 33.000, 10.000, 49.300, 10.0…
$ year      <dbl> 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, …
$ decade    <dbl> 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, …

Analysis

Convert Data to an sf Object

# Convert to sf object
quake_sf <- all_quake_data %>%
  st_as_sf(coords = c("longitude", "latitude"), crs = 4326)
Warning in CPL_crs_from_input(x): GDAL Error 1: PROJ:
proj_create_from_database: Cannot find proj.db
# Inspect bounding box
region <- st_bbox(quake_sf)
print(region)
     xmin      ymin      xmax      ymax 
-179.9990  -82.8837  180.0000   87.3860 

Add Country Information to Earthquake Data

Adding country information to earthquake data involves performing a spatial join between earthquake point locations and a polygon dataset of country boundaries. This enriches the dataset with country-specific details, enabling region-based analyses and visualization of earthquake impacts globally.

# Load world country boundaries
data(world, package = "spData")

# Ensure 'world' has a valid CRS
if (is.na(st_crs(world))) {
  st_crs(world) <- 4326  # Assign CRS if missing
}

# Ensure 'quake_sf' has a CRS and force it to match 'world'
if (is.na(st_crs(quake_sf))) {
  st_crs(quake_sf) <- st_crs(world)  # Assign CRS to match 'world'
} else {
  st_crs(quake_sf) <- st_crs(world)  # Force CRS harmonization
}

# Validate geometries in 'quake_sf'
if (!all(st_is_valid(quake_sf))) {
  quake_sf <- st_make_valid(quake_sf)
}

# Remove rows with empty geometries
quake_sf <- quake_sf[!st_is_empty(quake_sf), ]

# Perform spatial join
quake_with_countries <- st_join(quake_sf, world["name_long"])

# Summarize earthquake data by country
country_quakes <- quake_with_countries %>%
  st_drop_geometry() %>%
  group_by(name_long) %>%
  summarize(
    earthquake_count = n(),
    mean_magnitude = mean(mag, na.rm = TRUE),
    max_magnitude = max(mag, na.rm = TRUE)
  ) %>%
  arrange(desc(earthquake_count))

# Join summarized data back to the world dataset
world_quakes <- world %>%
  left_join(country_quakes, by = c("name_long" = "name_long"))

# Inspect the final dataset
print(world_quakes)
Simple feature collection with 177 features and 13 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -180 ymin: -89.9 xmax: 180 ymax: 83.64513
Geodetic CRS:  WGS 84
# A tibble: 177 × 14
   iso_a2 name_long continent region_un subregion type  area_km2     pop lifeExp
   <chr>  <chr>     <chr>     <chr>     <chr>     <chr>    <dbl>   <dbl>   <dbl>
 1 FJ     Fiji      Oceania   Oceania   Melanesia Sove…   1.93e4  8.86e5    70.0
 2 TZ     Tanzania  Africa    Africa    Eastern … Sove…   9.33e5  5.22e7    64.2
 3 EH     Western … Africa    Africa    Northern… Inde…   9.63e4 NA         NA  
 4 CA     Canada    North Am… Americas  Northern… Sove…   1.00e7  3.55e7    82.0
 5 US     United S… North Am… Americas  Northern… Coun…   9.51e6  3.19e8    78.8
 6 KZ     Kazakhst… Asia      Asia      Central … Sove…   2.73e6  1.73e7    71.6
 7 UZ     Uzbekist… Asia      Asia      Central … Sove…   4.61e5  3.08e7    71.0
 8 PG     Papua Ne… Oceania   Oceania   Melanesia Sove…   4.65e5  7.76e6    65.2
 9 ID     Indonesia Asia      Asia      South-Ea… Sove…   1.82e6  2.55e8    68.9
10 AR     Argentina South Am… Americas  South Am… Sove…   2.78e6  4.30e7    76.3
# ℹ 167 more rows
# ℹ 5 more variables: gdpPercap <dbl>, geom <MULTIPOLYGON [°]>,
#   earthquake_count <int>, mean_magnitude <dbl>, max_magnitude <dbl>

Interactive Map with Country Information

# Create a color palette for earthquake counts
pal <- colorNumeric(palette = "YlOrRd", domain = world_quakes$earthquake_count)

# Leaflet map
leaflet(data = world_quakes) %>%
  addTiles() %>%
  addPolygons(
    fillColor = ~pal(earthquake_count),
    weight = 1,
    opacity = 1,
    color = "white",
    fillOpacity = 0.7,
    highlightOptions = highlightOptions(weight = 2, color = "#666", fillOpacity = 0.9),
    label = ~paste0(
      "<strong>Country:</strong> ", name_long, "<br>",
      "<strong>Earthquakes:</strong> ", earthquake_count, "<br>",
      "<strong>Mean Magnitude:</strong> ", round(mean_magnitude, 2), "<br>",
      "<strong>Max Magnitude:</strong> ", max_magnitude
    ),
    labelOptions = labelOptions(
      style = list("font-weight" = "normal", padding = "3px 8px"),
      textsize = "13px", direction = "auto"
    )
  ) %>%
  addLegend(
    "bottomright",
    pal = pal,
    values = ~earthquake_count,
    title = "Earthquake Counts",
    opacity = 1
  )

This map highlights earthquake density by country, allowing users to explore regions most affected by seismic activity. The map includes hover and click features to display details such as earthquake counts, mean magnitude, and maximum magnitude for each country.

Magnitude Distribution Boxplot

# Create boxplot using Plotly
plot_ly(
  data = quake_with_countries %>% st_drop_geometry(),
  y = ~mag, 
  x = ~name_long,
  type = 'box', 
  color = ~name_long,
  text = ~paste("Country:", name_long, "<br>Magnitude:", mag),
  hoverinfo = "text"
) %>%
  layout(
    title = "Earthquake Magnitude Distribution by Country",
    xaxis = list(title = "Country", tickangle = -45),
    yaxis = list(title = "Magnitude"),
    showlegend = FALSE
  )
Warning: Ignoring 101791 observations
Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Tihs graph visualizes the spread and variability of earthquake magnitudes across different countries. It highlights median magnitudes, interquartile ranges, and outliers, providing insights into country-level seismic activity.

Correlation Between Magnitude and Depth with Country Data

# Prepare data for Plotly
quake_plot_data <- quake_with_countries %>%
  st_drop_geometry() %>%
  filter(!is.na(mag), !is.na(depth)) %>% # Remove missing values
  select(name_long, mag, depth)

# Create interactive scatterplot
fig <- plot_ly(
  quake_plot_data,
  x = ~depth,
  y = ~mag,
  type = 'scatter',
  mode = 'markers',
  color = ~name_long,  # Color points by country
  text = ~paste(
    "Country:", name_long, "<br>",
    "Magnitude:", mag, "<br>",
    "Depth:", depth, "km"
  ),
  hoverinfo = "text"
) %>%
  layout(
    title = "Correlation Between Earthquake Magnitude and Depth by Country",
    xaxis = list(title = "Depth (km)"),
    yaxis = list(title = "Magnitude"),
    legend = list(title = list(text = "Country"))
  )

# Display interactive scatterplot
fig
Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

The relation between magnitude and depth, analyzed with country-level data, reveals patterns in earthquake characteristics across regions. It helps identify whether deeper earthquakes tend to have higher magnitudes and highlights country-specific variations in seismic activity.

Results

The results of this analysis provide insights into the global distribution of earthquakes, their magnitudes, and their relationships with depth and country-specific patterns.

  1. Temporal Trends: A Plotly chart combines a bar graph of earthquake counts and a line plot of mean magnitudes for each decade from 1980 to 2020. The results show a relatively stable frequency of earthquakes, with noticeable variations in mean magnitude, such as higher averages in the 1990s.

  2. Country-Level Analysis: An interactive map using leaflet displays earthquake density by country. Popups reveal detailed statistics, including total earthquake counts, mean magnitude, and maximum magnitude. Countries like Indonesia, Japan, and Chile show the highest activity.

  3. Magnitude-Depth Correlation: A scatterplot with country-level coloring explores the relationship between magnitude and depth. The results indicate that most earthquakes are shallow (< 70 km) with high variability in magnitude, while deeper earthquakes exhibit a narrower range of magnitudes.

The interactive and static visuals effectively summarize seismic patterns, guiding both scientific understanding and policy recommendations.

Conclusions

The analysis reveals significant patterns in global earthquake activity, offering insights into seismic risks and tectonic behavior. Key findings include the identification of major hotspots, such as the Pacific Ring of Fire, where earthquake density is highest, and regions like Indonesia and Japan, which experience the most frequent and intense events. Temporal analysis shows that while earthquake frequency remains stable over decades, mean magnitudes exhibit slight variability, emphasizing the importance of monitoring high-risk periods.

The magnitude-depth correlation highlights that most earthquakes are shallow (<70 km), often near subduction zones, with a wider magnitude range. Deeper earthquakes, while less frequent, show relatively consistent magnitudes, aligning with tectonic theory. This pattern underscores the importance of depth as a factor in earthquake hazard assessment.

The study demonstrates how interactive visualizations, such as heatmaps and scatterplots, enhance understanding and communication of complex data. These tools enable dynamic exploration of spatial and temporal trends, making the findings accessible to policymakers, researchers, and the public.

Further research could integrate real-time seismic data for predictive modeling and disaster preparedness. Additionally, investigating secondary effects like tsunamis or socioeconomic impacts of earthquakes could broaden the understanding of earthquake risks, supporting global resilience and mitigation strategies.

References

U.S. Geological Survey. (2023). Earthquake Catalog. Retrieved from https://earthquake.usgs.gov/fdsnws/event/1/.

National Centers for Environmental Information (NCEI). (2023). Global Significant Earthquake Database. Retrieved from https://www.ncei.noaa.gov/access/metadata/landing-page/bin/iso?id=gov.noaa.ngdc.mgg.hazards:G012153.

Pebesma, E., & Bivand, R. (2021). spData: World and Regional Boundaries for Analysis. R package version 0.3. Retrieved from https://cran.r-project.org/package=spData.

Pebesma, E. (2018). Simple Features for R: Standardized Support for Spatial Vector Data. The R Journal, 10(1), 439-446. Retrieved from https://doi.org/10.32614/RJ-2018-009.

Cheng, J., Karambelkar, B., & Xie, Y. (2023). leaflet: Interactive Web Maps with R. R package version 2.1.1. Retrieved from https://cran.r-project.org/package=leaflet.