knitr::opts_chunk$set(fig.path = "figure/")
This report uses the U.S. National Oceanic and Atmospheric Administration’s (NOAA) Storm Database, which records characteristics of major storms and weather events across the United States from 1950 to November 2011, to identify which types of severe weather events are most harmful to population health and which have the greatest economic consequences. Health impact is measured as the sum of fatalities and injuries reported per event type, and economic impact is measured as the sum of property and crop damage (in dollars), after converting the database’s coded damage-magnitude exponents (K/M/B) into numeric multipliers. Event type labels in the raw data are lightly standardized (trimmed and case-normalized, with a small number of clear abbreviation duplicates merged) before aggregation. The analysis finds that tornadoes are, by a wide margin, the event type responsible for the most fatalities and injuries combined, while floods account for the largest total economic damage, with hurricanes/typhoons and tornadoes also among the most economically costly event types. These results are intended to help a government or municipal manager prioritize resources across event types when preparing for severe weather.
The raw data is provided as a bzip2-compressed CSV file. It is read
directly from the compressed file with read.csv(), which
handles the bzip2 decompression automatically, so no separate unzip step
is needed. The full file has 37 columns, but this analysis only needs 7
of them (the event type and the four fields that measure human and
economic impact). Loading all 37 columns - including free-text fields
like REMARKS - uses substantially more memory than the
analysis needs, so colClasses is used to tell
read.csv() to skip every column we don’t need while it
reads, rather than loading everything and subsetting afterward. This
chunk is cached since reading the full file is still the slowest
step.
col_names <- c("STATE__", "BGN_DATE", "BGN_TIME", "TIME_ZONE", "COUNTY",
"COUNTYNAME", "STATE", "EVTYPE", "BGN_RANGE", "BGN_AZI",
"BGN_LOCATI", "END_DATE", "END_TIME", "COUNTY_END",
"COUNTYENDN", "END_RANGE", "END_AZI", "END_LOCATI", "LENGTH",
"WIDTH", "F", "MAG", "FATALITIES", "INJURIES", "PROPDMG",
"PROPDMGEXP", "CROPDMG", "CROPDMGEXP", "WFO", "STATEOFFIC",
"ZONENAMES", "LATITUDE", "LONGITUDE", "LATITUDE_E",
"LONGITUDE_", "REMARKS", "REFNUM")
keep_cols <- c("EVTYPE", "FATALITIES", "INJURIES",
"PROPDMG", "PROPDMGEXP", "CROPDMG", "CROPDMGEXP")
numeric_cols <- c("FATALITIES", "INJURIES", "PROPDMG", "CROPDMG")
col_classes <- ifelse(col_names %in% keep_cols,
ifelse(col_names %in% numeric_cols, "numeric", "character"),
"NULL")
storm <- read.csv("repdata-data-StormData.csv.bz2", stringsAsFactors = FALSE,
colClasses = col_classes)
dim(storm)
## [1] 902297 7
str(storm)
## 'data.frame': 902297 obs. of 7 variables:
## $ EVTYPE : chr "TORNADO" "TORNADO" "TORNADO" "TORNADO" ...
## $ FATALITIES: num 0 0 0 0 0 0 0 0 1 0 ...
## $ INJURIES : num 15 0 2 2 2 6 1 0 14 0 ...
## $ PROPDMG : num 25 2.5 25 2.5 2.5 2.5 2.5 2.5 25 25 ...
## $ PROPDMGEXP: chr "K" "K" "K" "K" ...
## $ CROPDMG : num 0 0 0 0 0 0 0 0 0 0 ...
## $ CROPDMGEXP: chr "" "" "" "" ...
This reads only the 7 columns needed for the analysis below, which is far lighter on memory than loading the full 37-column, 902,297-row dataset and subsetting afterward.
The raw EVTYPE field has 985 distinct values, far more
than the ~48 official event categories in the NOAA documentation,
because of inconsistent capitalization, leading/trailing whitespace, and
a handful of common abbreviations. A full remapping to the official 48
categories is beyond the scope of this assignment, but three simple,
justifiable standardizations are applied so that obviously identical
event types are not split into separate categories in the results
below:
TSTM with
THUNDERSTORM (e.g. TSTM WIND and
THUNDERSTORM WIND are the same event type).THUNDERSTORM WIND
and THUNDERSTORM WINDS.storm$EVTYPE <- trimws(toupper(storm$EVTYPE))
storm$EVTYPE <- gsub("TSTM", "THUNDERSTORM", storm$EVTYPE)
storm$EVTYPE <- gsub("THUNDERSTORM WINDS", "THUNDERSTORM WIND", storm$EVTYPE)
length(unique(storm$EVTYPE))
## [1] 874
PROPDMG/CROPDMG give a damage amount, and
PROPDMGEXP/CROPDMGEXP give a magnitude code
for that amount. Following the commonly used interpretation of these
codes (consistent with the National Weather Service instructions for
this field): K/k = thousands,
M/m = millions, B/b
= billions, a digit 0-9 is treated as a power-of-ten
exponent, and any other symbol (blank, -, +,
?) is treated as no multiplier (i.e. a multiplier of 1 for
+/blank and 0 for -/?, since
those do not correspond to a usable magnitude). This lets us convert the
coded amounts into actual dollar figures.
exp_to_multiplier <- function(e) {
e <- toupper(trimws(e))
ifelse(e == "K", 1e3,
ifelse(e == "M", 1e6,
ifelse(e == "B", 1e9,
ifelse(e %in% as.character(0:9), 10^as.numeric(e),
ifelse(e == "+", 1,
0)))))
}
storm$PROP_MULT <- exp_to_multiplier(storm$PROPDMGEXP)
## Warning in ifelse(e %in% as.character(0:9), 10^as.numeric(e), ifelse(e == : NAs
## introduced by coercion
storm$CROP_MULT <- exp_to_multiplier(storm$CROPDMGEXP)
## Warning in ifelse(e %in% as.character(0:9), 10^as.numeric(e), ifelse(e == : NAs
## introduced by coercion
storm$PROP_DAMAGE <- storm$PROPDMG * storm$PROP_MULT
storm$CROP_DAMAGE <- storm$CROPDMG * storm$CROP_MULT
storm$TOTAL_DAMAGE <- storm$PROP_DAMAGE + storm$CROP_DAMAGE
The dataset is now ready for aggregation by event type to answer the two questions posed in this assignment.
For each event type we sum FATALITIES and
INJURIES across all recorded events, then look at the 10
event types with the highest totals for each.
fatalities_by_type <- aggregate(FATALITIES ~ EVTYPE, data = storm, FUN = sum)
injuries_by_type <- aggregate(INJURIES ~ EVTYPE, data = storm, FUN = sum)
top_fatalities <- head(fatalities_by_type[order(-fatalities_by_type$FATALITIES), ], 10)
top_injuries <- head(injuries_by_type[order(-injuries_by_type$INJURIES), ], 10)
top_fatalities
## EVTYPE FATALITIES
## 757 TORNADO 5633
## 108 EXCESSIVE HEAT 1903
## 130 FLASH FLOOD 978
## 234 HEAT 937
## 409 LIGHTNING 816
## 676 THUNDERSTORM WIND 701
## 146 FLOOD 470
## 514 RIP CURRENT 368
## 311 HIGH WIND 248
## 11 AVALANCHE 224
top_injuries
## EVTYPE INJURIES
## 757 TORNADO 91346
## 676 THUNDERSTORM WIND 9353
## 146 FLOOD 6789
## 108 EXCESSIVE HEAT 6525
## 409 LIGHTNING 5230
## 234 HEAT 2100
## 378 ICE STORM 1975
## 130 FLASH FLOOD 1777
## 203 HAIL 1361
## 864 WINTER STORM 1321
par(mfrow = c(1, 2), mar = c(8, 4, 3, 1))
barplot(top_fatalities$FATALITIES,
names.arg = top_fatalities$EVTYPE,
las = 2, cex.names = 0.7, col = "firebrick",
main = "Top 10 Event Types by Total Fatalities",
ylab = "Total fatalities (1950-2011)")
barplot(top_injuries$INJURIES,
names.arg = top_injuries$EVTYPE,
las = 2, cex.names = 0.7, col = "darkorange",
main = "Top 10 Event Types by Total Injuries",
ylab = "Total injuries (1950-2011)")
Figure 1. Total fatalities (left) and total injuries (right) summed across all recorded events for each of the ten most harmful event types, 1950-2011. Tornadoes cause by far the largest number of both fatalities and injuries of any event type in the database, followed by excessive heat for fatalities and thunderstorm wind/flood-related events for injuries.
For each event type we sum the converted property damage, crop damage, and their total, then look at the 10 event types with the highest total economic damage.
damage_by_type <- aggregate(cbind(PROP_DAMAGE, CROP_DAMAGE, TOTAL_DAMAGE) ~ EVTYPE,
data = storm, FUN = sum)
top_damage <- head(damage_by_type[order(-damage_by_type$TOTAL_DAMAGE), ], 10)
# Express in billions of dollars for readability
top_damage$PROP_DAMAGE_B <- top_damage$PROP_DAMAGE / 1e9
top_damage$CROP_DAMAGE_B <- top_damage$CROP_DAMAGE / 1e9
top_damage$TOTAL_DAMAGE_B <- top_damage$TOTAL_DAMAGE / 1e9
top_damage[, c("EVTYPE", "PROP_DAMAGE_B", "CROP_DAMAGE_B", "TOTAL_DAMAGE_B")]
## EVTYPE PROP_DAMAGE_B CROP_DAMAGE_B TOTAL_DAMAGE_B
## 146 FLOOD 144.657710 5.6619684 150.31968
## 363 HURRICANE/TYPHOON 69.305840 2.6078728 71.91371
## 757 TORNADO 56.947381 0.4149533 57.36233
## 589 STORM SURGE 43.323536 0.0000050 43.32354
## 203 HAIL 15.735267 3.0259545 18.76122
## 130 FLASH FLOOD 16.822724 1.4213171 18.24404
## 76 DROUGHT 1.046106 13.9725660 15.01867
## 354 HURRICANE 11.868319 2.7419100 14.61023
## 676 THUNDERSTORM WIND 9.920829 1.1595052 11.08033
## 519 RIVER FLOOD 5.118945 5.0294590 10.14840
plot_matrix <- t(as.matrix(top_damage[, c("PROP_DAMAGE_B", "CROP_DAMAGE_B")]))
colnames(plot_matrix) <- top_damage$EVTYPE
par(mar = c(8, 4, 3, 1))
barplot(plot_matrix,
names.arg = top_damage$EVTYPE,
las = 2, cex.names = 0.7,
col = c("steelblue", "seagreen"),
legend.text = c("Property damage", "Crop damage"),
args.legend = list(x = "topright", bty = "n"),
main = "Top 10 Event Types by Total Economic Damage",
ylab = "Damage (billions of USD)")
Figure 2. Total property damage and crop damage (stacked, in billions of USD) summed across all recorded events for each of the ten costliest event types, 1950-2011. Floods cause the greatest total economic damage of any event type, driven overwhelmingly by property damage, followed by hurricanes/typhoons and tornadoes. Crop damage is comparatively small for most event types except drought, which is a much larger share of crop losses than of property losses.
Across the United States from 1950 to 2011, tornadoes are the single most harmful event type to population health, causing the largest number of both fatalities and injuries, while floods have caused the greatest total economic damage, with hurricanes/typhoons and tornadoes also ranking among the most economically destructive event types. A government or municipal manager prioritizing resources for severe weather preparedness should treat tornado response and flood mitigation as the two highest priorities under this analysis.