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.
This report answers the two assignment questions:
All processing and analysis code is shown below. Only
knitr and rmarkdown are needed to render this
report; the analysis uses base R.
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.
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 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.")| 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))))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.
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.
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.
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.
## 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