Synopsis

This analysis examines recorded health and economic impacts of severe weather in the NOAA Storm Database from 1950 through November 2011. Population health impacts are assessed using fatalities and injuries separately. Economic impacts are measured as the sum of interpretable property and crop damage estimates in nominal US dollars. Tornadoes have the highest recorded totals, with 5,633 fatalities and 91,346 injuries. Flood has the largest recorded economic total, approximately $150.32 billion. However, this ranking is strongly influenced by one California flood record containing $115 billion in property damage. When that entire record is excluded in a sensitivity analysis, hurricane/typhoon ranks first at approximately $87.12 billion. The results describe historical recorded totals rather than current risk, and are limited by uneven reporting, incomplete event-label harmonization, ambiguous damage codes, and influential records.

Data Processing

Reproducible setup and data loading

All processing starts from the original compressed CSV file. If the file is absent, the code downloads it from the course data source. No manually processed intermediate dataset is required.

knitr::opts_chunk$set(
  echo = TRUE,
  fig.width = 11,
  fig.height = 6,
  fig.align = "center"
)

options(timeout = 600)
data_file <- "StormData.csv.bz2"

data_url <- paste0(
  "https://d396qusza40orc.cloudfront.net/",
  "repdata%2Fdata%2FStormData.csv.bz2"
)

if (!file.exists(data_file)) {
  download.file(
    data_url,
    destfile = data_file,
    mode = "wb"
  )
}

storm <- read.csv(
  data_file,
  stringsAsFactors = FALSE
)

dim(storm)
## [1] 902297     37
dat <- storm[, c(
  "BGN_DATE", "STATE", "EVTYPE",
  "FATALITIES", "INJURIES",
  "PROPDMG", "PROPDMGEXP",
  "CROPDMG", "CROPDMGEXP"
)]

dat$row_id <- seq_len(nrow(dat))

colSums(is.na(dat[, c(
  "FATALITIES", "INJURIES", "PROPDMG", "CROPDMG"
)]))
## FATALITIES   INJURIES    PROPDMG    CROPDMG 
##          0          0          0          0

All records in the supplied file are retained for the primary analysis. Earlier years have less complete event coverage, so cumulative totals should not be interpreted as comparisons over equal observation periods. Fatalities and injuries are analyzed separately to avoid assigning them an arbitrary common weight.

Event-label normalization

Event labels are converted to uppercase, and leading, trailing, and repeated spaces are removed. The explicit mapping below combines a small number of clear naming variants. Other labels remain distinct; this is not a complete reconciliation to the NOAA event classification.

dat$event <- toupper(trimws(dat$EVTYPE))
dat$event <- gsub("[[:space:]]+", " ", dat$event)

aliases <- c(
  "TSTM WIND" = "THUNDERSTORM WIND",
  "THUNDERSTORM WINDS" = "THUNDERSTORM WIND",
  "MARINE TSTM WIND" = "MARINE THUNDERSTORM WIND",
  "HURRICANE" = "HURRICANE/TYPHOON",
  "TYPHOON" = "HURRICANE/TYPHOON"
)

idx <- match(dat$event, names(aliases))
matched <- !is.na(idx)

dat$event[matched] <- unname(aliases[idx[matched]])

data.frame(
  original_labels = length(unique(dat$EVTYPE)),
  normalized_labels = length(unique(dat$event))
)
##   original_labels normalized_labels
## 1             985               878

Converting damage estimates to dollars

The National Weather Service documentation identifies K, M, and B as thousands, millions, and billions, respectively. See the Storm Data Preparation instructions.

Lowercase codes are converted to uppercase. For this analysis, H is assumed to mean hundreds and a blank code is assumed to indicate unscaled dollars. These are explicit assumptions for legacy entries. Other numeric and symbolic codes are treated as uninterpretable. Zero-valued damage components remain zero regardless of their code.

Uninterpretable positive components are excluded from monetary sums, while any interpretable component from the same record is retained. Therefore, the reported sums do not establish that omitted losses were zero, and they may understate recorded damage.

table(dat$PROPDMGEXP, useNA = "ifany")
## 
##             -      ?      +      0      1      2      3      4      5      6 
## 465934      1      8      5    216     25     13      4      4     28      4 
##      7      8      B      h      H      K      m      M 
##      5      1     40      1      6 424665      7  11330
table(dat$CROPDMGEXP, useNA = "ifany")
## 
##             ?      0      2      B      k      K      m      M 
## 618413      7     19      1      9     21 281832      1   1994
damage_dollars <- function(amount, exponent) {
  exponent <- toupper(trimws(exponent))

  multipliers <- c(
    H = 100,
    K = 1000,
    M = 1000000,
    B = 1000000000
  )

  multiplier <- unname(multipliers[exponent])
  multiplier[exponent == ""] <- 1

  result <- amount * multiplier
  result[amount == 0] <- 0

  result
}

dat$property_usd <- damage_dollars(
  dat$PROPDMG, dat$PROPDMGEXP
)

dat$crop_usd <- damage_dollars(
  dat$CROPDMG, dat$CROPDMGEXP
)

unknown_components <- data.frame(
  component = c("Property", "Crop"),
  excluded_positive_components = c(
    sum(is.na(dat$property_usd)),
    sum(is.na(dat$crop_usd))
  )
)

knitr::kable(unknown_components)
component excluded_positive_components
Property 244
Crop 12

Damage estimates are summed in nominal dollars without inflation adjustment. They cover reported property and crop damage, rather than all economic costs such as business interruption or health expenditure.

Aggregation and sensitivity analysis

Health outcomes and interpretable monetary components are summed by normalized event label. Rankings measure total recorded burden, not average severity per event or risk per exposed person.

The largest property-damage entry is inspected below. The primary analysis retains it as recorded. A sensitivity analysis excludes the entire record without claiming that its reported amount has been independently verified or corrected.

health <- aggregate(
  cbind(FATALITIES, INJURIES) ~ event,
  data = dat,
  FUN = sum
)

economic_totals <- function(x) {
  result <- aggregate(
    x[, c("property_usd", "crop_usd")],
    by = list(event = x$event),
    FUN = sum,
    na.rm = TRUE
  )

  result$total_usd <- result$property_usd + result$crop_usd

  result <- result[
    order(result$total_usd, decreasing = TRUE),
  ]

  result
}

economic <- economic_totals(dat)

influential <- which(
  dat$STATE == "CA" &
    dat$event == "FLOOD" &
    dat$BGN_DATE == "1/1/2006 0:00:00" &
    dat$PROPDMG == 115 &
    toupper(trimws(dat$PROPDMGEXP)) == "B"
)

stopifnot(length(influential) == 1L)

knitr::kable(
  dat[influential, c(
    "row_id", "BGN_DATE", "STATE", "event",
    "property_usd", "crop_usd"
  )]
)
row_id BGN_DATE STATE event property_usd crop_usd
605953 605953 1/1/2006 0:00:00 CA FLOOD 1.15e+11 32500000
economic_sensitivity <- economic_totals(
  dat[-influential, ]
)

Results

Which events are most harmful to population health?

top_fatalities <- head(
  health[order(health$FATALITIES, decreasing = TRUE), ],
  10
)

top_injuries <- head(
  health[order(health$INJURIES, decreasing = TRUE), ],
  10
)

knitr::kable(
  top_fatalities,
  row.names = FALSE,
  caption = "Top ten event categories ranked by fatalities."
)
Top ten event categories ranked by fatalities.
event FATALITIES INJURIES
TORNADO 5633 91346
EXCESSIVE HEAT 1903 6525
FLASH FLOOD 978 1777
HEAT 937 2100
LIGHTNING 816 5230
THUNDERSTORM WIND 701 9363
FLOOD 470 6789
RIP CURRENT 368 232
HIGH WIND 248 1137
AVALANCHE 224 170
knitr::kable(
  top_injuries,
  row.names = FALSE,
  caption = "Top ten event categories ranked by injuries."
)
Top ten event categories ranked by injuries.
event FATALITIES INJURIES
TORNADO 5633 91346
THUNDERSTORM WIND 701 9363
FLOOD 470 6789
EXCESSIVE HEAT 1903 6525
LIGHTNING 816 5230
HEAT 937 2100
ICE STORM 89 1975
FLASH FLOOD 978 1777
HAIL 15 1361
HURRICANE/TYPHOON 125 1326
par(mfrow = c(1, 2), mar = c(5, 11, 3, 1))

barplot(
  rev(top_fatalities$FATALITIES),
  names.arg = rev(top_fatalities$event),
  horiz = TRUE,
  las = 1,
  col = "firebrick",
  border = NA,
  cex.names = 0.75,
  main = "Recorded Fatalities",
  xlab = "Number of fatalities"
)

barplot(
  rev(top_injuries$INJURIES),
  names.arg = rev(top_injuries$event),
  horiz = TRUE,
  las = 1,
  col = "steelblue",
  border = NA,
  cex.names = 0.75,
  main = "Recorded Injuries",
  xlab = "Number of injuries"
)
Figure 1. Ten leading event categories by recorded fatalities and injuries. Each panel uses its own numerical scale.

Figure 1. Ten leading event categories by recorded fatalities and injuries. Each panel uses its own numerical scale.

par(mfrow = c(1, 1))

Tornadoes rank first for both outcomes, with 5,633 fatalities and 91,346 injuries. Excessive heat ranks second for fatalities, whereas thunderstorm wind ranks second for injuries. The ordering therefore depends on the health outcome examined.

Which events have the greatest economic consequences?

top_economic <- head(economic, 10)

economic_display <- data.frame(
  Event = top_economic$event,
  Property_billion_USD = top_economic$property_usd / 1e9,
  Crop_billion_USD = top_economic$crop_usd / 1e9,
  Total_billion_USD = top_economic$total_usd / 1e9
)

knitr::kable(
  economic_display,
  digits = 3,
  row.names = FALSE
)
Event Property_billion_USD Crop_billion_USD Total_billion_USD
FLOOD 144.658 5.662 150.320
HURRICANE/TYPHOON 81.774 5.351 87.125
TORNADO 56.937 0.415 57.352
STORM SURGE 43.324 0.000 43.324
HAIL 15.732 3.026 18.758
FLASH FLOOD 16.141 1.421 17.562
DROUGHT 1.046 13.973 15.019
THUNDERSTORM WIND 9.712 1.160 10.872
RIVER FLOOD 5.119 5.029 10.148
ICE STORM 3.945 5.022 8.967
par(mar = c(5, 12, 3, 2))

barplot(
  rev(top_economic$total_usd / 1e9),
  names.arg = rev(top_economic$event),
  horiz = TRUE,
  las = 1,
  col = "darkorange",
  border = NA,
  cex.names = 0.85,
  main = "Recorded Property and Crop Damage",
  xlab = "Damage (billion nominal USD)"
)
Figure 2. Ten leading event categories by combined interpretable property and crop damage, retaining all records. Values are billions of nominal US dollars.

Figure 2. Ten leading event categories by combined interpretable property and crop damage, retaining all records. Values are billions of nominal US dollars.

With all records retained, flood has the largest estimated total, approximately $150.32 billion. Hurricane/typhoon and tornado rank next. These are totals for the retained event categories, not complete costs of individual named disasters: for example, storm surge remains a separate category.

How sensitive is the economic ranking to the largest record?

sensitivity_display <- data.frame(
  Event = head(economic_sensitivity$event, 10),
  Total_billion_USD = head(
    economic_sensitivity$total_usd, 10
  ) / 1e9
)

knitr::kable(
  sensitivity_display,
  digits = 3,
  row.names = FALSE
)
Event Total_billion_USD
HURRICANE/TYPHOON 87.125
TORNADO 57.352
STORM SURGE 43.324
FLOOD 35.287
HAIL 18.758
FLASH FLOOD 17.562
DROUGHT 15.019
THUNDERSTORM WIND 10.872
RIVER FLOOD 10.148
ICE STORM 8.967
comparison_events <- unique(c(
  head(economic$event, 5),
  head(economic_sensitivity$event, 5)
))

comparison_values <- rbind(
  economic$total_usd[
    match(comparison_events, economic$event)
  ],
  economic_sensitivity$total_usd[
    match(comparison_events, economic_sensitivity$event)
  ]
) / 1e9

colnames(comparison_values) <- comparison_events
par(mar = c(5, 12, 3, 2))

barplot(
  comparison_values,
  beside = TRUE,
  horiz = TRUE,
  las = 1,
  col = c("steelblue", "darkorange"),
  border = NA,
  cex.names = 0.85,
  main = "Sensitivity of Economic Damage Totals",
  xlab = "Damage (billion nominal USD)"
)

legend(
  "topright",
  legend = c("All records", "Influential record excluded"),
  fill = c("steelblue", "darkorange"),
  bty = "n",
  cex = 0.8
)
Figure 3. Economic totals with all records retained and with the entire influential California flood record excluded. The selected categories are the union of the top five in either analysis.

Figure 3. Economic totals with all records retained and with the entire influential California flood record excluded. The selected categories are the union of the top five in either analysis.

Excluding the entire influential record reduces the flood total to approximately $35.29 billion. Hurricane/typhoon then ranks first at approximately $87.12 billion. The removed record includes both property and crop damage, so the reduction slightly exceeds $115 billion.

The economic leader is therefore sensitive to this record. The primary result is reported as recorded, with the alternative ranking presented to show its influence.

Limitations

Reporting practices and coverage changed over the study period. Only selected naming variants were combined, so related hazards may remain split across categories. Damage-code assumptions and excluded uninterpretable components introduce uncertainty. Monetary values are not inflation-adjusted, and the influential flood entry has not been independently corrected. The findings summarize historical recorded burden and do not estimate present-day probabilities or future losses.

Computational environment

sessionInfo()
## R version 4.6.0 (2026-04-24 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=English_Indonesia.utf8  LC_CTYPE=English_Indonesia.utf8   
## [3] LC_MONETARY=English_Indonesia.utf8 LC_NUMERIC=C                      
## [5] LC_TIME=English_Indonesia.utf8    
## 
## time zone: Asia/Makassar
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## loaded via a namespace (and not attached):
##  [1] digest_0.6.39     R6_2.6.1          fastmap_1.2.0     xfun_0.60        
##  [5] cachem_1.1.0      knitr_1.51        htmltools_0.5.9   rmarkdown_2.31   
##  [9] lifecycle_1.0.5   cli_3.6.6         sass_0.4.10       jquerylib_0.1.4  
## [13] compiler_4.6.0    rstudioapi_0.18.0 tools_4.6.0       evaluate_1.0.5   
## [17] bslib_0.11.0      yaml_2.3.12       otel_0.2.0        jsonlite_2.0.0   
## [21] rlang_1.2.0