Synopsis: This analysis explores the NOAA Storm Database and aims to identify the most severe weather events both in terms of injuries, fatalities and the economic damage caused across the United States. The raw ‘EVTYPE’ field contains many inconsistent and overlapping labels which were cleaned and consolidated in to eight categories: Tornado, Flood, Thunderstorm Wind, Lightning, Precipitation, Wind and a composite group called Extreme Weather Events- the consolidation was done by pattern matching on standardised weather descriptions. Marine and Land-based thunderstorm wind events were kept separate, since these may have different practical implications. Economic damage was determined by converting the property and crop damage magnitude and exponent fields in to actual dollar values. Results show that Tornadoes are by far the leading cause of both fatalities and injuries exceeding all other categories. However, when it comes to economic impact this changes and flooding tends to cause the greatest total damage narrowly. But it takes only a narrow lead over the broader Extreme Weather Events category, with Tornadoes and Precipitation-related events (such as rain or hail) following behind. These findings suggest tornado preparedness should be prioritized for protecting public health, while flood mitigation infrastructure offers the greatest potential for the reduction of economic losses.
Load the required libraries
library(ggplot2)
library(reshape2)
The raw data was obtained from the NOAA Storm Data-base, it was downloaded programatically (using the ‘download.file()’ function) in order to ensure reproducibility and loaded into R using the read.csv function. The dataset contains 902,397 storm event records and 37 variables.
download.file('https://d396qusza40orc.cloudfront.net/repdata%2Fdata%2FStormData.csv.bz2', method = 'curl', destfile = 'repdata_data_StormData.csv.bz2')
sddata <- read.csv('repdata_data_StormData.csv.bz2')
Here, the ‘EVTYPE’ field was converted into upper-case for easy analysis and all whitespaces were trimmed Processing the data
sddata$EVTYPE <- toupper(sddata$EVTYPE) # Convert the 'EVTYPE' column of data-set to uppercase
sddata$EVTYPE <- gsub(" ", "", sddata$EVTYPE) # Trim all white-spaces
# Initialize the 'EVENT_CATEGORY' column
sddata$EVENT_CATEGORY <- NA
The EVTYPE variable, which records the type of weather event contained inconsistent, misspelled labels (‘TSTMWIND’,‘THUNDERSTORMWINDS’ and ‘THUNDERSTORMWIND’) all describing the same phenomenon. This problem was addressed by converting EVTYPE to uppercase and trimming all white-spaces, which were then consolidated into a smaller set of standardized categories using pattern matching. Events were classified in order into: Thunderstorm Wind, Marine Thunderstorm Wind, Lightning, Tornado, Flood, Precipitation, and a broad category called Extreme Weather Event, and Wind (remaining non-thunderstorm-related). Any event not matching one of these patterns was into a residual ‘Other’ category. This categorization scheme groups events primarily by the underlying phenomenon, with location used where the documentation treats it as a meaningfully distinct event type. Classify the events into categories
sddata$EVENT_CATEGORY[grepl('THUNDERSTORM|TSTM', sddata$EVTYPE) & !grepl("MARINE", sddata$EVTYPE)] <- "Thunderstorm Wind"
sddata$EVENT_CATEGORY[grepl('THUNDERSTORM|TSTM', sddata$EVTYPE) & grepl("MARINE", sddata$EVTYPE)] <- "Marine Thunderstorm Wind"
sddata$EVENT_CATEGORY[sddata$EVTYPE == "LIGHTNING"] <- "Lightning"
sddata$EVENT_CATEGORY[sddata$EVTYPE == "TORNADO"] <- "Tornado"
sddata$EVENT_CATEGORY[grepl("FLOOD", sddata$EVTYPE) & is.na(sddata$EVENT_CATEGORY)] <- "Flood"
sddata$EVENT_CATEGORY[grepl('HAIL|RAIN|SNOW|SLEET', sddata$EVTYPE) & is.na(sddata$EVENT_CATEGORY)] <- 'Precipitation'
sddata$EVENT_CATEGORY[grepl("HURRICANE|TYPHOON|TROPICALSTORM|TROPICALDEPRESSION|BLIZZARD|ICESTORM|WINTERSTORM|EXTREMEHEAT|EXCESSIVEHEAT|EXTREMECOLD|DROUGHT|WILDFIRE|FIRE", sddata$EVTYPE) & is.na(sddata$EVENT_CATEGORY)] <- "Extreme Weather Events"
sddata$EVENT_CATEGORY[grepl("WIND", sddata$EVTYPE) & is.na(sddata$EVENT_CATEGORY)] <- "Wind"
sddata$EVENT_CATEGORY[is.na(sddata$EVENT_CATEGORY)] <- "Other"
To assess the economic impact of the above events, the ‘PROPDMGEXP’ and the ‘CROPDMGEXP’ fields, which encode a multiplier for the corresponding damage magnitude fields were standardized to uppercase and mapped to their numeric multipliers. A smaller number of rows contained undocumented or ambiguous exponent codes, these represented well under one percent of the data-set and were treated conservatively. Property and crop damage were each multiplied by their respective standardized components and summed to produce a total economic damage figure for each event
# Clean and standardize EXP codes
sddata$PROPDMGEXP <- toupper(trimws(sddata$PROPDMGEXP))
sddata$CROPDMGEXP <- toupper(trimws(sddata$CROPDMGEXP))
# Property damage multiplier
sddata$PROP_MULT <- 1
sddata$PROP_MULT[sddata$PROPDMGEXP == "H"] <- 1e2
sddata$PROP_MULT[sddata$PROPDMGEXP == "K"] <- 1e3
sddata$PROP_MULT[sddata$PROPDMGEXP == "M"] <- 1e6
sddata$PROP_MULT[sddata$PROPDMGEXP == "B"] <- 1e9
digit_rows_prop <- grepl("^[0-8]$", sddata$PROPDMGEXP)
sddata$PROP_MULT[digit_rows_prop] <- 10^as.numeric(sddata$PROPDMGEXP[digit_rows_prop])
sddata$PROP_MULT[!(sddata$PROPDMGEXP %in% c("", "H","K","M","B","0","1","2","3","4","5","6","7","8"))] <- 0
sddata$PROP_DAMAGE <- sddata$PROPDMG * sddata$PROP_MULT
# Crop damage multiplier
sddata$CROP_MULT <- 1
sddata$CROP_MULT[sddata$CROPDMGEXP == "H"] <- 1e2
sddata$CROP_MULT[sddata$CROPDMGEXP == "K"] <- 1e3
sddata$CROP_MULT[sddata$CROPDMGEXP == "M"] <- 1e6
sddata$CROP_MULT[sddata$CROPDMGEXP == "B"] <- 1e9
digit_rows_crop <- grepl("^[0-8]$", sddata$CROPDMGEXP)
sddata$CROP_MULT[digit_rows_crop] <- 10^as.numeric(sddata$CROPDMGEXP[digit_rows_crop])
sddata$CROP_MULT[!(sddata$CROPDMGEXP %in% c("", "H","K","M","B","0","1","2","3","4","5","6","7","8"))] <- 0
sddata$CROP_DAMAGE <- sddata$CROPDMG * sddata$CROP_MULT
# Total economic damage
sddata$TOTAL_DAMAGE <- sddata$PROP_DAMAGE + sddata$CROP_DAMAGE
##Population Health Impact Figure 1 shows total fatalities and injuries aggregated by event category, sorted from most to least harmful. Tornadoes are responsible for the highest number of both fatalities (5,633) and injuries (91,346) by a substantial margin — more than double the fatalities of the next-highest category, the broader Extreme Weather Events group (3,008 fatalities), and over six times the injury count of the next-closest category. Flood, Thunderstorm Wind, and Lightning follow as the next most significant contributors to fatalities, while injury patterns show a similar ordering. These results indicate that tornado preparedness and early-warning systems represent the highest-priority investment for reducing loss of life and injury from severe weather.
Aggregation of fatalities and injuries by category
health_agg <-aggregate(cbind(FATALITIES, INJURIES) ~ EVENT_CATEGORY, data = sddata, sum)
health_agg[order(-health_agg$FATALITIES),]
## EVENT_CATEGORY FATALITIES INJURIES
## 8 Tornado 5633 91346
## 1 Extreme Weather Events 3007 14426
## 5 Other 2552 6626
## 2 Flood 1525 8604
## 3 Lightning 816 5230
## 7 Thunderstorm Wind 735 9510
## 9 Wind 564 1912
## 6 Precipitation 294 2840
## 4 Marine Thunderstorm Wind 19 34
Aggregation of Total Economic Damage by category
damage_agg <-aggregate(TOTAL_DAMAGE ~ EVENT_CATEGORY, data = sddata, sum)
damage_agg[order(-damage_agg$TOTAL_DAMAGE),]
## EVENT_CATEGORY TOTAL_DAMAGE
## 2 Flood 180591769913
## 1 Extreme Weather Events 141622474931
## 8 Tornado 57362333886
## 5 Other 51546650430
## 6 Precipitation 24238825976
## 7 Thunderstorm Wind 14053055288
## 9 Wind 6965571473
## 3 Lightning 942471520
## 4 Marine Thunderstorm Wind 5907400
For plotting we will use the ggplot2 library Figure 1 Population Health Impact
health_long <- melt(health_agg, id.vars = "EVENT_CATEGORY",
measure.vars = c("FATALITIES", "INJURIES"),
variable.name = "Metric", value.name = "Count")
ggplot(health_long, aes(x = reorder(EVENT_CATEGORY, -Count), y=Count, fill= Metric)) +
geom_bar(stat = "identity") +
facet_wrap(~ Metric, scales = "free_y") +
theme_minimal() +
theme(axis.text = element_text(angle = 45, hjust = 1)) +
labs(title = "Population Health Impact by Event category",
x="Event Category" , y = "Count") +
scale_fill_manual(values = c("FATALITIES" = "firebrick", "INJURIES" = "orange"))
Figure 2 shows total economic damage (property and crop damage combined) by event category, in billions of dollars. Flood events caused the greatest total damage (approximately $180.4 billion), narrowly ahead of the composite Extreme Weather Events category (approximately $141.7 billion), followed by Tornado ($57.4 billion) and the residual ‘Other’ category ($51.7 billion). Precipitation-related events (hail, rain, snow) and Thunderstorm Wind caused comparatively smaller but still notable damage. These findings suggest that flood mitigation and drainage infrastructure investment offers the greatest potential return in reducing future economic losses from severe weather, complementing the population-health case for tornado-focused preparedness.
ggplot(damage_agg, aes(x = reorder(EVENT_CATEGORY, -TOTAL_DAMAGE), y = TOTAL_DAMAGE/1e9)) +
geom_bar(stat = "identity", fill = "steelblue") +
theme_minimal() +
theme(axis.text = element_text(angle = 45, hjust = 1)) +
labs(title = "Total Economic Damage by Event Category",
x="Event Category" , y = "Damage (Billions USD)")