Reproducible Research Course Project 2
Storms and other severe weather events can result in fatalities, injuries, and property damage, and preventing such outcomes to the extent possible is a key concern. Our hypothesis is that some weather events are more harmful, with respect to population health, while some are more costly, with respect to economic consequence. To investigate this hypothesis, we explored the data from the U.S. National Oceanic and Atmospheric Administration (NOAA). From these data (between 1950 and 2011), we found that tornado is the most harmful weather event, while excessive heat and heat have the highest fatality rate among the top 5 weather events, and flood has the greatest economic consequences.
Loading the packages
library(tidyverse)
library(scales)
Reading the data file
Reading the data file from the website, the data come in the form of a comma-separated-value file compressed via the bzip2 algorithm.
urlFile <- "https://d396qusza40orc.cloudfront.net/repdata%2Fdata%2FStormData.csv.bz2"
download.file(urlFile, destfile="/Users/Doris/Desktop/Data Science Specialization/Assignments/NOAA_storm/StormData.csv.bz2", method="curl")
storm_data <- read.csv("StormData.csv.bz2")
After reading in the data, we check the first few rows of the data set to get a general idea of it.
dim(storm_data)
## [1] 902297 37
head(storm_data)
## 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
There are 902297 observations with 37 variables, but what we are interested in by now are only a few columns related to fatalities, injuries, and damages.
Subsetting the data frame
First, we subset the data frame with the columns that we are interested in.
storm_sub <- storm_data %>%
select(EVTYPE, FATALITIES, INJURIES, PROPDMG, PROPDMGEXP, CROPDMG, CROPDMGEXP)
Checking summary of the data and missing values
summary(storm_sub)
## EVTYPE FATALITIES INJURIES PROPDMG
## Length:902297 Min. : 0.0000 Min. : 0.0000 Min. : 0.00
## Class :character 1st Qu.: 0.0000 1st Qu.: 0.0000 1st Qu.: 0.00
## Mode :character Median : 0.0000 Median : 0.0000 Median : 0.00
## Mean : 0.0168 Mean : 0.1557 Mean : 12.06
## 3rd Qu.: 0.0000 3rd Qu.: 0.0000 3rd Qu.: 0.50
## Max. :583.0000 Max. :1700.0000 Max. :5000.00
## PROPDMGEXP CROPDMG CROPDMGEXP
## Length:902297 Min. : 0.000 Length:902297
## Class :character 1st Qu.: 0.000 Class :character
## Mode :character Median : 0.000 Mode :character
## Mean : 1.527
## 3rd Qu.: 0.000
## Max. :990.000
colSums(is.na(storm_sub))
## EVTYPE FATALITIES INJURIES PROPDMG PROPDMGEXP CROPDMG CROPDMGEXP
## 0 0 0 0 0 0 0
There is no missing value in “storm_sub” data frame.
Processing variable “EVTYPE”
We check the unique values of event types in column “EVTYPE”.
unique(storm_sub$EVTYPE)
It shows 985 unique values, while the number of event types are 48 in the storm data documentation, so we clean the data accordingly as following:
storm_sub$EVTYPE <- toupper(storm_sub$EVTYPE)
storm_sub$EVTYPE <- str_trim(storm_sub$EVTYPE)
storm_sub$EVENTS <- "OTHERS"
storm_sub$EVENTS[grep("ASTRONOMICAL", storm_sub$EVTYPE)] <- "ASTRONOMICAL LOW TIDE"
storm_sub$EVENTS[grep("AVALANCHE", storm_sub$EVTYPE)] <- "AVALANCHE"
storm_sub$EVENTS[grep("BLIZZARD", storm_sub$EVTYPE)] <- "BLIZZARD"
storm_sub$EVENTS[grep("COASTAL FLOOD", storm_sub$EVTYPE)] <- "COASTAL FLOOD"
storm_sub$EVENTS[grep("FLASH FLOOD", storm_sub$EVTYPE)] <- "FLASH FLOOD"
storm_sub$EVENTS[grep("LAKESHORE FLOOD", storm_sub$EVTYPE)] <- "LAKESHORE FLOOD"
storm_sub$EVENTS[which(grepl("FLOOD", storm_sub$EVTYPE) & !grepl("FLASH", storm_sub$EVTYPE) & !grepl("COASTAL", storm_sub$EVTYPE) & !grepl("LAKESHORE", storm_sub$EVTYPE))] <- "FLOOD"
storm_sub$EVENTS[which(grepl("COLD | WIND CHILL", storm_sub$EVTYPE) & !grepl("EXTREME", storm_sub$EVTYPE))] <- "COLD/WIND CHILL"
storm_sub$EVENTS[which(grepl("EXTREME COLD", storm_sub$EVTYPE) | grepl("EXTREME WINDCHILL", storm_sub$EVTYPE))] <- "EXTREME COLD/WIND CHILL"
storm_sub$EVENTS[grep("DEBRIS FLOW", storm_sub$EVTYPE)] <- "DEBRIS FLOW"
storm_sub$EVENTS[grep("DENSE FOG", storm_sub$EVTYPE)] <- "DENSE FOG"
storm_sub$EVENTS[grep("DENSE SMOKE", storm_sub$EVTYPE)] <- "DENSE SMOKE"
storm_sub$EVENTS[grep("DROUGHT", storm_sub$EVTYPE)] <- "DROUGHT"
storm_sub$EVENTS[grep("DUST DEVIL", storm_sub$EVTYPE)] <- "DUST DEVIL"
storm_sub$EVENTS[grep("DUST STORM", storm_sub$EVTYPE)] <- "DUST STORM"
storm_sub$EVENTS[grep("EXCESSIVE HEAT", storm_sub$EVTYPE)] <- "EXCESSIVE HEAT"
storm_sub$EVENTS[which(grepl("HEAT", storm_sub$EVTYPE) & !grepl("EXCESSIVE", storm_sub$EVTYPE))] <- "HEAT"
storm_sub$EVENTS[which(grepl("FROST", storm_sub$EVTYPE) | grepl("FREEZE", storm_sub$EVTYPE))] <- "FROST/FREEZE"
storm_sub$EVENTS[grep("FUNNEL CLOUD", storm_sub$EVTYPE)] <- "FUNNEL CLOUD"
storm_sub$EVENTS[grep("FREEZING FOG", storm_sub$EVTYPE)] <- "FREEZING FOG"
storm_sub$EVENTS[grep("MARINE HAIL", storm_sub$EVTYPE)] <- "MARINE HAIL"
storm_sub$EVENTS[which(grepl("HAIL", storm_sub$EVTYPE) & !grepl("MARINE", storm_sub$EVTYPE))] <- "HAIL"
storm_sub$EVENTS[grep("MARINE HIGH WIND", storm_sub$EVTYPE)] <- "MARINE HIGH WIND"
storm_sub$EVENTS[which(grepl("HIGH WIND", storm_sub$EVTYPE) & !grepl("MARINE", storm_sub$EVTYPE))] <- "HIGH WIND"
storm_sub$EVENTS[grep("MARINE STRONG WIND", storm_sub$EVTYPE)] <- "MARINE STRONG WIND"
storm_sub$EVENTS[which(grepl("STRONG WIND", storm_sub$EVTYPE) & !grepl("MARINE", storm_sub$EVTYPE))] <- "STRONG WIND"
storm_sub$EVENTS[grep("MARINE THUNDERSTORM WIND", storm_sub$EVTYPE)] <- "MARINE THUNDERSTORM WIND"
storm_sub$EVENTS[which(grepl("THUNDERSTORM WIND | TSTM WIND", storm_sub$EVTYPE) & !grepl("MARINE", storm_sub$EVTYPE))] <- "THUNDERSTORM WIND"
storm_sub$EVENTS[grep("HEAVY RAIN", storm_sub$EVTYPE)] <- "HEAVY RAIN"
storm_sub$EVENTS[grep("HEAVY SNOW", storm_sub$EVTYPE)] <- "HEAVY SNOW"
storm_sub$EVENTS[grep("HIGH SURF", storm_sub$EVTYPE)] <- "HIGH SURF"
storm_sub$EVENTS[which(grepl("HURRICANE", storm_sub$EVTYPE) | grepl("TYPHOON", storm_sub$EVTYPE))] <- "HURRICANE (TYPHOON)"
storm_sub$EVENTS[grep("ICE STORM", storm_sub$EVTYPE)] <- "ICE STORM"
storm_sub$EVENTS[grep("LAKE-EFFECT SNOW", storm_sub$EVTYPE)] <- "LAKE-EFFECT SNOW"
storm_sub$EVENTS[grep("LIGHTNING", storm_sub$EVTYPE)] <- "LIGHTNING"
storm_sub$EVENTS[grep("RIP CURRENT", storm_sub$EVTYPE)] <- "RIP CURRENT"
storm_sub$EVENTS[grep("SEICHE", storm_sub$EVTYPE)] <- "SEICHE"
storm_sub$EVENTS[grep("SLEET", storm_sub$EVTYPE)] <- "SLEET"
storm_sub$EVENTS[grep("STORM SURGE", storm_sub$EVTYPE)] <- "STORM SURGE/TIDE"
storm_sub$EVENTS[grep("TORNADO", storm_sub$EVTYPE)] <- "TORNADO"
storm_sub$EVENTS[grep("TROPICAL DEPRESSION", storm_sub$EVTYPE)] <- "TROPICAL DEPRESSION"
storm_sub$EVENTS[grep("TROPICAL STORM", storm_sub$EVTYPE)] <- "TROPICAL STORM"
storm_sub$EVENTS[grep("TSUNAMI", storm_sub$EVTYPE)] <- "TSUNAMI"
storm_sub$EVENTS[grep("VOLCANIC ASH", storm_sub$EVTYPE)] <- "VOLCANIC ASH"
storm_sub$EVENTS[grep("WATERSPOUT", storm_sub$EVTYPE)] <- "WATERSPOUT"
storm_sub$EVENTS[grep("WILDFIRE", storm_sub$EVTYPE)] <- "WILDFIRE"
storm_sub$EVENTS[grep("WINTER STORM", storm_sub$EVTYPE)] <- "WINTER STORM"
storm_sub$EVENTS[grep("WINTER WEATHER", storm_sub$EVTYPE)] <- "WINTER WEATHER"
unique(storm_sub$EVENTS)
## [1] "TORNADO" "OTHERS"
## [3] "HAIL" "ICE STORM"
## [5] "WINTER STORM" "HURRICANE (TYPHOON)"
## [7] "HEAVY RAIN" "LIGHTNING"
## [9] "DENSE FOG" "RIP CURRENT"
## [11] "FLASH FLOOD" "HIGH WIND"
## [13] "FUNNEL CLOUD" "HEAT"
## [15] "FLOOD" "WATERSPOUT"
## [17] "EXTREME COLD/WIND CHILL" "BLIZZARD"
## [19] "HEAVY SNOW" "FROST/FREEZE"
## [21] "COASTAL FLOOD" "AVALANCHE"
## [23] "DUST STORM" "SLEET"
## [25] "DUST DEVIL" "EXCESSIVE HEAT"
## [27] "STRONG WIND" "HIGH SURF"
## [29] "WINTER WEATHER" "DROUGHT"
## [31] "STORM SURGE/TIDE" "TROPICAL STORM"
## [33] "WILDFIRE" "COLD/WIND CHILL"
## [35] "THUNDERSTORM WIND" "LAKE-EFFECT SNOW"
## [37] "FREEZING FOG" "VOLCANIC ASH"
## [39] "SEICHE" "TROPICAL DEPRESSION"
## [41] "ASTRONOMICAL LOW TIDE" "MARINE HAIL"
## [43] "MARINE HIGH WIND" "TSUNAMI"
## [45] "DENSE SMOKE" "LAKESHORE FLOOD"
## [47] "MARINE THUNDERSTORM WIND" "MARINE STRONG WIND"
It shows 48 event types, including “others”, no “Debris Flow” comparing to the event types list in the documentation.
Processing variables “PROPDMG” and “CROPDMG”
According to the storm data documentation, there are two types of economic damages in this dataset, corn and property, both comes with the estimated numbers(“PROPDMG”, “CROPDMG”) and the expressions(“PROPDMGEXP”, “CROPDMGEXP”).
We check the value distribution of column “PROPDMGEXP” first.
table(storm_sub$PROPDMGEXP)
##
## - ? + 0 1 2 3 4 5 6
## 465934 1 8 5 216 25 13 4 4 28 4
## 7 8 B h H K m M
## 5 1 40 1 6 424665 7 11330
The expression value is a little mess up, and there is not much information from the documentation, except “K” represents for thousands, “M” for millions, and “B” for billions, so we assume H/h represent for hundreds, and blank inputs for 0.
We check the proportion of valid value in this column.
mean(storm_sub$PROPDMGEXP %in% c("", 0, "H", "h", "K", "k", "M", "m", "B", "b"))
## [1] 0.9998914
There are 99.99% of valid data, so we’ll ignore the invalid data for now.
We then do the same with the column “CROPDMGEXP”.
table(storm_sub$CROPDMGEXP)
##
## ? 0 2 B k K m M
## 618413 7 19 1 9 21 281832 1 1994
mean(storm_sub$CROPDMGEXP %in% c("", 0, "K", "k", "M", "m", "B", "b"))
## [1] 0.9999911
The proportion of valid data is over 99.99%, so we’ll ignore the invalid ones as well.
Now, we transform the estimated damage number to the real value.
storm_sub <- storm_sub %>%
mutate(PROPDMG=ifelse(storm_sub$PROPDMGEXP=="H"| storm_sub$PROPDMGEXP=="h", PROPDMG * 10^2, ifelse(
storm_sub$PROPDMGEXP=="K" | storm_sub$PROPDMGEXP=="k", PROPDMG * 10^3, ifelse(
storm_sub$PROPDMGEXP=="M"| storm_sub$PROPDMGEXP=="m", PROPDMG * 10^6, ifelse(
storm_sub$PROPDMGEXP=="B"| storm_sub$PROPDMGEXP=="b", PROPDMG * 10^9, PROPDMG)
))))
storm_sub <- storm_sub %>%
mutate(CROPDMG=ifelse(storm_sub$CROPDMGEXP=="K" | storm_sub$CROPDMGEXP=="k", CROPDMG * 10^3, ifelse(
storm_sub$CROPDMGEXP=="M"| storm_sub$CROPDMGEXP=="m", CROPDMG * 10^6, ifelse(
storm_sub$CROPDMGEXP=="B"| storm_sub$CROPDMGEXP=="b", CROPDMG * 10^9, CROPDMG)
)))
Across the United States, which types of events are most harmful with respect to population health?
1. Calculating total injuries and fatalities
We subset the data frame to calculate the total injuries and
fatalities and the fatality rate in each event type, ranking by total
numbers.
(Note: we exclude “others” from the ranking, because it is a total
number of all “other” types.)
harm <- storm_sub %>%
select(EVENTS, FATALITIES, INJURIES) %>%
dplyr::filter(EVENTS != "OTHERS") %>%
group_by(EVENTS) %>%
summarize(Fatality=sum(FATALITIES), Injury=sum(INJURIES)) %>%
mutate(TotalNum=Fatality+Injury) %>%
mutate(FatalityRate=percent(Fatality / (TotalNum), accuracy=0.01)) %>%
arrange(-TotalNum)
2. Visualizing the data
We then make a plot for the most harmful weather events (top 5) with the total number of fatalities and injuries and the fatality rate from 1950 to 2011.
harm %>%
top_n(5, TotalNum) %>%
ggplot(aes(x=reorder(EVENTS, -TotalNum), TotalNum))+
geom_bar(stat="identity", fill="brown")+
ylim(0,110000)+
labs(title="The Most Harmful Weather Events in US (1950-2011)", subtitle="Total injuries and fatilities & The fatality rate", x="Weather Events", y="Total Injury and Fatality")+
geom_text(aes(label=FatalityRate), col="white", vjust=1.3, size=4)+
geom_label(aes(label=TotalNum), vjust=-0.5, size=4)
As can be seen from the plot:
Across the United States, which types of events have the greatest economic consequences?
1. Calculating total damage values
We subset the data frame to calculate the total damage value of each
type of weather events.
(Note: we exclude “others” from the ranking again, because it’s the sum
value of all “other” types.)
damage <- storm_sub %>%
mutate(Total=PROPDMG + CROPDMG) %>%
select(EVENTS, Total) %>%
dplyr::filter(EVENTS != "OTHERS") %>%
group_by(EVENTS) %>%
summarize(TotalDMG=sum(Total)) %>%
arrange(desc(TotalDMG))
2. Visualizing the data
We make a plot to show the weather events with top 5 total damage value as following.
damage %>%
top_n(5, TotalDMG) %>%
ggplot(aes(x=reorder(EVENTS, -TotalDMG), y=TotalDMG/10^9))+
geom_col(fill="lightblue")+
labs(title="The Most Costly Weather Events in US (Billions)", subtitle="From 1950 to 2011", x="Weather Events", y="Total Damage (Billions)")+
geom_label(aes(label=round(TotalDMG/10^9,1), vjust=1.5))
As demonstrated in the above plot: