Synopsis

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.

Data Processing

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)

Converting damage magnitude codes to dollar amounts

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
    )

Normalizing event types

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.

Results

Which types of events are most harmful to population health?

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()
Figure 1: Top 10 event types by total fatalities and injuries (population health impact), summed across all U.S. events from 1950-2011.
Figure 1: Top 10 event types by total fatalities and injuries (population health impact), summed across all U.S. events from 1950-2011.

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.

Which types of events have the greatest economic consequences?

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()
Figure 2: Top 10 event types by total economic damage (property + crop damage, in billions of dollars), summed across all U.S. events from 1950-2011.
Figure 2: Top 10 event types by total economic damage (property + crop damage, in billions of dollars), summed across all U.S. events from 1950-2011.

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.

Conclusion

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.