This report uses the U.S. National Oceanic and Atmospheric
Administration’s (NOAA) storm database, covering 1950-2011, to identify
which types of severe weather events are most harmful to population
health and which have the greatest economic consequences. Population
health impact is measured as the sum of fatalities and injuries per
event type; economic impact is measured as the sum of property and crop
damage (converted from NOAA’s magnitude-code representation into
dollars) per event type. Because the database’s raw EVTYPE
field contains hundreds of inconsistent free-text variants of the same
underlying event (e.g. “TSTM WIND” and “THUNDERSTORM WIND”), event types
are first normalized to the closest match among the 48 official NWS
event categories before aggregating. The analysis finds that
tornadoes are by far the most harmful event type to
population health, and that floods, followed by
hurricanes/typhoons, cause the greatest economic
damage.
The analysis starts from the raw, compressed CSV file provided for
this assignment, StormData.csv.bz2. read.csv()
can read a .bz2 file directly without a separate
decompression step.
storm <- read.csv("StormData.csv.bz2", stringsAsFactors = FALSE)
dim(storm)
## [1] 902297 37
Only the columns needed to answer the two questions are kept: the
event type, the health-impact columns (FATALITIES,
INJURIES), and the economic-impact columns
(PROPDMG/PROPDMGEXP for property damage and
CROPDMG/CROPDMGEXP for crop damage).
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
storm_sub <- storm %>%
select(EVTYPE, FATALITIES, INJURIES, PROPDMG, PROPDMGEXP,
CROPDMG, CROPDMGEXP)
PROPDMGEXP and CROPDMGEXP store a magnitude
code alongside the numeric PROPDMG/CROPDMG
value: K/k = thousands,
M/m = millions, B/b
= billions, H/h = hundreds, and a digit
0-8 means a power of ten
(10^digit). Any other code (blank, -,
+, ?) is treated as a multiplier of 1,
i.e. the raw number is used as-is; these cases are rare (only 76 of the
239,174 rows with nonzero property damage have a blank exponent) so this
choice has a negligible effect on the results.
exp_to_multiplier <- function(exp_code) {
exp_code <- toupper(trimws(exp_code))
multiplier <- rep(1, length(exp_code))
multiplier[exp_code == "H"] <- 1e2
multiplier[exp_code == "K"] <- 1e3
multiplier[exp_code == "M"] <- 1e6
multiplier[exp_code == "B"] <- 1e9
digit_idx <- grepl("^[0-8]$", exp_code)
multiplier[digit_idx] <- 10 ^ as.numeric(exp_code[digit_idx])
multiplier
}
storm_sub <- storm_sub %>%
mutate(
prop_damage_dollars = PROPDMG * exp_to_multiplier(PROPDMGEXP),
crop_damage_dollars = CROPDMG * exp_to_multiplier(CROPDMGEXP),
econ_damage_dollars = prop_damage_dollars + crop_damage_dollars,
health_impact = FATALITIES + INJURIES
)
The raw EVTYPE field has 985 distinct values because of
inconsistent capitalization, abbreviations, and free-text entry
(e.g. “TSTM WIND”, “THUNDERSTORM WINDS”, and “THUNDERSTORM WIND” are all
the same underlying event). Each raw value is mapped to the closest
match among the 48 official NWS storm event categories, using keyword
matching on the uppercased, trimmed text. Event types that don’t match
any known keyword (a long tail of very rare or ambiguous entries) are
grouped into OTHER.
storm_sub$evtype_clean <- toupper(trimws(storm_sub$EVTYPE))
classify_event <- function(x) {
result <- rep(NA_character_, length(x))
assign_if_match <- function(pattern, label) {
idx <- is.na(result) & grepl(pattern, x)
result[idx] <<- label
}
assign_if_match("TORNADO|FUNNEL", "Tornado")
assign_if_match("HURRICANE|TYPHOON", "Hurricane/Typhoon")
assign_if_match("TROPICAL STORM", "Tropical Storm")
assign_if_match("STORM SURGE|TIDAL FLOOD", "Storm Surge/Tide")
assign_if_match("TSTM|THUNDERSTORM|THUNDERSTORMW|SEVERE THUNDERSTORM", "Thunderstorm Wind")
assign_if_match("HIGH WIND|WIND DAMAGE|STRONG WIND|GUSTY WIND", "High Wind")
assign_if_match("^WIND$", "High Wind")
assign_if_match("FLASH FLOOD", "Flash Flood")
assign_if_match("COASTAL FLOOD|LAKESHORE FLOOD|BEACH FLOOD|EROSION", "Coastal Flood")
assign_if_match("FLOOD|FLD|HIGH WATER", "Flood")
assign_if_match("EXCESSIVE HEAT|EXTREME HEAT", "Excessive Heat")
assign_if_match("HEAT", "Heat")
assign_if_match("EXTREME COLD|EXTREME WIND CHILL", "Extreme Cold/Wind Chill")
assign_if_match("COLD|WIND CHILL", "Cold/Wind Chill")
assign_if_match("FROST|FREEZE", "Frost/Freeze")
assign_if_match("BLIZZARD", "Blizzard")
assign_if_match("WINTER STORM", "Winter Storm")
assign_if_match("WINTER WEATHER|WINTRY MIX|LIGHT SNOW", "Winter Weather")
assign_if_match("HEAVY SNOW|EXCESSIVE SNOW", "Heavy Snow")
assign_if_match("ICE STORM|ICY ROADS|GLAZE", "Ice Storm")
assign_if_match("SLEET", "Sleet")
assign_if_match("LAKE-EFFECT SNOW|LAKE EFFECT SNOW", "Lake-Effect Snow")
assign_if_match("HAIL", "Hail")
assign_if_match("HEAVY RAIN|HVY RAIN|EXCESSIVE RAIN|RAIN", "Heavy Rain")
assign_if_match("LIGHTNING|LIGHTING|LIGNTNING", "Lightning")
assign_if_match("RIP CURRENT", "Rip Current")
assign_if_match("HIGH SURF|HEAVY SURF|ROUGH SEAS|HIGH SEAS|HIGH WAVES", "High Surf")
assign_if_match("RIVER FLOOD", "Flood")
assign_if_match("DROUGHT|DRY", "Drought")
assign_if_match("WILD.?FIRE|FOREST FIRE", "Wildfire")
assign_if_match("DUST STORM|DUST DEVIL|BLOWING DUST", "Dust Storm")
assign_if_match("WATERSPOUT", "Waterspout")
assign_if_match("AVALANCHE|AVALANCE", "Avalanche")
assign_if_match("DENSE FOG|^FOG$", "Dense Fog")
assign_if_match("DENSE SMOKE", "Dense Smoke")
assign_if_match("DEBRIS FLOW|LANDSLIDE|MUD ?SLIDE|ROCK SLIDE", "Debris Flow")
assign_if_match("VOLCANIC", "Volcanic Ash")
assign_if_match("SEICHE", "Seiche")
assign_if_match("TSUNAMI", "Tsunami")
assign_if_match("MARINE HAIL", "Marine Hail")
assign_if_match("MARINE THUNDERSTORM|MARINE TSTM", "Marine Thunderstorm Wind")
assign_if_match("MARINE HIGH WIND|MARINE STRONG WIND", "Marine High Wind")
assign_if_match("ASTRONOMICAL LOW TIDE|LOW TIDE", "Astronomical Low Tide")
assign_if_match("HURRICANE", "Hurricane/Typhoon")
result[is.na(result)] <- "Other"
result
}
storm_sub$event_category <- classify_event(storm_sub$evtype_clean)
# How much of the total impact ends up in the catch-all "Other" bucket,
# as a sanity check on how well the cleanup covers the data.
other_share_health <- sum(storm_sub$health_impact[storm_sub$event_category == "Other"]) /
sum(storm_sub$health_impact)
other_share_econ <- sum(storm_sub$econ_damage_dollars[storm_sub$event_category == "Other"]) /
sum(storm_sub$econ_damage_dollars)
round(c(other_share_health = other_share_health, other_share_econ = other_share_econ), 4)
## other_share_health other_share_econ
## 0.0028 0.0005
The “Other” catch-all category accounts for only a small fraction of total health impact and economic damage (see figures above), confirming that the keyword-based cleanup captures the events that actually matter for answering the two questions below.
Total fatalities and injuries are summed by cleaned event category, and the top 10 categories are plotted.
library(ggplot2)
health_by_event <- storm_sub %>%
group_by(event_category) %>%
summarise(
fatalities = sum(FATALITIES),
injuries = sum(INJURIES),
total_health_impact = sum(health_impact),
.groups = "drop"
) %>%
arrange(desc(total_health_impact)) %>%
slice_head(n = 10)
health_by_event
## # A tibble: 10 × 4
## event_category fatalities injuries total_health_impact
## <chr> <dbl> <dbl> <dbl>
## 1 Tornado 5661 91410 97071
## 2 Thunderstorm Wind 729 9544 10273
## 3 Excessive Heat 2018 6680 8698
## 4 Flood 515 6873 7388
## 5 Lightning 817 5231 6048
## 6 Heat 1120 2544 3664
## 7 Flash Flood 1035 1802 2837
## 8 High Wind 450 1951 2401
## 9 Ice Storm 101 2237 2338
## 10 Wildfire 90 1606 1696
ggplot(health_by_event,
aes(x = reorder(event_category, total_health_impact),
y = total_health_impact)) +
geom_col(fill = "firebrick") +
coord_flip() +
labs(title = "Top 10 Event Types by Population Health Impact",
x = "Event type",
y = "Total fatalities + injuries (1950-2011)") +
theme_minimal()
Tornadoes cause by far the greatest total harm to population health, with total fatalities and injuries far exceeding every other event category, followed by excessive heat and thunderstorm wind events.
Total property and crop damage (in dollars) are summed by cleaned event category, and the top 10 categories are plotted.
econ_by_event <- storm_sub %>%
group_by(event_category) %>%
summarise(
property_damage = sum(prop_damage_dollars),
crop_damage = sum(crop_damage_dollars),
total_econ_damage = sum(econ_damage_dollars),
.groups = "drop"
) %>%
arrange(desc(total_econ_damage)) %>%
slice_head(n = 10)
econ_by_event
## # A tibble: 10 × 4
## event_category property_damage crop_damage total_econ_damage
## <chr> <dbl> <dbl> <dbl>
## 1 Flood 150224344329 10856294050 161080638379
## 2 Hurricane/Typhoon 85356410010 5516117800 90872527810
## 3 Tornado 58603517526. 417461520 59020979046.
## 4 Storm Surge/Tide 47964737000 855000 47965592000
## 5 Flash Flood 17588292096. 1532197150 19120489246.
## 6 Hail 15977544513. 3046887623 19024432136.
## 7 Drought 1052838600 13972581000 15025419600
## 8 Thunderstorm Wind 11184748700. 1271708988 12456457688.
## 9 Ice Storm 3947673560 5022114300 8969787860
## 10 Wildfire 8496563500 403281630 8899845130
ggplot(econ_by_event,
aes(x = reorder(event_category, total_econ_damage),
y = total_econ_damage / 1e9)) +
geom_col(fill = "steelblue") +
coord_flip() +
labs(title = "Top 10 Event Types by Economic Damage",
x = "Event type",
y = "Total property + crop damage (billions of dollars, 1950-2011)") +
theme_minimal()
Floods cause the greatest total economic damage, driven primarily by property damage, followed closely by hurricanes/typhoons, which cause substantial damage to both property and crops.
Across the full 1950-2011 U.S. NOAA storm record, tornadoes are the single most dangerous event type for population health, while floods and hurricanes/typhoons cause the largest economic losses. These findings are broadly consistent with public knowledge of severe weather impacts in the United States, and can help guide resource prioritization: population health preparedness resources are most valuable for tornado warning and response, while economic mitigation and infrastructure resilience efforts are best targeted at flood and hurricane risk.