Synopsis

This report analyzes the U.S. National Oceanic and Atmospheric Administration’s (NOAA) storm database to identify weather events most harmful to public health and those with the greatest economic consequences. The database covers storm events from 1950 to November 2011 across the United States. After loading and cleaning the data, we aggregated fatalities, injuries, and property/crop damages by event type. Our analysis reveals that tornadoes are by far the most harmful weather events with respect to population health, accounting for the highest number of both fatalities and injuries. For economic consequences, floods cause the greatest total property and crop damage in dollar terms. These findings suggest that emergency management resources should prioritize preparedness for tornadoes in terms of protecting human life and floods in terms of protecting economic assets. The analysis uses only the raw data provided and all transformations are documented herein.

Data Processing

Loading Required Libraries

# Load required libraries
library(ggplot2)
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
library(tidyr)
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
library(scales)

# Set seed for reproducibility
set.seed(42)

Downloading and Loading the Data

# Download the file if it doesn't exist
storm_data <- read.csv("repdata_data_StormData.csv", 
                          header = TRUE, 
                          sep = ",",
                          stringsAsFactors = FALSE,
                          na.strings = c("", "NA"))



# Display basic structure
dim(storm_data)
## [1] 902297     37

Initial Data Exploration

# View the structure of the dataset
str(storm_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  NA NA NA NA ...
##  $ BGN_LOCATI: chr  NA NA NA NA ...
##  $ END_DATE  : chr  NA NA NA NA ...
##  $ END_TIME  : chr  NA NA NA NA ...
##  $ 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  NA NA NA NA ...
##  $ END_LOCATI: chr  NA NA NA NA ...
##  $ 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  NA NA NA NA ...
##  $ WFO       : chr  NA NA NA NA ...
##  $ STATEOFFIC: chr  NA NA NA NA ...
##  $ ZONENAMES : chr  NA NA NA NA ...
##  $ 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  NA NA NA NA ...
##  $ REFNUM    : num  1 2 3 4 5 6 7 8 9 10 ...
# Check the first few rows
head(storm_data[, c("BGN_DATE", "EVTYPE", "FATALITIES", "INJURIES", 
                    "PROPDMG", "PROPDMGEXP", "CROPDMG", "CROPDMGEXP")], 10)
##              BGN_DATE  EVTYPE FATALITIES INJURIES PROPDMG PROPDMGEXP CROPDMG
## 1   4/18/1950 0:00:00 TORNADO          0       15    25.0          K       0
## 2   4/18/1950 0:00:00 TORNADO          0        0     2.5          K       0
## 3   2/20/1951 0:00:00 TORNADO          0        2    25.0          K       0
## 4    6/8/1951 0:00:00 TORNADO          0        2     2.5          K       0
## 5  11/15/1951 0:00:00 TORNADO          0        2     2.5          K       0
## 6  11/15/1951 0:00:00 TORNADO          0        6     2.5          K       0
## 7  11/16/1951 0:00:00 TORNADO          0        1     2.5          K       0
## 8   1/22/1952 0:00:00 TORNADO          0        0     2.5          K       0
## 9   2/13/1952 0:00:00 TORNADO          1       14    25.0          K       0
## 10  2/13/1952 0:00:00 TORNADO          0        0    25.0          K       0
##    CROPDMGEXP
## 1        <NA>
## 2        <NA>
## 3        <NA>
## 4        <NA>
## 5        <NA>
## 6        <NA>
## 7        <NA>
## 8        <NA>
## 9        <NA>
## 10       <NA>
# Summary of key variables
summary(storm_data[, c("FATALITIES", "INJURIES", "PROPDMG", "CROPDMG")])
##    FATALITIES           INJURIES            PROPDMG           CROPDMG       
##  Min.   :  0.00000   Min.   :   0.0000   Min.   :   0.00   Min.   :  0.000  
##  1st Qu.:  0.00000   1st Qu.:   0.0000   1st Qu.:   0.00   1st Qu.:  0.000  
##  Median :  0.00000   Median :   0.0000   Median :   0.00   Median :  0.000  
##  Mean   :  0.01678   Mean   :   0.1557   Mean   :  12.06   Mean   :  1.527  
##  3rd Qu.:  0.00000   3rd Qu.:   0.0000   3rd Qu.:   0.50   3rd Qu.:  0.000  
##  Max.   :583.00000   Max.   :1700.0000   Max.   :5000.00   Max.   :990.000
# Check number of unique event types
cat("Number of unique EVTYPE values:", length(unique(storm_data$EVTYPE)), "\n")
## Number of unique EVTYPE values: 985

Data Cleaning and Transformation

Step 1: Select Relevant Variables

# Select only the columns needed for our analysis
storm_clean <- storm_data %>%
  select(
    BGN_DATE,
    EVTYPE,
    FATALITIES,
    INJURIES,
    PROPDMG,
    PROPDMGEXP,
    CROPDMG,
    CROPDMGEXP
  )

cat("Dimensions after selecting relevant variables:", dim(storm_clean), "\n")
## Dimensions after selecting relevant variables: 902297 8

Step 2: Clean Event Types

# Convert EVTYPE to uppercase for consistency
storm_clean$EVTYPE <- toupper(trimws(storm_clean$EVTYPE))

# Check top event types by frequency
top_events <- storm_clean %>%
  count(EVTYPE, sort = TRUE) %>%
  head(20)

print(top_events)
##                      EVTYPE      n
## 1                      HAIL 288661
## 2                 TSTM WIND 219946
## 3         THUNDERSTORM WIND  82564
## 4                   TORNADO  60652
## 5               FLASH FLOOD  54278
## 6                     FLOOD  25327
## 7        THUNDERSTORM WINDS  20843
## 8                 HIGH WIND  20214
## 9                 LIGHTNING  15755
## 10               HEAVY SNOW  15708
## 11               HEAVY RAIN  11742
## 12             WINTER STORM  11433
## 13           WINTER WEATHER   7045
## 14             FUNNEL CLOUD   6844
## 15         MARINE TSTM WIND   6175
## 16 MARINE THUNDERSTORM WIND   5812
## 17               WATERSPOUT   3797
## 18              STRONG WIND   3569
## 19     URBAN/SML STREAM FLD   3392
## 20                 WILDFIRE   2761

Step 3: Convert Property Damage Exponents

The PROPDMGEXP and CROPDMGEXP columns contain multiplier codes that need to be converted to actual numeric values. According to the NOAA documentation:

K or k = thousands (10^3) M or m = millions (10^6) B or b = billions (10^9) H or h = hundreds (10^2) Numeric values 0-8 represent the exponent (10^n) Empty/unknown values are treated as 1 (10^0)

# Function to convert damage exponent codes to numeric multipliers
convert_exp <- function(exp_code) {
  exp_code <- toupper(trimws(as.character(exp_code)))
  
  multiplier <- case_when(
    exp_code == "K" ~ 1e3,
    exp_code == "M" ~ 1e6,
    exp_code == "B" ~ 1e9,
    exp_code == "H" ~ 1e2,
    exp_code == "0" ~ 1e0,
    exp_code == "1" ~ 1e1,
    exp_code == "2" ~ 1e2,
    exp_code == "3" ~ 1e3,
    exp_code == "4" ~ 1e4,
    exp_code == "5" ~ 1e5,
    exp_code == "6" ~ 1e6,
    exp_code == "7" ~ 1e7,
    exp_code == "8" ~ 1e8,
    exp_code == "NA" ~ 1,
    TRUE ~ 1  # Default: treat as 1 for unknown codes
  )
  return(multiplier)
}

# Check unique exponent values before conversion
cat("Unique PROPDMGEXP values:\n")
## Unique PROPDMGEXP values:
print(table(storm_clean$PROPDMGEXP, useNA = "always"))
## 
##      -      ?      +      0      1      2      3      4      5      6      7 
##      1      8      5    216     25     13      4      4     28      4      5 
##      8      B      h      H      K      m      M   <NA> 
##      1     40      1      6 424665      7  11330 465934
cat("\nUnique CROPDMGEXP values:\n")
## 
## Unique CROPDMGEXP values:
print(table(storm_clean$CROPDMGEXP, useNA = "always"))
## 
##      ?      0      2      B      k      K      m      M   <NA> 
##      7     19      1      9     21 281832      1   1994 618413
# Apply the conversion function to calculate actual damage values
storm_clean <- storm_clean %>%
  mutate(
    PROPDMGEXP_NUM = convert_exp(PROPDMGEXP),
    CROPDMGEXP_NUM = convert_exp(CROPDMGEXP),
    PROP_DAMAGE    = PROPDMG * PROPDMGEXP_NUM,
    CROP_DAMAGE    = CROPDMG * CROPDMGEXP_NUM,
    TOTAL_DAMAGE   = PROP_DAMAGE + CROP_DAMAGE
  )

# Verify the transformation
cat("\nSummary of calculated damage values (in USD):\n")
## 
## Summary of calculated damage values (in USD):
summary(storm_clean[, c("PROP_DAMAGE", "CROP_DAMAGE", "TOTAL_DAMAGE")])
##   PROP_DAMAGE         CROP_DAMAGE         TOTAL_DAMAGE     
##  Min.   :0.000e+00   Min.   :0.000e+00   Min.   :0.00e+00  
##  1st Qu.:0.000e+00   1st Qu.:0.000e+00   1st Qu.:0.00e+00  
##  Median :0.000e+00   Median :0.000e+00   Median :0.00e+00  
##  Mean   :4.746e+05   Mean   :5.442e+04   Mean   :5.29e+05  
##  3rd Qu.:5.000e+02   3rd Qu.:0.000e+00   3rd Qu.:1.00e+03  
##  Max.   :1.150e+11   Max.   :5.000e+09   Max.   :1.15e+11

Step 4: Filter Records with Non-Zero Values

# For health analysis: filter records with fatalities or injuries
health_data <- storm_clean %>%
  filter(FATALITIES > 0 | INJURIES > 0)

cat("Records with health impact:", nrow(health_data), "\n")
## Records with health impact: 21929
# For economic analysis: filter records with property or crop damage
econ_data <- storm_clean %>%
  filter(TOTAL_DAMAGE > 0)

cat("Records with economic impact:", nrow(econ_data), "\n")
## Records with economic impact: 245031

Step 5: Aggregate Data by Event Type

# Aggregate health impacts by event type
health_by_event <- storm_clean %>%
  group_by(EVTYPE) %>%
  summarise(
    Total_Fatalities = sum(FATALITIES, na.rm = TRUE),
    Total_Injuries   = sum(INJURIES,   na.rm = TRUE),
    Total_Health     = Total_Fatalities + Total_Injuries,
    .groups = "drop"
  ) %>%
  arrange(desc(Total_Health))

# Top 15 events by total health impact
top15_health <- head(health_by_event, 15)
cat("Top 15 Events by Total Health Impact:\n")
## Top 15 Events by Total Health Impact:
print(top15_health)
## # A tibble: 15 × 4
##    EVTYPE            Total_Fatalities Total_Injuries Total_Health
##    <chr>                        <dbl>          <dbl>        <dbl>
##  1 TORNADO                       5633          91346        96979
##  2 EXCESSIVE HEAT                1903           6525         8428
##  3 TSTM WIND                      504           6957         7461
##  4 FLOOD                          470           6789         7259
##  5 LIGHTNING                      816           5230         6046
##  6 HEAT                           937           2100         3037
##  7 FLASH FLOOD                    978           1777         2755
##  8 ICE STORM                       89           1975         2064
##  9 THUNDERSTORM WIND              133           1488         1621
## 10 WINTER STORM                   206           1321         1527
## 11 HIGH WIND                      248           1137         1385
## 12 HAIL                            15           1361         1376
## 13 HURRICANE/TYPHOON               64           1275         1339
## 14 HEAVY SNOW                     127           1021         1148
## 15 WILDFIRE                        75            911          986
# Aggregate economic impacts by event type
econ_by_event <- storm_clean %>%
  group_by(EVTYPE) %>%
  summarise(
    Total_PropDamage = sum(PROP_DAMAGE, na.rm = TRUE),
    Total_CropDamage = sum(CROP_DAMAGE, na.rm = TRUE),
    Total_Damage     = sum(TOTAL_DAMAGE, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(Total_Damage))

# Top 15 events by total economic impact
top15_econ <- head(econ_by_event, 15)
cat("Top 15 Events by Total Economic Impact (USD):\n")
## Top 15 Events by Total Economic Impact (USD):
print(top15_econ)
## # A tibble: 15 × 4
##    EVTYPE            Total_PropDamage Total_CropDamage  Total_Damage
##    <chr>                        <dbl>            <dbl>         <dbl>
##  1 FLOOD                144657709807        5661968450 150319678257 
##  2 HURRICANE/TYPHOON     69305840000        2607872800  71913712800 
##  3 TORNADO               56947380676.        414953270  57362333946.
##  4 STORM SURGE           43323536000              5000  43323541000 
##  5 HAIL                  15735267513.       3025954473  18761221986.
##  6 FLASH FLOOD           16822723978.       1421317100  18244041078.
##  7 DROUGHT                1046106000       13972566000  15018672000 
##  8 HURRICANE             11868319010        2741910000  14610229010 
##  9 RIVER FLOOD            5118945500        5029459000  10148404500 
## 10 ICE STORM              3944927860        5022113500   8967041360 
## 11 TROPICAL STORM         7703890550         678346000   8382236550 
## 12 WINTER STORM           6688497251          26944000   6715441251 
## 13 HIGH WIND              5270046295         638571300   5908617595 
## 14 WILDFIRE               4765114000         295472800   5060586800 
## 15 TSTM WIND              4493058495         554007350   5047065845

Results

Question 1: Which Events Are Most Harmful to Population Health?

# Prepare data for plotting - top 10 by fatalities
top10_fatal <- health_by_event %>%
  arrange(desc(Total_Fatalities)) %>%
  head(10) %>%
  mutate(EVTYPE = factor(EVTYPE, levels = rev(EVTYPE)))

# Top 10 by injuries
top10_injury <- health_by_event %>%
  arrange(desc(Total_Injuries)) %>%
  head(10) %>%
  mutate(EVTYPE = factor(EVTYPE, levels = rev(EVTYPE)))

# Plot 1: Fatalities
p1 <- ggplot(top10_fatal, 
             aes(x = EVTYPE, y = Total_Fatalities, fill = Total_Fatalities)) +
  geom_bar(stat = "identity", color = "black", size = 0.3) +
  coord_flip() +
  scale_fill_gradient(low = "#FFC0C0", high = "#CC0000", 
                      name = "Fatalities",
                      labels = comma) +
  scale_y_continuous(labels = comma) +
  labs(
    title = "Top 10 Events by Total Fatalities",
    subtitle = "United States, 1950-2011",
    x = "Event Type",
    y = "Total Fatalities"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title    = element_text(face = "bold", size = 12, color = "#CC0000"),
    plot.subtitle = element_text(size = 10, color = "gray50"),
    axis.text.y   = element_text(size = 9, face = "bold"),
    axis.text.x   = element_text(size = 9),
    legend.position = "none",
    panel.grid.minor = element_blank()
  )
## Warning in geom_bar(stat = "identity", color = "black", size = 0.3): Ignoring
## unknown parameters: `size`
# Plot 2: Injuries
p2 <- ggplot(top10_injury, 
             aes(x = EVTYPE, y = Total_Injuries, fill = Total_Injuries)) +
  geom_bar(stat = "identity", color = "black", size = 0.3) +
  coord_flip() +
  scale_fill_gradient(low = "#FFD580", high = "#FF8C00", 
                      name = "Injuries",
                      labels = comma) +
  scale_y_continuous(labels = comma) +
  labs(
    title = "Top 10 Events by Total Injuries",
    subtitle = "United States, 1950-2011",
    x = "Event Type",
    y = "Total Injuries"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title    = element_text(face = "bold", size = 12, color = "#FF8C00"),
    plot.subtitle = element_text(size = 10, color = "gray50"),
    axis.text.y   = element_text(size = 9, face = "bold"),
    axis.text.x   = element_text(size = 9),
    legend.position = "none",
    panel.grid.minor = element_blank()
  )
## Warning in geom_bar(stat = "identity", color = "black", size = 0.3): Ignoring
## unknown parameters: `size`
# Combine plots side by side
grid.arrange(p1, p2, 
             ncol = 2,
             top = grid::textGrob(
               "Weather Events Most Harmful to Population Health (1950-2011)",
               gp = grid::gpar(fontsize = 14, fontface = "bold")
             ))

Key Findings - Population Health:

The analysis clearly shows that TORNADO is by far the most dangerous weather event for human health:

# Summary table for top 10 health impacts
health_summary <- health_by_event %>%
  head(10) %>%
  mutate(
    Total_Fatalities = format(Total_Fatalities, big.mark = ","),
    Total_Injuries   = format(Total_Injuries,   big.mark = ","),
    Total_Health     = format(Total_Health,     big.mark = ",")
  )

knitr::kable(
  health_summary,
  col.names = c("Event Type", "Total Fatalities", 
                "Total Injuries", "Total Health Impact"),
  caption   = "Table 1: Top 10 Weather Events by Total Health Impact (1950-2011)",
  align     = c("l", "r", "r", "r")
)
Table 1: Top 10 Weather Events by Total Health Impact (1950-2011)
Event Type Total Fatalities Total Injuries Total Health Impact
TORNADO 5,633 91,346 96,979
EXCESSIVE HEAT 1,903 6,525 8,428
TSTM WIND 504 6,957 7,461
FLOOD 470 6,789 7,259
LIGHTNING 816 5,230 6,046
HEAT 937 2,100 3,037
FLASH FLOOD 978 1,777 2,755
ICE STORM 89 1,975 2,064
THUNDERSTORM WIND 133 1,488 1,621
WINTER STORM 206 1,321 1,527

Question 2: Which Events Have the Greatest Economic Consequences?

# Prepare data for economic plots
# Top 10 by property damage
top10_prop <- econ_by_event %>%
  arrange(desc(Total_PropDamage)) %>%
  head(10) %>%
  mutate(
    EVTYPE = factor(EVTYPE, levels = rev(EVTYPE)),
    Damage_Billions = Total_PropDamage / 1e9
  )

# Top 10 by crop damage
top10_crop <- econ_by_event %>%
  arrange(desc(Total_CropDamage)) %>%
  head(10) %>%
  mutate(
    EVTYPE = factor(EVTYPE, levels = rev(EVTYPE)),
    Damage_Billions = Total_CropDamage / 1e9
  )

# Plot 3: Property Damage
p3 <- ggplot(top10_prop, 
             aes(x = EVTYPE, y = Damage_Billions, fill = Damage_Billions)) +
  geom_bar(stat = "identity", color = "black", size = 0.3) +
  coord_flip() +
  scale_fill_gradient(low = "#B0C4DE", high = "#00008B",
                      name = "USD (Billions)") +
  scale_y_continuous(labels = dollar_format(suffix = "B", prefix = "$")) +
  labs(
    title    = "Top 10 Events by Property Damage",
    subtitle = "United States, 1950-2011",
    x        = "Event Type",
    y        = "Total Property Damage (Billions USD)"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title    = element_text(face = "bold", size = 12, color = "#00008B"),
    plot.subtitle = element_text(size = 10, color = "gray50"),
    axis.text.y   = element_text(size = 9, face = "bold"),
    axis.text.x   = element_text(size = 8, angle = 15, hjust = 1),
    legend.position = "none",
    panel.grid.minor = element_blank()
  )
## Warning in geom_bar(stat = "identity", color = "black", size = 0.3): Ignoring
## unknown parameters: `size`
# Plot 4: Crop Damage
p4 <- ggplot(top10_crop, 
             aes(x = EVTYPE, y = Damage_Billions, fill = Damage_Billions)) +
  geom_bar(stat = "identity", color = "black", size = 0.3) +
  coord_flip() +
  scale_fill_gradient(low = "#90EE90", high = "#006400",
                      name = "USD (Billions)") +
  scale_y_continuous(labels = dollar_format(suffix = "B", prefix = "$")) +
  labs(
    title    = "Top 10 Events by Crop Damage",
    subtitle = "United States, 1950-2011",
    x        = "Event Type",
    y        = "Total Crop Damage (Billions USD)"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title    = element_text(face = "bold", size = 12, color = "#006400"),
    plot.subtitle = element_text(size = 10, color = "gray50"),
    axis.text.y   = element_text(size = 9, face = "bold"),
    axis.text.x   = element_text(size = 8, angle = 15, hjust = 1),
    legend.position = "none",
    panel.grid.minor = element_blank()
  )
## Warning in geom_bar(stat = "identity", color = "black", size = 0.3): Ignoring
## unknown parameters: `size`
# Combine plots
grid.arrange(p3, p4,
             ncol = 2,
             top = grid::textGrob(
               "Weather Events with Greatest Economic Consequences (1950-2011)",
               gp = grid::gpar(fontsize = 14, fontface = "bold")
             ))

# Top 10 by total damage
top10_total <- econ_by_event %>%
  arrange(desc(Total_Damage)) %>%
  head(10) %>%
  mutate(EVTYPE = factor(EVTYPE, levels = rev(EVTYPE)))

# Reshape to long format for stacked bar
top10_long <- top10_total %>%
  select(EVTYPE, Total_PropDamage, Total_CropDamage) %>%
  pivot_longer(
    cols      = c(Total_PropDamage, Total_CropDamage),
    names_to  = "Damage_Type",
    values_to = "Amount"
  ) %>%
  mutate(
    Amount_Billions = Amount / 1e9,
    Damage_Type = recode(Damage_Type,
                         "Total_PropDamage" = "Property Damage",
                         "Total_CropDamage" = "Crop Damage")
  )

# Figure 3: Stacked bar - Total Combined Economic Damage
p5 <- ggplot(top10_long,
             aes(x = EVTYPE,
                 y = Amount_Billions,
                 fill = Damage_Type)) +
  geom_bar(stat  = "identity",
           color = "black",
           size  = 0.3) +
  coord_flip() +
  scale_fill_manual(
    values = c("Property Damage" = "#4472C4",
               "Crop Damage"     = "#70AD47"),
    name   = "Damage Type"
  ) +
  scale_y_continuous(
    labels = dollar_format(suffix = "B", prefix = "$")
  ) +
  labs(
    title    = "Top 10 Weather Events by Total Economic Damage",
    subtitle = "Combined Property and Crop Damage, United States (1950-2011)",
    x        = "Event Type",
    y        = "Total Damage (Billions USD)",
    caption  = "Source: NOAA Storm Database (1950-2011). Property damage dominates total economic losses."
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title    = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(size = 11, color = "gray50"),
    plot.caption  = element_text(size = 9,  color = "gray60"),
    axis.text.y   = element_text(size = 10, face = "bold"),
    axis.text.x   = element_text(size = 10),
    legend.position    = "bottom",
    legend.title       = element_text(face = "bold"),
    panel.grid.minor   = element_blank(),
    panel.grid.major.y = element_blank()
  )
## Warning in geom_bar(stat = "identity", color = "black", size = 0.3): Ignoring
## unknown parameters: `size`
print(p5)

Economic Summary Table

# Format economic summary table
econ_summary <- econ_by_event %>%
  head(10) %>%
  mutate(
    Total_PropDamage = paste0("$", format(
      round(Total_PropDamage / 1e9, 2), 
      big.mark = ","), "B"),
    Total_CropDamage = paste0("$", format(
      round(Total_CropDamage / 1e9, 2), 
      big.mark = ","), "B"),
    Total_Damage = paste0("$", format(
      round(Total_Damage / 1e9, 2),     
      big.mark = ","), "B")
  )

knitr::kable(
  econ_summary,
  col.names = c("Event Type", 
                "Property Damage (USD)",
                "Crop Damage (USD)",
                "Total Damage (USD)"),
  caption   = "Table 2: Top 10 Weather Events by Total Economic Damage (1950-2011)",
  align     = c("l", "r", "r", "r")
)
Table 2: Top 10 Weather Events by Total Economic Damage (1950-2011)
Event Type Property Damage (USD) Crop Damage (USD) Total Damage (USD)
FLOOD $144.66B $ 5.66B $150.32B
HURRICANE/TYPHOON $ 69.31B $ 2.61B $ 71.91B
TORNADO $ 56.95B $ 0.41B $ 57.36B
STORM SURGE $ 43.32B $ 0.00B $ 43.32B
HAIL $ 15.74B $ 3.03B $ 18.76B
FLASH FLOOD $ 16.82B $ 1.42B $ 18.24B
DROUGHT $ 1.05B $13.97B $ 15.02B
HURRICANE $ 11.87B $ 2.74B $ 14.61B
RIVER FLOOD $ 5.12B $ 5.03B $ 10.15B
ICE STORM $ 3.94B $ 5.02B $ 8.97B

Key Findings Summary

# Print key numeric findings
cat("============================================\n")
## ============================================
cat("     KEY FINDINGS SUMMARY\n")
##      KEY FINDINGS SUMMARY
cat("============================================\n\n")
## ============================================
# Health findings
top_fatal  <- health_by_event$EVTYPE[1]
top_injury <- health_by_event %>% 
  arrange(desc(Total_Injuries)) %>% 
  pull(EVTYPE) %>% 
  first()

cat("--- POPULATION HEALTH ---\n")
## --- POPULATION HEALTH ---
cat(sprintf(
  "Most fatal event     : %s (%s fatalities)\n",
  top_fatal,
  format(health_by_event$Total_Fatalities[1], big.mark = ",")
))
## Most fatal event     : TORNADO (5,633 fatalities)
cat(sprintf(
  "Most injurious event : %s (%s injuries)\n\n",
  top_injury,
  format(
    health_by_event %>% 
      arrange(desc(Total_Injuries)) %>% 
      pull(Total_Injuries) %>% 
      first(),
    big.mark = ","
  )
))
## Most injurious event : TORNADO (91,346 injuries)
# Economic findings
top_econ      <- econ_by_event$EVTYPE[1]
top_prop      <- econ_by_event %>% 
  arrange(desc(Total_PropDamage)) %>% 
  pull(EVTYPE) %>% 
  first()
top_crop      <- econ_by_event %>% 
  arrange(desc(Total_CropDamage)) %>% 
  pull(EVTYPE) %>% 
  first()

cat("--- ECONOMIC CONSEQUENCES ---\n")
## --- ECONOMIC CONSEQUENCES ---
cat(sprintf(
  "Greatest total damage    : %s ($%.2fB)\n",
  top_econ,
  econ_by_event$Total_Damage[1] / 1e9
))
## Greatest total damage    : FLOOD ($150.32B)
cat(sprintf(
  "Greatest property damage : %s ($%.2fB)\n",
  top_prop,
  econ_by_event %>% 
    arrange(desc(Total_PropDamage)) %>% 
    pull(Total_PropDamage) %>% 
    first() / 1e9
))
## Greatest property damage : FLOOD ($144.66B)
cat(sprintf(
  "Greatest crop damage     : %s ($%.2fB)\n",
  top_crop,
  econ_by_event %>% 
    arrange(desc(Total_CropDamage)) %>% 
    pull(Total_CropDamage) %>% 
    first() / 1e9
))
## Greatest crop damage     : DROUGHT ($13.97B)
cat("============================================\n")
## ============================================