This analysis examines the NOAA Storm Database to identify which weather events are most harmful to public health and which have the greatest economic consequences. The data covers storm events from 1950 to November 2011 across the United States. Tornadoes are found to cause the most fatalities and injuries, making them the greatest threat to public health. Floods cause the most property damage, while droughts cause the most crop damage. These findings can help government and municipal managers prioritize resources for different types of severe weather events.
This section describes how the data is loaded and processed for analysis.
# Load required packages
library(ggplot2)
# Download data if not already present
if(!file.exists("StormData.csv.bz2")) {
download.file(
"https://d396qusza40orc.cloudfront.net/repdata%2Fdata%2FStormData.csv.bz2",
"StormData.csv.bz2"
)
}
# Read the compressed CSV file
data <- read.csv("StormData.csv.bz2")
# Check basic structure
str(data)
## 'data.frame': 902297 obs. of 37 variables:
## $ STATE__ : num 1 1 1 1 1 1 1 1 1 1 ...
## $ BGN_DATE : chr "4/18/1950 0:00:00" "4/18/1950 0:00:00" "2/20/1951 0:00:00" "6/8/1951 0:00:00" ...
## $ BGN_TIME : chr "0130" "0145" "1600" "0900" ...
## $ TIME_ZONE : chr "CST" "CST" "CST" "CST" ...
## $ COUNTY : num 97 3 57 89 43 77 9 123 125 57 ...
## $ COUNTYNAME: chr "MOBILE" "BALDWIN" "FAYETTE" "MADISON" ...
## $ STATE : chr "AL" "AL" "AL" "AL" ...
## $ EVTYPE : chr "TORNADO" "TORNADO" "TORNADO" "TORNADO" ...
## $ BGN_RANGE : num 0 0 0 0 0 0 0 0 0 0 ...
## $ BGN_AZI : chr "" "" "" "" ...
## $ BGN_LOCATI: chr "" "" "" "" ...
## $ END_DATE : chr "" "" "" "" ...
## $ END_TIME : chr "" "" "" "" ...
## $ COUNTY_END: num 0 0 0 0 0 0 0 0 0 0 ...
## $ COUNTYENDN: logi NA NA NA NA NA NA ...
## $ END_RANGE : num 0 0 0 0 0 0 0 0 0 0 ...
## $ END_AZI : chr "" "" "" "" ...
## $ END_LOCATI: chr "" "" "" "" ...
## $ LENGTH : num 14 2 0.1 0 0 1.5 1.5 0 3.3 2.3 ...
## $ WIDTH : num 100 150 123 100 150 177 33 33 100 100 ...
## $ F : int 3 2 2 2 2 2 2 1 3 3 ...
## $ MAG : num 0 0 0 0 0 0 0 0 0 0 ...
## $ 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 "" "" "" "" ...
## $ WFO : chr "" "" "" "" ...
## $ STATEOFFIC: chr "" "" "" "" ...
## $ ZONENAMES : chr "" "" "" "" ...
## $ LATITUDE : num 3040 3042 3340 3458 3412 ...
## $ LONGITUDE : num 8812 8755 8742 8626 8642 ...
## $ LATITUDE_E: num 3051 0 0 0 0 ...
## $ LONGITUDE_: num 8806 0 0 0 0 ...
## $ REMARKS : chr "" "" "" "" ...
## $ REFNUM : num 1 2 3 4 5 6 7 8 9 10 ...
Focus on key variables: EVTYPE, FATALITIES, INJURIES, PROPDMG, PROPDMGEXP, CROPDMG, CROPDMGEXP.
# Keep relevant columns
data_clean <- data[, c("EVTYPE", "FATALITIES", "INJURIES",
"PROPDMG", "PROPDMGEXP", "CROPDMG", "CROPDMGEXP")]
# Convert damage amounts to actual dollar values
convert_damage <- function(value, exponent) {
exponent <- toupper(exponent)
multiplier <- ifelse(exponent == "K", 1000,
ifelse(exponent == "M", 1000000,
ifelse(exponent == "B", 1000000000, 1)))
return(value * multiplier)
}
data_clean$PROPDMG_VALUE <- mapply(convert_damage, data_clean$PROPDMG, data_clean$PROPDMGEXP)
data_clean$CROPDMG_VALUE <- mapply(convert_damage, data_clean$CROPDMG, data_clean$CROPDMGEXP)
# Get unique event types
event_types <- unique(data_clean$EVTYPE)
# Initialize summary data frame
total_fatalities <- numeric(length(event_types))
total_injuries <- numeric(length(event_types))
total_prop_damage <- numeric(length(event_types))
total_crop_damage <- numeric(length(event_types))
total_health <- numeric(length(event_types))
# Calculate totals for each event type
for(i in 1:length(event_types)) {
event <- event_types[i]
subset_data <- data_clean[data_clean$EVTYPE == event, ]
total_fatalities[i] <- sum(subset_data$FATALITIES, na.rm = TRUE)
total_injuries[i] <- sum(subset_data$INJURIES, na.rm = TRUE)
total_prop_damage[i] <- sum(subset_data$PROPDMG_VALUE, na.rm = TRUE)
total_crop_damage[i] <- sum(subset_data$CROPDMG_VALUE, na.rm = TRUE)
total_health[i] <- total_fatalities[i] + total_injuries[i]
}
# Create summary data frame
damage_by_event <- data.frame(
EVTYPE = event_types,
total_fatalities = total_fatalities,
total_injuries = total_injuries,
total_prop_damage = total_prop_damage,
total_crop_damage = total_crop_damage,
total_health = total_health
)
# Sort by health impact
damage_by_event <- damage_by_event[order(-damage_by_event$total_health), ]
# Top 10 by health impact
top_health <- damage_by_event[1:10, c("EVTYPE", "total_fatalities", "total_injuries", "total_health")]
# Rename columns for display
colnames(top_health) <- c("Event Type", "Fatalities", "Injuries", "Total")
# Print table
knitr::kable(top_health,
caption = "Top 10 Weather Events by Total Health Impact")
| Event Type | Fatalities | Injuries | Total | |
|---|---|---|---|---|
| 1 | TORNADO | 5633 | 91346 | 96979 |
| 99 | EXCESSIVE HEAT | 1903 | 6525 | 8428 |
| 2 | TSTM WIND | 504 | 6957 | 7461 |
| 36 | FLOOD | 470 | 6789 | 7259 |
| 15 | LIGHTNING | 816 | 5230 | 6046 |
| 27 | HEAT | 937 | 2100 | 3037 |
| 20 | FLASH FLOOD | 978 | 1777 | 2755 |
| 65 | ICE STORM | 89 | 1975 | 2064 |
| 16 | THUNDERSTORM WIND | 133 | 1488 | 1621 |
| 8 | WINTER STORM | 206 | 1321 | 1527 |
# Prepare data for plotting
health_plot_data <- data.frame(
EVTYPE = rep(top_health$`Event Type`, 2),
Count = c(top_health$Fatalities, top_health$Injuries),
Impact_Type = rep(c("Fatalities", "Injuries"), each = 10)
)
# Create bar plot
ggplot(health_plot_data, aes(x = reorder(EVTYPE, -Count), y = Count, fill = Impact_Type)) +
geom_bar(stat = "identity", position = "dodge") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
labs(title = "Top 10 Weather Events by Health Impact",
x = "Event Type",
y = "Number of Casualties",
fill = "Impact Type") +
scale_fill_manual(values = c("red", "orange"))
# Calculate total economic impact
damage_by_event$total_economic <- damage_by_event$total_prop_damage + damage_by_event$total_crop_damage
# Sort by economic impact
damage_by_event <- damage_by_event[order(-damage_by_event$total_economic), ]
# Top 10 by economic impact
top_economic <- damage_by_event[1:10, c("EVTYPE", "total_prop_damage", "total_crop_damage")]
# Rename columns for display
colnames(top_economic) <- c("Event Type", "Property Damage", "Crop Damage")
# Print table
knitr::kable(top_economic,
caption = "Top 10 Weather Events by Economic Impact (USD)")
| Event Type | Property Damage | Crop Damage | |
|---|---|---|---|
| 36 | FLOOD | 144657709807 | 5661968450 |
| 973 | HURRICANE/TYPHOON | 69305840000 | 2607872800 |
| 1 | TORNADO | 56937160779 | 414953270 |
| 204 | STORM SURGE | 43323536000 | 5000 |
| 3 | HAIL | 15732267048 | 3025954473 |
| 20 | FLASH FLOOD | 16140812067 | 1421317100 |
| 194 | DROUGHT | 1046106000 | 13972566000 |
| 226 | HURRICANE | 11868319010 | 2741910000 |
| 52 | RIVER FLOOD | 5118945500 | 5029459000 |
| 65 | ICE STORM | 3944927860 | 5022113500 |
# Prepare data for plotting
economic_plot_data <- data.frame(
EVTYPE = rep(top_economic$`Event Type`, 2),
Amount = c(top_economic$`Property Damage`, top_economic$`Crop Damage`),
Damage_Type = rep(c("Property Damage", "Crop Damage"), each = 10)
)
# Create bar plot
ggplot(economic_plot_data, aes(x = reorder(EVTYPE, -Amount), y = Amount/1e9, fill = Damage_Type)) +
geom_bar(stat = "identity", position = "dodge") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
labs(title = "Top 10 Weather Events by Economic Impact",
x = "Event Type",
y = "Damage Amount (in Billions USD)",
fill = "Damage Type") +
scale_fill_manual(values = c("blue", "green"))
Public Health: Tornadoes are the most dangerous weather event, causing the highest number of fatalities and injuries.
Economic Impact: Floods cause the most property damage, while droughts cause the most crop damage. Floods have the highest total economic impact overall.
Recommendations: Resources should be prioritized for tornado preparedness and flood prevention to minimize both health and economic impacts.