Synopsis

This analysis examines severe weather events recorded in the NOAA Storm Database from 1950 through November 2011. It identifies event types associated with the greatest reported harm to population health and the greatest economic consequences. Fatalities and injuries are used to measure health impacts, while property and crop damage measure economic impacts. The original compressed CSV file is loaded and processed in R. Damage magnitude codes are converted into dollar multipliers, and impacts are aggregated by event type. Two figures present the principal rankings. The results describe historical reported impacts and do not predict future weather events.

Data Processing

1. Load the Original Data

The NOAA Storm Database is downloaded if it is not already available in the working directory.

file_url <- "https://d396qusza40orc.cloudfront.net/repdata%2Fdata%2FStormData.csv.bz2"
data_file <- "repdata_data_StormData.csv.bz2"

if (!file.exists(data_file)) {
  download.file(
    file_url,
    destfile = data_file,
    mode = "wb",
    method = "libcurl"
  )
}

storm <- read.csv(
  bzfile(data_file),
  stringsAsFactors = FALSE
)

cat(
  "Dataset dimensions:",
  nrow(storm), "rows and",
  ncol(storm), "columns\n"
)
## Dataset dimensions: 902297 rows and 37 columns
head(storm)
##   STATE__           BGN_DATE BGN_TIME TIME_ZONE COUNTY COUNTYNAME STATE  EVTYPE
## 1       1  4/18/1950 0:00:00     0130       CST     97     MOBILE    AL TORNADO
## 2       1  4/18/1950 0:00:00     0145       CST      3    BALDWIN    AL TORNADO
## 3       1  2/20/1951 0:00:00     1600       CST     57    FAYETTE    AL TORNADO
## 4       1   6/8/1951 0:00:00     0900       CST     89    MADISON    AL TORNADO
## 5       1 11/15/1951 0:00:00     1500       CST     43    CULLMAN    AL TORNADO
## 6       1 11/15/1951 0:00:00     2000       CST     77 LAUDERDALE    AL TORNADO
##   BGN_RANGE BGN_AZI BGN_LOCATI END_DATE END_TIME COUNTY_END COUNTYENDN
## 1         0                                               0         NA
## 2         0                                               0         NA
## 3         0                                               0         NA
## 4         0                                               0         NA
## 5         0                                               0         NA
## 6         0                                               0         NA
##   END_RANGE END_AZI END_LOCATI LENGTH WIDTH F MAG FATALITIES INJURIES PROPDMG
## 1         0                      14.0   100 3   0          0       15    25.0
## 2         0                       2.0   150 2   0          0        0     2.5
## 3         0                       0.1   123 2   0          0        2    25.0
## 4         0                       0.0   100 2   0          0        2     2.5
## 5         0                       0.0   150 2   0          0        2     2.5
## 6         0                       1.5   177 2   0          0        6     2.5
##   PROPDMGEXP CROPDMG CROPDMGEXP WFO STATEOFFIC ZONENAMES LATITUDE LONGITUDE
## 1          K       0                                         3040      8812
## 2          K       0                                         3042      8755
## 3          K       0                                         3340      8742
## 4          K       0                                         3458      8626
## 5          K       0                                         3412      8642
## 6          K       0                                         3450      8748
##   LATITUDE_E LONGITUDE_ REMARKS REFNUM
## 1       3051       8806              1
## 2          0          0              2
## 3          0          0              3
## 4          0          0              4
## 5          0          0              5
## 6          0          0              6

2. Select and Clean the Required Variables

Only variables required for the research questions are retained. Event names are converted to uppercase and trimmed to combine labels that differ only by capitalization or surrounding spaces. Missing event names are labeled UNKNOWN. Missing numeric values are replaced with zero for this analysis.

required_columns <- c(
  "EVTYPE",
  "FATALITIES",
  "INJURIES",
  "PROPDMG",
  "PROPDMGEXP",
  "CROPDMG",
  "CROPDMGEXP"
)

missing_columns <- setdiff(required_columns, names(storm))

if (length(missing_columns) > 0) {
  stop(
    "Required columns missing: ",
    paste(missing_columns, collapse = ", ")
  )
}

storm <- storm[, required_columns]

storm$EVTYPE <- toupper(trimws(as.character(storm$EVTYPE)))

storm$EVTYPE[
  is.na(storm$EVTYPE) | storm$EVTYPE == ""
] <- "UNKNOWN"

numeric_columns <- c(
  "FATALITIES",
  "INJURIES",
  "PROPDMG",
  "CROPDMG"
)

for (column in numeric_columns) {
  storm[[column]] <- as.numeric(as.character(storm[[column]]))
  storm[[column]][is.na(storm[[column]])] <- 0
}

summary(storm[, numeric_columns])
##    FATALITIES          INJURIES            PROPDMG           CROPDMG       
##  Min.   :  0.0000   Min.   :   0.0000   Min.   :   0.00   Min.   :  0.000  
##  1st Qu.:  0.0000   1st Qu.:   0.0000   1st Qu.:   0.00   1st Qu.:  0.000  
##  Median :  0.0000   Median :   0.0000   Median :   0.00   Median :  0.000  
##  Mean   :  0.0168   Mean   :   0.1557   Mean   :  12.06   Mean   :  1.527  
##  3rd Qu.:  0.0000   3rd Qu.:   0.0000   3rd Qu.:   0.50   3rd Qu.:  0.000  
##  Max.   :583.0000   Max.   :1700.0000   Max.   :5000.00   Max.   :990.000

3. Identify Events Most Harmful to Population Health

Fatalities and injuries are aggregated separately by event type. Their sum is used to rank combined reported health impacts, while the separate counts allow deaths and nonfatal injuries to be examined independently.

health <- aggregate(
  cbind(FATALITIES, INJURIES) ~ EVTYPE,
  data = storm,
  FUN = sum
)

health$TOTAL_IMPACTS <-
  health$FATALITIES + health$INJURIES

health <- health[
  order(
    -health$TOTAL_IMPACTS,
    -health$FATALITIES,
    health$EVTYPE
  ),
]

health_top <- head(health, 10)

knitr::kable(
  health_top,
  row.names = FALSE,
  caption = paste(
    "Ten event types with the highest combined",
    "reported fatalities and injuries"
  ),
  digits = 0
)
Ten event types with the highest combined reported fatalities and injuries
EVTYPE FATALITIES INJURIES TOTAL_IMPACTS
TORNADO 5633 91346 96979
EXCESSIVE HEAT 1903 6525 8428
TSTM WIND 504 6957 7461
FLOOD 470 6789 7259
LIGHTNING 816 5230 6046
HEAT 937 2100 3037
FLASH FLOOD 978 1777 2755
ICE STORM 89 1975 2064
THUNDERSTORM WIND 133 1488 1621
WINTER STORM 206 1321 1527

4. Convert Property and Crop Damage into Dollars

Damage amounts are stored separately from magnitude codes. Common codes include H for hundreds, K for thousands, M for millions, and B for billions. Numeric codes from 0 to 9 are interpreted as powers of ten. Blank codes are assigned a multiplier of one. Unrecognized codes are treated as unknown, and positive-damage records with unknown codes are reported separately.

damage_multiplier <- function(code) {

  code <- toupper(trimws(as.character(code)))

  multiplier <- rep(NA_real_, length(code))

  # Blank and zero codes
  multiplier[
    !is.na(code) & code %in% c("", "0")
  ] <- 1

  # Common letter codes
  multiplier[!is.na(code) & code == "H"] <- 1e2
  multiplier[!is.na(code) & code == "K"] <- 1e3
  multiplier[!is.na(code) & code == "M"] <- 1e6
  multiplier[!is.na(code) & code == "B"] <- 1e9

  # Numeric exponent codes
  numeric_code <- !is.na(code) & grepl("^[0-9]+$", code)

  multiplier[numeric_code] <-
    10^as.numeric(code[numeric_code])

  multiplier
}

prop_multiplier <- damage_multiplier(storm$PROPDMGEXP)
crop_multiplier <- damage_multiplier(storm$CROPDMGEXP)

# Initialize damage values in dollars
storm$PROPDMG_USD <- 0
storm$CROPDMG_USD <- 0

# Convert property damage
positive_property <- storm$PROPDMG > 0

valid_property <- positive_property & !is.na(prop_multiplier)

storm$PROPDMG_USD[valid_property] <-
  storm$PROPDMG[valid_property] *
  prop_multiplier[valid_property]

# Convert crop damage
positive_crop <- storm$CROPDMG > 0

valid_crop <- positive_crop & !is.na(crop_multiplier)

storm$CROPDMG_USD[valid_crop] <-
  storm$CROPDMG[valid_crop] *
  crop_multiplier[valid_crop]

# Count positive-damage records with unknown codes
unknown_property_codes <- sum(
  positive_property & is.na(prop_multiplier)
)

unknown_crop_codes <- sum(
  positive_crop & is.na(crop_multiplier)
)

code_summary <- data.frame(
  Damage_Type = c("Property", "Crop"),
  Positive_Damage_Records_With_Unknown_Codes = c(
    unknown_property_codes,
    unknown_crop_codes
  )
)

knitr::kable(
  code_summary,
  row.names = FALSE,
  caption = "Positive-damage records with unrecognized magnitude codes"
)
Positive-damage records with unrecognized magnitude codes
Damage_Type Positive_Damage_Records_With_Unknown_Codes
Property 6
Crop 0

5. Calculate Economic Impacts by Event Type

Property damage and crop damage are summed separately. Their sum gives the combined reported economic damage used for ranking. Positive-damage records with unrecognized magnitude codes are excluded from the corresponding monetary totals because their values cannot be reliably calculated.

economic <- aggregate(
  cbind(PROPDMG_USD, CROPDMG_USD) ~ EVTYPE,
  data = storm,
  FUN = sum
)

economic$TOTAL_DAMAGE_USD <-
  economic$PROPDMG_USD + economic$CROPDMG_USD

economic <- economic[
  order(
    -economic$TOTAL_DAMAGE_USD,
    -economic$PROPDMG_USD,
    economic$EVTYPE
  ),
]

economic_top <- head(economic, 10)

economic_display <- data.frame(
  Event = economic_top$EVTYPE,
  Property_Damage_Billion_USD =
    economic_top$PROPDMG_USD / 1e9,
  Crop_Damage_Billion_USD =
    economic_top$CROPDMG_USD / 1e9,
  Total_Damage_Billion_USD =
    economic_top$TOTAL_DAMAGE_USD / 1e9
)

knitr::kable(
  economic_display,
  row.names = FALSE,
  digits = 3,
  caption = paste(
    "Ten event types with the greatest combined",
    "reported property and crop damage"
  )
)
Ten event types with the greatest combined reported property and crop damage
Event Property_Damage_Billion_USD Crop_Damage_Billion_USD Total_Damage_Billion_USD
FLOOD 144.658 5.662 150.320
HURRICANE/TYPHOON 69.306 2.608 71.914
TORNADO 56.947 0.415 57.362
STORM SURGE 43.324 0.000 43.324
HAIL 15.735 3.026 18.761
FLASH FLOOD 16.823 1.421 18.244
DROUGHT 1.046 13.973 15.019
HURRICANE 11.868 2.742 14.610
RIVER FLOOD 5.119 5.029 10.148
ICE STORM 3.945 5.022 8.967

Results

1. Events Most Harmful to Population Health

The table above ranks event types by their combined reported fatalities and injuries. The following figure displays the two measures separately to distinguish deaths from nonfatal injuries.

library(ggplot2)
library(scales)

health_long <- rbind(
  data.frame(
    EVTYPE = health_top$EVTYPE,
    Impact = "Fatalities",
    Count = health_top$FATALITIES
  ),
  data.frame(
    EVTYPE = health_top$EVTYPE,
    Impact = "Injuries",
    Count = health_top$INJURIES
  )
)

health_long$EVTYPE <- factor(
  health_long$EVTYPE,
  levels = rev(health_top$EVTYPE)
)

ggplot(
  health_long,
  aes(x = EVTYPE, y = Count, fill = Impact)
) +
  geom_col() +
  coord_flip() +
  scale_y_continuous(labels = scales::comma) +
  labs(
    title = "Weather Events with the Greatest Health Impacts",
    x = "Event type",
    y = "Reported count",
    fill = "Impact"
  ) +
  theme_minimal()
Figure 1. Ten event types with the highest combined reported fatalities and injuries in the NOAA Storm Database.

Figure 1. Ten event types with the highest combined reported fatalities and injuries in the NOAA Storm Database.

The leading event types account for the largest combined reported numbers of fatalities and injuries among the event labels in the database. The separate values should also be considered because fatalities and nonfatal injuries represent different outcomes.

2. Events with the Greatest Economic Consequences

The following figure compares reported property damage and crop damage for the ten event types with the highest combined totals. Monetary values are expressed in billions of U.S. dollars.

economic_long <- rbind(
  data.frame(
    EVTYPE = economic_top$EVTYPE,
    Damage_Type = "Property damage",
    Damage_Billion = economic_top$PROPDMG_USD / 1e9
  ),
  data.frame(
    EVTYPE = economic_top$EVTYPE,
    Damage_Type = "Crop damage",
    Damage_Billion = economic_top$CROPDMG_USD / 1e9
  )
)

economic_long$EVTYPE <- factor(
  economic_long$EVTYPE,
  levels = rev(economic_top$EVTYPE)
)

ggplot(
  economic_long,
  aes(
    x = EVTYPE,
    y = Damage_Billion,
    fill = Damage_Type
  )
) +
  geom_col() +
  coord_flip() +
  scale_y_continuous(labels = scales::label_number()) +
  labs(
    title = "Weather Events with the Greatest Economic Impacts",
    x = "Event type",
    y = "Reported damage (billions of U.S. dollars)",
    fill = "Damage type"
  ) +
  theme_minimal()
Figure 2. Ten event types with the greatest combined reported property and crop damage, expressed in billions of U.S. dollars.

Figure 2. Ten event types with the greatest combined reported property and crop damage, expressed in billions of U.S. dollars.

The economic results identify event types with the largest combined reported property and crop damage totals under the stated magnitude-code assumptions. Displaying the two damage categories separately shows their contribution to the overall total.

3. Conclusion

This analysis examines two important dimensions of severe weather impacts in the United States. Population health impacts are measured through reported fatalities and injuries, while economic impacts are measured through reported property and crop damage.

The tables and figures identify the event types with the largest totals in each category. These results summarize historical database records rather than predict future events. Their interpretation depends on reporting completeness, consistency in event labels, and the treatment of damage magnitude codes. In particular, records with positive damage but unrecognized magnitude codes are excluded from the corresponding monetary totals, which may cause some economic impacts to be underestimated.