`## Synopsis

This analysis examines severe weather events across the United States using the NOAA Storm Database. It identifies the event types associated with the greatest population health impacts and economic consequences. Fatalities and injuries are used to measure harm to human health. Property damage and crop damage are converted into estimated dollar amounts and aggregated by event type. The ten highest-ranking event types are presented in tables and figures. The results provide an overview of the historical impacts of severe weather events. The analysis starts from the original compressed CSV file and performs all data processing in R. Differences in reporting practices, event classifications, and damage estimates may affect the results.

Data Processing

1. Download and load the raw data

The NOAA Storm Database contains records of severe weather events, including event types, fatalities, injuries, and estimates of property and crop damage. The original compressed CSV file is downloaded directly and read into R.

# Install ggplot2 if it is not already installed.
if (!requireNamespace("ggplot2", quietly = TRUE)) {
  install.packages("ggplot2")
}

library(ggplot2)

# URL of the original NOAA Storm Database file.
file_url <- paste0(
  "https://d396qusza40orc.cloudfront.net/",
  "repdata%2Fdata%2FStormData.csv.bz2"
)

data_file <- "StormData.csv.bz2"

# Download the raw file if necessary.
if (!file.exists(data_file)) {
  download.file(
    file_url,
    destfile = data_file,
    mode = "wb"
  )
}

# Read the compressed CSV.
storm <- read.csv(
  data_file,
  stringsAsFactors = FALSE
)

# Inspect the dimensions and variables.
dim(storm)
## [1] 902297     37
names(storm)
##  [1] "STATE__"    "BGN_DATE"   "BGN_TIME"   "TIME_ZONE"  "COUNTY"    
##  [6] "COUNTYNAME" "STATE"      "EVTYPE"     "BGN_RANGE"  "BGN_AZI"   
## [11] "BGN_LOCATI" "END_DATE"   "END_TIME"   "COUNTY_END" "COUNTYENDN"
## [16] "END_RANGE"  "END_AZI"    "END_LOCATI" "LENGTH"     "WIDTH"     
## [21] "F"          "MAG"        "FATALITIES" "INJURIES"   "PROPDMG"   
## [26] "PROPDMGEXP" "CROPDMG"    "CROPDMGEXP" "WFO"        "STATEOFFIC"
## [31] "ZONENAMES"  "LATITUDE"   "LONGITUDE"  "LATITUDE_E" "LONGITUDE_"
## [36] "REMARKS"    "REFNUM"
head(storm)
##   STATE__           BGN_DATE BGN_TIME TIME_ZONE COUNTY COUNTYNAME STATE  EVTYPE
## 1       1  4/18/1950 0:00:00     0130       CST     97     MOBILE    AL TORNADO
## 2       1  4/18/1950 0:00:00     0145       CST      3    BALDWIN    AL TORNADO
## 3       1  2/20/1951 0:00:00     1600       CST     57    FAYETTE    AL TORNADO
## 4       1   6/8/1951 0:00:00     0900       CST     89    MADISON    AL TORNADO
## 5       1 11/15/1951 0:00:00     1500       CST     43    CULLMAN    AL TORNADO
## 6       1 11/15/1951 0:00:00     2000       CST     77 LAUDERDALE    AL TORNADO
##   BGN_RANGE BGN_AZI BGN_LOCATI END_DATE END_TIME COUNTY_END COUNTYENDN
## 1         0                                               0         NA
## 2         0                                               0         NA
## 3         0                                               0         NA
## 4         0                                               0         NA
## 5         0                                               0         NA
## 6         0                                               0         NA
##   END_RANGE END_AZI END_LOCATI LENGTH WIDTH F MAG FATALITIES INJURIES PROPDMG
## 1         0                      14.0   100 3   0          0       15    25.0
## 2         0                       2.0   150 2   0          0        0     2.5
## 3         0                       0.1   123 2   0          0        2    25.0
## 4         0                       0.0   100 2   0          0        2     2.5
## 5         0                       0.0   150 2   0          0        2     2.5
## 6         0                       1.5   177 2   0          0        6     2.5
##   PROPDMGEXP CROPDMG CROPDMGEXP WFO STATEOFFIC ZONENAMES LATITUDE LONGITUDE
## 1          K       0                                         3040      8812
## 2          K       0                                         3042      8755
## 3          K       0                                         3340      8742
## 4          K       0                                         3458      8626
## 5          K       0                                         3412      8642
## 6          K       0                                         3450      8748
##   LATITUDE_E LONGITUDE_ REMARKS REFNUM
## 1       3051       8806              1
## 2          0          0              2
## 3          0          0              3
## 4          0          0              4
## 5          0          0              5
## 6          0          0              6

2. Select and clean the relevant variables

The analysis uses EVTYPE, FATALITIES, INJURIES, PROPDMG, PROPDMGEXP, CROPDMG, and CROPDMGEXP. Event names are converted to uppercase and surrounding whitespace is removed. Records without an event type are excluded. Missing health counts are treated as zero.

required_columns <- c(
  "EVTYPE",
  "FATALITIES",
  "INJURIES",
  "PROPDMG",
  "PROPDMGEXP",
  "CROPDMG",
  "CROPDMGEXP"
)

stopifnot(all(required_columns %in% names(storm)))

storm <- storm[, required_columns]

# Standardize capitalization and whitespace.
storm$EVTYPE <- toupper(trimws(storm$EVTYPE))

# Remove missing or empty event types.
storm <- storm[
  !is.na(storm$EVTYPE) & storm$EVTYPE != "",
]

# Convert health measures to numeric.
storm$FATALITIES <- as.numeric(storm$FATALITIES)
storm$INJURIES <- as.numeric(storm$INJURIES)

# Replace missing health counts with zero.
storm$FATALITIES[is.na(storm$FATALITIES)] <- 0
storm$INJURIES[is.na(storm$INJURIES)] <- 0

summary(storm[, c("FATALITIES", "INJURIES")])
##    FATALITIES          INJURIES        
##  Min.   :  0.0000   Min.   :   0.0000  
##  1st Qu.:  0.0000   1st Qu.:   0.0000  
##  Median :  0.0000   Median :   0.0000  
##  Mean   :  0.0168   Mean   :   0.1557  
##  3rd Qu.:  0.0000   3rd Qu.:   0.0000  
##  Max.   :583.0000   Max.   :1700.0000

3. Convert damage estimates to dollars

Property and crop damage are recorded as numeric amounts with separate magnitude codes. For example, K represents thousands, M represents millions, and B represents billions. Numeric codes from 0 through 8 are interpreted as powers of ten. Blank or unrecognized codes receive a multiplier of one; this is a simplifying assumption that may affect some estimates.

damage_multiplier <- function(x) {
  code <- toupper(trimws(as.character(x)))

  # Default multiplier.
  multiplier <- rep(1, length(code))

  # Standard magnitude codes.
  multiplier[code == "H"] <- 1e2
  multiplier[code == "K"] <- 1e3
  multiplier[code == "M"] <- 1e6
  multiplier[code == "B"] <- 1e9

  # Numeric exponent codes.
  numeric_code <- !is.na(code) & grepl("^[0-8]$", code)

  multiplier[numeric_code] <-
    10 ^ as.numeric(code[numeric_code])

  # Treat blanks and conventional placeholder codes as multiplier 1.
  multiplier[
    is.na(code) |
      code == "" |
      code %in% c("+", "-", "?")
  ] <- 1

  multiplier
}

# Convert damage amounts to numeric values.
storm$PROPDMG <- as.numeric(storm$PROPDMG)
storm$CROPDMG <- as.numeric(storm$CROPDMG)

storm$PROPDMG[is.na(storm$PROPDMG)] <- 0
storm$CROPDMG[is.na(storm$CROPDMG)] <- 0

# Calculate property and crop damage in dollars.
storm$PROPERTY_DAMAGE <-
  storm$PROPDMG * damage_multiplier(storm$PROPDMGEXP)

storm$CROP_DAMAGE <-
  storm$CROPDMG * damage_multiplier(storm$CROPDMGEXP)

# Guard against invalid calculations.
storm$PROPERTY_DAMAGE[
  !is.finite(storm$PROPERTY_DAMAGE)
] <- 0

storm$CROP_DAMAGE[
  !is.finite(storm$CROP_DAMAGE)
] <- 0

# Combined economic damage.
storm$TOTAL_DAMAGE <-
  storm$PROPERTY_DAMAGE + storm$CROP_DAMAGE

summary(
  storm[, c(
    "PROPERTY_DAMAGE",
    "CROP_DAMAGE",
    "TOTAL_DAMAGE"
  )]
)
##  PROPERTY_DAMAGE      CROP_DAMAGE         TOTAL_DAMAGE     
##  Min.   :0.000e+00   Min.   :0.000e+00   Min.   :0.00e+00  
##  1st Qu.:0.000e+00   1st Qu.:0.000e+00   1st Qu.:0.00e+00  
##  Median :0.000e+00   Median :0.000e+00   Median :0.00e+00  
##  Mean   :4.746e+05   Mean   :5.442e+04   Mean   :5.29e+05  
##  3rd Qu.:5.000e+02   3rd Qu.:0.000e+00   3rd Qu.:1.00e+03  
##  Max.   :1.150e+11   Max.   :5.000e+09   Max.   :1.15e+11

4. Aggregate the data by event type

The health measures are summed separately. Economic damage is also aggregated separately for property, crops, and their combined total. Each ranking is sorted from highest to lowest impact.

# Aggregate fatalities and injuries.
health_summary <- aggregate(
  cbind(FATALITIES, INJURIES) ~ EVTYPE,
  data = storm,
  FUN = sum
)

health_by_fatalities <- health_summary[
  order(-health_summary$FATALITIES),
]

health_by_injuries <- health_summary[
  order(-health_summary$INJURIES),
]

# Aggregate property, crop, and combined damage.
economic_summary <- aggregate(
  cbind(
    PROPERTY_DAMAGE,
    CROP_DAMAGE,
    TOTAL_DAMAGE
  ) ~ EVTYPE,
  data = storm,
  FUN = sum
)

economic_by_total <- economic_summary[
  order(-economic_summary$TOTAL_DAMAGE),
]

economic_by_property <- economic_summary[
  order(-economic_summary$PROPERTY_DAMAGE),
]

economic_by_crops <- economic_summary[
  order(-economic_summary$CROP_DAMAGE),
]

Results

1. Which event types are most harmful to population health?

Fatalities and injuries provide two different measures of population health impacts. The following tables identify the ten event types with the highest cumulative totals for each measure.

Top 10 event types by fatalities

fatality_top10 <- head(
  health_by_fatalities[
    , c("EVTYPE", "FATALITIES")
  ],
  10
)

knitr::kable(
  fatality_top10,
  col.names = c("Event type", "Total fatalities"),
  caption = "Top 10 event types by reported fatalities",
  format.args = list(big.mark = ",")
)
Top 10 event types by reported fatalities
Event type Total fatalities
750 TORNADO 5,633
108 EXCESSIVE HEAT 1,903
130 FLASH FLOOD 978
235 HEAT 937
410 LIGHTNING 816
771 TSTM WIND 504
146 FLOOD 470
516 RIP CURRENT 368
312 HIGH WIND 248
11 AVALANCHE 224

Top 10 event types by injuries

injury_top10 <- head(
  health_by_injuries[
    , c("EVTYPE", "INJURIES")
  ],
  10
)

knitr::kable(
  injury_top10,
  col.names = c("Event type", "Total injuries"),
  caption = "Top 10 event types by reported injuries",
  format.args = list(big.mark = ",")
)
Top 10 event types by reported injuries
Event type Total injuries
750 TORNADO 91,346
771 TSTM WIND 6,957
146 FLOOD 6,789
108 EXCESSIVE HEAT 6,525
410 LIGHTNING 5,230
235 HEAT 2,100
379 ICE STORM 1,975
130 FLASH FLOOD 1,777
677 THUNDERSTORM WIND 1,488
204 HAIL 1,361

Figure 1: Health impacts of severe weather events

The following figure compares the leading event types for fatalities and injuries. The panels are ranked independently, and their horizontal axes are scaled separately.

fatality_plot_data <- data.frame(
  EVTYPE = fatality_top10$EVTYPE,
  Impact = "Fatalities",
  Count = fatality_top10$FATALITIES
)

injury_plot_data <- data.frame(
  EVTYPE = injury_top10$EVTYPE,
  Impact = "Injuries",
  Count = injury_top10$INJURIES
)

health_plot_data <- rbind(
  fatality_plot_data,
  injury_plot_data
)

ggplot(
  health_plot_data,
  aes(x = Count, y = reorder(EVTYPE, Count))
) +
  geom_col(fill = "steelblue") +
  facet_wrap(
    ~ Impact,
    scales = "free",
    ncol = 2
  ) +
  labs(
    title = "Weather Events with the Greatest Health Impacts",
    x = "Total reported count",
    y = "Event type"
  ) +
  theme_minimal(base_size = 10) +
  theme(
    plot.title = element_text(face = "bold"),
    strip.text = element_text(face = "bold")
  )

The tables and figure show which event types account for the largest reported numbers of fatalities and injuries. The two rankings may differ because some event types contribute more heavily to deaths while others cause more nonfatal injuries. These figures represent cumulative totals across recorded events, not the average severity of an individual event.

2. Which event types have the greatest economic consequences?

Economic impacts are measured by the combined estimates of property and crop damage. The values are expressed in dollars after applying the recorded magnitude codes.

Top 10 event types by combined damage

economic_top10 <- head(
  economic_by_total[
    , c(
      "EVTYPE",
      "PROPERTY_DAMAGE",
      "CROP_DAMAGE",
      "TOTAL_DAMAGE"
    )
  ],
  10
)

knitr::kable(
  economic_top10,
  col.names = c(
    "Event type",
    "Property damage ($)",
    "Crop damage ($)",
    "Combined damage ($)"
  ),
  caption = "Top 10 event types by combined estimated damage",
  format.args = list(big.mark = ",", scientific = FALSE)
)
Top 10 event types by combined estimated damage
Event type Property damage (\()| Crop damage (\)) Combined damage ($)
146 FLOOD 144,657,709,807 5,661,968,450 150,319,678,257
364 HURRICANE/TYPHOON 69,305,840,000 2,607,872,800 71,913,712,800
750 TORNADO 56,947,380,677 414,953,270 57,362,333,947
591 STORM SURGE 43,323,536,000 5,000 43,323,541,000
204 HAIL 15,735,267,513 3,025,954,473 18,761,221,986
130 FLASH FLOOD 16,822,723,979 1,421,317,100 18,244,041,079
76 DROUGHT 1,046,106,000 13,972,566,000 15,018,672,000
355 HURRICANE 11,868,319,010 2,741,910,000 14,610,229,010
521 RIVER FLOOD 5,118,945,500 5,029,459,000 10,148,404,500
379 ICE STORM 3,944,927,860 5,022,113,500 8,967,041,360

Property damage and crop damage separately

The next two tables distinguish losses to property from agricultural losses.

property_top10 <- head(
  economic_by_property[
    , c("EVTYPE", "PROPERTY_DAMAGE")
  ],
  10
)

crop_top10 <- head(
  economic_by_crops[
    , c("EVTYPE", "CROP_DAMAGE")
  ],
  10
)

knitr::kable(
  property_top10,
  col.names = c("Event type", "Property damage ($)"),
  caption = "Top 10 event types by property damage",
  format.args = list(big.mark = ",", scientific = FALSE)
)
Top 10 event types by property damage
Event type Property damage ($)
146 FLOOD 144,657,709,807
364 HURRICANE/TYPHOON 69,305,840,000
750 TORNADO 56,947,380,677
591 STORM SURGE 43,323,536,000
130 FLASH FLOOD 16,822,723,979
204 HAIL 15,735,267,513
355 HURRICANE 11,868,319,010
764 TROPICAL STORM 7,703,890,550
880 WINTER STORM 6,688,497,251
312 HIGH WIND 5,270,046,295
knitr::kable(
  crop_top10,
  col.names = c("Event type", "Crop damage ($)"),
  caption = "Top 10 event types by crop damage",
  format.args = list(big.mark = ",", scientific = FALSE)
)
Top 10 event types by crop damage
Event type Crop damage ($)
76 DROUGHT 13,972,566,000
146 FLOOD 5,661,968,450
521 RIVER FLOOD 5,029,459,000
379 ICE STORM 5,022,113,500
204 HAIL 3,025,954,473
355 HURRICANE 2,741,910,000
364 HURRICANE/TYPHOON 2,607,872,800
130 FLASH FLOOD 1,421,317,100
117 EXTREME COLD 1,312,973,000
179 FROST/FREEZE 1,094,186,000

Figure 2: Economic impacts of severe weather events

This figure compares the ten leading event types for property damage and crop damage. Damage values are displayed in billions of dollars for readability.

property_plot_data <- data.frame(
  EVTYPE = property_top10$EVTYPE,
  DamageType = "Property damage",
  Damage = property_top10$PROPERTY_DAMAGE
)

crop_plot_data <- data.frame(
  EVTYPE = crop_top10$EVTYPE,
  DamageType = "Crop damage",
  Damage = crop_top10$CROP_DAMAGE
)

economic_plot_data <- rbind(
  property_plot_data,
  crop_plot_data
)

ggplot(
  economic_plot_data,
  aes(x = Damage / 1e9, y = reorder(EVTYPE, Damage))
) +
  geom_col(fill = "darkorange3") +
  facet_wrap(
    ~ DamageType,
    scales = "free",
    ncol = 2
  ) +
  labs(
    title = "Weather Events with the Greatest Economic Losses",
    x = "Estimated damage (billions of dollars)",
    y = "Event type"
  ) +
  theme_minimal(base_size = 10) +
  theme(
    plot.title = element_text(face = "bold"),
    strip.text = element_text(face = "bold")
  )

The combined economic ranking identifies the event types associated with the largest total reported losses. The separate rankings show whether property or crop damage contributes more heavily to each event type’s overall economic impact. These values are estimates and depend on the quality of the original records and the interpretation of the damage magnitude codes.

3. Summary of the leading event types

The following code prints the top-ranked event type for each impact measure directly from the aggregated data.

top_fatalities <- health_by_fatalities[1, ]
top_injuries <- health_by_injuries[1, ]
top_combined_damage <- economic_by_total[1, ]
top_property_damage <- economic_by_property[1, ]
top_crop_damage <- economic_by_crops[1, ]

cat(
  "Most fatalities:",
  top_fatalities$EVTYPE,
  "\nTotal fatalities:",
  format(top_fatalities$FATALITIES, big.mark = ","),
  "\n\n"
)
## Most fatalities: TORNADO 
## Total fatalities: 5,633
cat(
  "Most injuries:",
  top_injuries$EVTYPE,
  "\nTotal injuries:",
  format(top_injuries$INJURIES, big.mark = ","),
  "\n\n"
)
## Most injuries: TORNADO 
## Total injuries: 91,346
cat(
  "Greatest combined economic damage:",
  top_combined_damage$EVTYPE,
  "\nCombined damage ($):",
  format(
    round(top_combined_damage$TOTAL_DAMAGE, 0),
    big.mark = ",",
    scientific = FALSE
  ),
  "\n\n"
)
## Greatest combined economic damage: FLOOD 
## Combined damage ($): 150,319,678,257
cat(
  "Greatest property damage:",
  top_property_damage$EVTYPE,
  "\nProperty damage ($):",
  format(
    round(top_property_damage$PROPERTY_DAMAGE, 0),
    big.mark = ",",
    scientific = FALSE
  ),
  "\n\n"
)
## Greatest property damage: FLOOD 
## Property damage ($): 144,657,709,807
cat(
  "Greatest crop damage:",
  top_crop_damage$EVTYPE,
  "\nCrop damage ($):",
  format(
    round(top_crop_damage$CROP_DAMAGE, 0),
    big.mark = ",",
    scientific = FALSE
  ),
  "\n"
)
## Greatest crop damage: DROUGHT 
## Crop damage ($): 13,972,566,000

Limitations

Several limitations should be considered when interpreting these results.

  1. Historical coverage: The database’s event coverage and reporting practices have changed over time.
  2. Event classification: Standardizing capitalization and whitespace does not merge misspellings or synonymous event labels. As a result, impacts associated with a hazard may be split across multiple categories.
  3. Cumulative totals: The analysis compares total recorded impacts, not average impacts per event, impacts per capita, or the probability of a hazard occurring.
  4. Damage estimates: The economic analysis relies on the recorded damage values and magnitude codes. Treating unrecognized codes as a multiplier of one is a simplifying assumption.
  5. Recorded impacts: The analysis summarizes recorded associations and does not establish causation.

Conclusion

This analysis uses the NOAA Storm Database to compare the health and economic impacts of severe weather events across the United States. Fatality and injury rankings identify the event types associated with the greatest cumulative human harm. Property, crop, and combined damage rankings identify the event types associated with the largest estimated economic losses. The tables and figures provide complementary perspectives on historical weather impacts. The findings can support further analysis of severe weather risks, while the limitations of historical reporting and event classification should be considered when interpreting the rankings.

References