Severe weather events can cause substantial loss of life, injuries, property damage, and agricultural losses. This study analyzes the U.S. National Oceanic and Atmospheric Administration (NOAA) storm dataset supplied for the course, containing 902,297 records from 1950 through November 2011. The analysis has two objectives: identifying the most harmful recorded event types to public health and determining those with the greatest economic impact. Fatalities and injuries are summed separately, while property and crop damage are converted to dollars and added together. The analysis starts from the original compressed data file, with all processing shown in R. Tornadoes have the highest recorded fatalities and injuries, and floods have the highest recorded property and crop damage combined under the conversion rules used here. Earlier records are less complete, and differences in event names and uncertain damage values need to be considered when interpreting these results.
The analysis addresses the two questions specified for this study:
All processing and analysis code is shown below. Only
knitr and rmarkdown are needed to render this
report; the analysis uses base R.
The data are loaded directly from the original compressed CSV file,
StormData.csv.bz2, using read.csv(). If the
file is not available, the code downloads it from the course website.
Seven variables are selected for the analysis:
EVTYPE gives the recorded
name of the weather event.FATALITIES and
INJURIES give the reported numbers of deaths and
injuries.PROPDMG and
CROPDMG contain damage amounts. PROPDMGEXP and
CROPDMGEXP give the codes needed to convert these amounts
to dollars.All records are retained. Blank damage codes are kept for the conversion step. No previously cleaned data or saved R workspace is used.
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 dataset contains 902,297 records. The checks above confirm that the four numeric impact fields have no missing or negative values. These checks do not verify the accuracy of the reported estimates.
Event type names: Capital letters and spacing are
made consistent. Other differences in names are left unchanged. For
example, TSTM WIND and THUNDERSTORM WIND
remain separate because this analysis does not attempt to reclassify
every historical event name. The results therefore compare recorded
event labels, and related events may appear in separate groups. Records
are not deleted just because they have the same impact values.
Converting damage values: Property and crop damage
are calculated using their amount and code. K,
M, and B mean thousands, millions, and
billions, as described in the National
Weather Service documentation. For example, 2.5 with code
K means $2,500. This analysis also assumes that
H means hundreds and a blank code means dollars. Lowercase
letters are converted to uppercase before the calculation.
Unclear damage codes: Numeric and punctuation codes
are left unresolved. A zero amount is counted as zero. A positive amount
with an unclear code is set to NA and excluded from that
damage total; the other damage amount and health values in the same
record are retained. The table below counts the excluded property and
crop amounts, not distinct events. No replacement losses are estimated.
The original codes are kept, and converted values are stored in new
columns.
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 |
Data analysis: Fatalities and injuries are summed by event label to assess public health impact. They are ranked separately because deaths and injuries are different measures. Economic losses are calculated as the sum of property and crop damage with interpretable codes. All years are included, and amounts are not adjusted for inflation. The ten highest labels for each measure are shown in bar charts. The final checks confirm that the grouped totals match the included input values.
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))))Tornadoes therefore have the greatest recorded population-health impact on both measures used in this analysis. Figure 1 shows the ten highest event labels. The panels use different scales, so bar lengths should only be compared within the same 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.
Floods therefore have the greatest recorded economic consequences under the conversion rules used here. Figure 2 compares the ten highest event labels. These totals include direct property and crop losses; they do not include indirect costs such as business interruption.
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.
Earlier records are less complete, so care is needed when comparing event types across the full period. Differences in event naming may split related events into separate groups. Damage estimates may contain errors, unclear damage amounts are excluded, and values are not adjusted for inflation. Large reported losses have not been removed or changed. These results describe total recorded impacts over the study period, not the risk or average severity of a single event. Within these limits, tornadoes lead the health measures and floods lead the economic totals.
This analysis answers the two project questions. Tornadoes have the highest recorded totals for both deaths and injuries, making them the most harmful event type to population health in this dataset. Floods have the highest combined property and crop damage under the conversion rules used here. These findings describe recorded losses over the study period; they do not measure the risk from an individual event. Reporting differences and uncertain damage estimates should be considered when interpreting the results.
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