Synopsis

This analysis examines which recorded weather event types caused the greatest health and economic impacts in the United States using the NOAA storm dataset supplied for the course, covering 1950 through November 2011. Health impact is measured by total fatalities and total injuries, assessed separately; economic impact is measured by property plus crop damage in reported US dollars, without adjustment for inflation. All processing starts from the original compressed CSV file and includes basic event-name cleaning and conversion of damage codes into dollar amounts. Among the resulting event labels, TORNADO has the highest recorded totals for both fatalities and injuries, while FLOOD has the greatest recorded economic damage under the stated conversion rules. These findings describe cumulative recorded losses and should be interpreted in light of incomplete historical reporting, inconsistent event labels, and uncertain damage estimates.

Data Processing

This report answers the two assignment questions:

  1. Across the United States, which types of events, as indicated by EVTYPE, are most harmful with respect to population health?
  2. Across the United States, which types of events have the greatest economic consequences?

All processing and analysis code is shown below. Only knitr and rmarkdown are needed to render this report; the analysis uses base R.

Load the original data

The course dataset is read directly from StormData.csv.bz2; if absent, it is downloaded from the course URL. Seven variables are retained: EVTYPE identifies the recorded event type; FATALITIES and INJURIES give the reported health impacts; PROPDMG and CROPDMG give the property and crop damage amounts; and PROPDMGEXP and CROPDMGEXP specify their scale. Selecting these columns reduces memory use without removing any records. Blank damage codes are preserved for interpretation in the next step. The report uses no previously cleaned data or restored workspace objects.

knitr::opts_chunk$set(echo = TRUE, cache = FALSE, fig.align = "center")
options(scipen = 999)
raw_file <- "StormData.csv.bz2"
data_url <- paste0("https://d396qusza40orc.cloudfront.net/",
                   "repdata%2Fdata%2FStormData.csv.bz2")
if (!file.exists(raw_file)) download.file(data_url, raw_file, mode = "wb")
keep <- c("EVTYPE", "FATALITIES", "INJURIES", "PROPDMG",
          "PROPDMGEXP", "CROPDMG", "CROPDMGEXP")
header <- names(read.csv(bzfile(raw_file), nrows = 0))
classes <- ifelse(header %in% keep, "character", "NULL")
storm <- read.csv(bzfile(raw_file), colClasses = classes,
                  na.strings = "NA", stringsAsFactors = FALSE)
numeric_fields <- c("FATALITIES", "INJURIES", "PROPDMG", "CROPDMG")
storm[numeric_fields] <- lapply(storm[numeric_fields], as.numeric)
stopifnot(nrow(storm) == 902297L,
          !anyNA(storm[numeric_fields]),
          all(as.matrix(storm[numeric_fields]) >= 0))

The raw data contain 902,297 records. The checks above confirm that the four numeric impact fields contain no missing or negative values; they do not establish whether every recorded estimate is accurate.

Event names and damage amounts

Event names are converted to uppercase, leading and trailing spaces are removed, and repeated spaces are collapsed so that capitalization or spacing alone does not split a category. Otherwise, the recorded EVTYPE labels are retained: for example, TSTM WIND and THUNDERSTORM WIND remain separate. The rankings therefore compare recorded labels rather than fully harmonized hazard categories. This avoids speculative reclassification, but can split related events across categories. Records with matching impact values are retained because separate events can have identical recorded impacts.

Damage codes K, M, and B represent thousands, millions, and billions, consistent with the National Weather Service documentation. For example, an amount of 2.5 with code K becomes 2.5 x 1,000 = $2,500. The additional assumptions used here are that H means hundreds and a blank code means dollars; lowercase letters are treated like uppercase letters.

Numeric and punctuation codes are treated as unresolved rather than assigned a guessed multiplier. A reported zero amount contributes zero regardless of its code. A positive amount with an unresolved code becomes missing (NA) and is excluded from the corresponding damage sum. The other damage component and the health impacts for that record are still retained. The table below counts these excluded components, which are not necessarily distinct records. Property and crop damage are converted once into new columns, preserving the original codes. Consequently, the reported economic totals cover interpretable amounts only; excluded losses are not estimated.

storm$event <- toupper(trimws(gsub("[[:space:]]+", " ", storm$EVTYPE)))
stopifnot(!anyNA(storm$event), all(nzchar(storm$event)))
damage_dollars <- function(amount, exponent) {
  code <- toupper(trimws(exponent))
  multiplier <- c(1, 100, 1000, 1000000, 1000000000)[
    match(code, c("", "H", "K", "M", "B"))]
  result <- amount * multiplier
  result[amount == 0] <- 0
  result
}
storm$property <- damage_dollars(storm$PROPDMG, storm$PROPDMGEXP)
storm$crop <- damage_dollars(storm$CROPDMG, storm$CROPDMGEXP)
excluded <- c(property = sum(is.na(storm$property)),
              crop = sum(is.na(storm$crop)))
knitr::kable(data.frame(Component = names(excluded),
                        Unresolved_positive_amounts = unname(excluded)),
             caption = "Positive damage components excluded because their codes are ambiguous.")
Positive damage components excluded because their codes are ambiguous.
Component Unresolved_positive_amounts
property 244
crop 12

All supplied years and records are retained. For each cleaned event label, fatalities and injuries are summed and ranked separately, avoiding an arbitrary equivalence between a death and an injury. Economic impact is the sum of interpretable property and crop damage amounts for each label, in reported dollars without inflation adjustment. Each ranking is sorted from largest to smallest, and its ten leading labels are plotted. The final checks verify that aggregation preserves the included totals.

health <- aggregate(storm[c("FATALITIES", "INJURIES")],
                    list(Event = storm$event), sum)
economic <- aggregate(storm[c("property", "crop")],
                      list(Event = storm$event), sum, na.rm = TRUE)
economic$total <- economic$property + economic$crop
fatal_top <- head(health[order(-health$FATALITIES), ], 10)
injury_top <- head(health[order(-health$INJURIES), ], 10)
economic_top <- head(economic[order(-economic$total), ], 10)
stopifnot(sum(health$FATALITIES) == sum(storm$FATALITIES),
          sum(health$INJURIES) == sum(storm$INJURIES),
          isTRUE(all.equal(sum(economic$total),
            sum(storm$property, na.rm = TRUE) + sum(storm$crop, na.rm = TRUE))))

Results

Population health

TORNADO has the highest recorded fatality total (5,633 deaths). TORNADO also has the highest injury total (91,346 injuries). Thus, tornadoes lead both measures used here to answer the population-health question. The next-highest labels are EXCESSIVE HEAT for fatalities and TSTM WIND for injuries. Figure 1 shows the ten leading labels for each measure; the panels use different scales, so bar lengths should be compared within each panel.

old_par <- par(mfrow = c(2, 1), mar = c(4, 12, 3, 1), las = 1)
barplot(rev(fatal_top$FATALITIES), names.arg = rev(fatal_top$Event),
        horiz = TRUE, col = "#B45309", border = NA, cex.names = 0.8,
        main = "Recorded fatalities", xlab = "Number of deaths")
barplot(rev(injury_top$INJURIES), names.arg = rev(injury_top$Event),
        horiz = TRUE, col = "#2563A6", border = NA, cex.names = 0.8,
        main = "Recorded injuries", xlab = "Number of injuries")
Figure 1. Ten event labels with the largest recorded fatality totals (top) and injury totals (bottom), 1950–November 2011. Each panel is ranked independently.

Figure 1. Ten event labels with the largest recorded fatality totals (top) and injury totals (bottom), 1950–November 2011. Each panel is ranked independently.

par(old_par)

Economic consequences

FLOOD has the greatest recorded property-plus-crop damage: $150.32 billion, including $144.66 billion in property damage and $5.66 billion in crop damage. It is followed by HURRICANE/TYPHOON and TORNADO. This answers the economic question using recorded direct property and crop losses; indirect costs such as business interruption are not measured. Figure 2 compares the ten leading event labels under the conversion rules above.

old_par <- par(mar = c(4, 12, 3, 1), las = 1)
barplot(rev(economic_top$total) / 1e9,
        names.arg = rev(economic_top$Event), horiz = TRUE,
        col = "#247568", border = NA, cex.names = 0.8,
        main = "Recorded economic damage",
        xlab = "Property + crop damage (US$ billions)")
Figure 2. Ten event labels with the largest recorded property-plus-crop losses, in billions of nominal US dollars. Ambiguous positive damage components are excluded.

Figure 2. Ten event labels with the largest recorded property-plus-crop losses, in billions of nominal US dollars. Ambiguous positive damage components are excluded.

par(old_par)

Interpretation and limitations

The two answers concern cumulative recorded impacts, not event frequency, per-event severity, or present-day risk. Earlier reporting is less complete, so comparisons across the full period favor hazards with longer reporting histories. Labels have only minimal normalization, meaning related hazards may be split across several categories. Economic rankings depend on the accuracy of the supplied damage estimates, omit unresolved components, and do not adjust for inflation. No outliers have been silently corrected or removed. Within these limits, tornadoes lead both health measures and floods lead recorded economic losses.

Reproducibility

The complete analysis above runs from the compressed raw file in a fresh R session. The following records the R and package environment used to build this document.

sessionInfo()
## R version 4.6.1 (2026-06-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=Dutch_Netherlands.utf8  LC_CTYPE=Dutch_Netherlands.utf8   
## [3] LC_MONETARY=Dutch_Netherlands.utf8 LC_NUMERIC=C                      
## [5] LC_TIME=Dutch_Netherlands.utf8    
## 
## time zone: Europe/Amsterdam
## 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.1    rstudioapi_0.19.0 tools_4.6.1       evaluate_1.0.5   
## [17] bslib_0.12.0      yaml_2.3.12       otel_0.2.0        jsonlite_2.0.0   
## [21] rlang_1.3.0