Synopsis

This report compares recorded fatalities, injuries, and property and crop damage across event types in the supplied NOAA Storm Database extract for 1950-2011. Tornadoes account for the largest cumulative number of recorded deaths and injuries over the full period. In the more comparable 1996-2011 period, excessive heat leads fatalities, while tornadoes lead injuries. Flood has the largest recorded combined property and crop damage total, but this ranking depends on one unusually large and internally inconsistent damage entry. Excluding that single record in a sensitivity analysis makes hurricane/typhoon the leading economic category. Drought accounts for the greatest recorded crop damage. The findings describe historical reported impacts by database label; uneven coverage, inconsistent labels, uncertain damage estimates, and unadjusted dollar values limit their interpretation.

Data Processing

Scope, measures, and reproducibility

The unit of analysis is a database record, which need not represent a distinct storm: one storm can produce records in several places and under several event labels. All records in the supplied file are included in the primary analysis, including U.S. territories and marine reporting areas; results are not restricted to the contiguous states. Population health is measured with fatalities and injuries separately so their different severity remains visible. Their sum is also shown as a descriptive count of recorded adverse health outcomes, without implying equal severity or a count of unique people. Economic impact is measured as reported property damage plus reported crop damage in nominal U.S. dollars. This excludes unrecorded losses and broader effects such as lost output and health costs.

The report reads the original compressed CSV directly; no prepared dataset is an input. Only knitr and rmarkdown are needed in addition to base R. To reproduce it in RStudio, install those packages once, put this .Rmd beside the raw .csv.bz2, and select Knit -> Knit to HTML. The code also searches the current user’s Downloads directory, or an explicit path can be passed with params = list(data_file = "path/to/file.csv.bz2") to rmarkdown::render(). Every analysis chunk is displayed with echo = TRUE; intermediate data and figures are created within this document.

knitr::opts_chunk$set(
  echo = TRUE, cache = FALSE, warning = TRUE, message = TRUE,
  fig.align = "center", out.width = "100%", dpi = 140
)
options(scipen = 999, width = 88)
fmt_n <- function(x) formatC(x, format = "f", digits = 0, big.mark = ",")
fmt_b <- function(x) sprintf("$%.2f billion", x / 1e9)
rank_by <- function(x, metric) {
  x[order(-x[[metric]], x$EVTYPE), , drop = FALSE]
}

Load and validate the raw file

read.csv() decompresses the file through bzfile(). Column classes are explicit, and columns unrelated to this analysis are skipped during import to reduce memory use. EVTYPE, the damage suffixes, and narratives remain character strings. Missing numeric entries are retained as missing; the checks below stop the analysis if any health counts or damage mantissas are missing, non-finite, or negative. Dates must parse successfully, and record identifiers must be unique.

candidates <- unique(c(
  path.expand(params$data_file),
  file.path(path.expand("~"), "Downloads", basename(params$data_file)),
  file.path(Sys.getenv("USERPROFILE"), "Downloads", basename(params$data_file))
))
available <- candidates[file.exists(candidates)]
if (!length(available)) {
  stop("Raw CSV not found. Put it beside this report or set params$data_file.")
}
data_path <- normalizePath(available[1], winslash = "/", mustWork = TRUE)
input_md5 <- unname(tools::md5sum(data_path))

header <- names(read.csv(bzfile(data_path), nrows = 0, check.names = FALSE))
text_fields <- c("BGN_DATE", "STATE", "COUNTYNAME", "EVTYPE",
                 "PROPDMGEXP", "CROPDMGEXP", "REMARKS")
numeric_fields <- c("FATALITIES", "INJURIES", "PROPDMG", "CROPDMG", "REFNUM")
stopifnot(all(c(text_fields, numeric_fields) %in% header))
classes <- rep("NULL", length(header))
classes[header %in% text_fields] <- "character"
classes[header %in% numeric_fields] <- "numeric"
storm <- read.csv(
  bzfile(data_path), colClasses = classes, stringsAsFactors = FALSE,
  na.strings = "NA", check.names = FALSE
)
storm$DATE <- as.Date(storm$BGN_DATE, format = "%m/%d/%Y")
storm$YEAR <- as.integer(format(storm$DATE, "%Y"))
stopifnot(!anyNA(storm$DATE), !anyDuplicated(storm$REFNUM))
numeric_audit <- data.frame(
  Field = numeric_fields,
  Missing = vapply(storm[numeric_fields], function(x) sum(is.na(x)), integer(1)),
  Nonfinite = vapply(storm[numeric_fields], function(x) sum(!is.finite(x)), integer(1)),
  Negative = vapply(storm[numeric_fields], function(x) sum(x < 0, na.rm = TRUE), integer(1))
)
stopifnot(all(numeric_audit$Nonfinite == 0), all(numeric_audit$Negative == 0))
knitr::kable(numeric_audit, row.names = FALSE,
             caption = "Table 1. Validation of imported numeric fields.")
Table 1. Validation of imported numeric fields.
Field Missing Nonfinite Negative
FATALITIES 0 0 0
INJURIES 0 0 0
PROPDMG 0 0 0
CROPDMG 0 0 0
REFNUM 0 0 0

Event labels and periods

Labels are converted to uppercase, trimmed, and repeated spaces are collapsed. No subjective reassignment of mixed events or spelling variants is performed. For example, TSTM WIND and THUNDERSTORM WIND, HEAT and EXCESSIVE HEAT, and FLOOD and FLASH FLOOD remain distinct. Consequently, the rankings answer the assignment at the EVTYPE label level rather than for fully harmonized hazard families. All records are retained, including records with zero reported impacts.

NOAA’s documentation of the periods of record describes expanded coverage beginning in 1996, with early records concentrated on tornadoes, thunderstorm winds, and hail. Full-period totals answer the question about this supplied file, while a separate 1996-2011 analysis checks how the leaders change during the period with broader event coverage. Neither period provides exposure-adjusted risk or severity per storm.

storm$EVTYPE_RAW <- storm$EVTYPE
storm$EVTYPE <- toupper(gsub("[[:space:]]+", " ", trimws(storm$EVTYPE)))
stopifnot(!anyNA(storm$EVTYPE), all(nzchar(storm$EVTYPE)))
scope <- data.frame(
  Item = c("Records", "First event date", "Last event date",
           "Original EVTYPE labels", "Normalized EVTYPE labels",
           "Records beginning in 1996 or later", "Raw compressed-file MD5"),
  Value = c(fmt_n(nrow(storm)), as.character(min(storm$DATE)),
            as.character(max(storm$DATE)), fmt_n(length(unique(storm$EVTYPE_RAW))),
            fmt_n(length(unique(storm$EVTYPE))), fmt_n(sum(storm$YEAR >= 1996)),
            input_md5)
)
knitr::kable(scope, row.names = FALSE, caption = "Table 2. Input and analysis scope.")
Table 2. Input and analysis scope.
Item Value
Records 902,297
First event date 1950-01-03
Last event date 2011-11-30
Original EVTYPE labels 985
Normalized EVTYPE labels 883
Records beginning in 1996 or later 653,530
Raw compressed-file MD5 df4aa61fff89427db6b7f7b1113b5553

Convert damage mantissas and magnitude suffixes

Multipliers are applied before aggregation. Following the magnitude convention described in NWS Storm Data Preparation, section 2.7, K, M, and B denote thousands, millions, and billions of dollars. The legacy H suffix is interpreted as hundreds; lowercase letters are accepted after conversion to uppercase. A blank suffix is treated as dollars (multiplier 1), an explicit assumption checked below. For numeric or punctuation suffixes with a positive mantissa, the unit is unresolved and that damage component is set to NA, rather than guessing a power of ten. A zero mantissa contributes zero regardless of suffix. Reported sums below omit unresolved components but retain any interpretable component from the same record, and the omissions are counted explicitly. Zero means zero recorded damage, not proof that no damage occurred.

decode_damage <- function(value, suffix) {
  suffix <- toupper(trimws(suffix))
  multiplier <- setNames(c(1, 1e2, 1e3, 1e6, 1e9), c("", "H", "K", "M", "B"))
  dollars <- value * unname(multiplier[match(suffix, names(multiplier))])
  dollars[value == 0] <- 0
  dollars
}
storm$PROPERTY <- decode_damage(storm$PROPDMG, storm$PROPDMGEXP)
storm$CROPS <- decode_damage(storm$CROPDMG, storm$CROPDMGEXP)
storm$HEALTH <- storm$FATALITIES + storm$INJURIES

suffix_audit <- function(value, suffix, component) {
  suffix <- toupper(trimws(suffix))
  d <- data.frame(Component = component,
                  Suffix = ifelse(suffix == "", "[blank]", suffix),
                  Records = 1L, Positive = as.integer(value > 0),
                  Unresolved = as.integer(is.na(decode_damage(value, suffix))))
  aggregate(cbind(Records, Positive, Unresolved) ~ Component + Suffix,
            data = d, FUN = sum)
}
damage_audit <- rbind(
  suffix_audit(storm$PROPDMG, storm$PROPDMGEXP, "Property"),
  suffix_audit(storm$CROPDMG, storm$CROPDMGEXP, "Crops")
)
unresolved_records <- sum(is.na(storm$PROPERTY) | is.na(storm$CROPS))
knitr::kable(damage_audit, row.names = FALSE,
             caption = "Table 3. Suffix audit; Positive counts positive mantissas.")
Table 3. Suffix audit; Positive counts positive mantissas.
Component Suffix Records Positive Unresolved
Property - 1 1 1
Property ? 8 0 0
Property [blank] 465934 76 0
Property + 5 5 5
Property 0 216 209 209
Property 1 25 0 0
Property 2 13 1 1
Property 3 4 1 1
Property 4 4 4 4
Property 5 28 18 18
Property 6 4 3 3
Property 7 5 2 2
Property 8 1 0 0
Property B 40 40 0
Property H 7 7 0
Property K 424665 227481 0
Property M 11337 11326 0
Crops ? 7 0 0
Crops [blank] 618413 3 0
Crops 0 19 12 12
Crops 2 1 0 0
Crops B 9 7 0
Crops K 281853 20158 0
Crops M 1995 1919 0

There are 256 records with at least one unresolved damage component. The economic figures are therefore sums of interpretable reported components, not complete estimates of all losses. An additional sensitivity treats all blank-suffix components as unresolved to check the dollar assumption without assigning units to numeric suffixes.

Aggregate and define sensitivity checks

The same aggregation function is used for the full file, the 1996 onward subset, and the damage sensitivity analyses. The largest interpretable property-damage record is inspected because one unusual record can dominate national totals. It is retained in the primary results, and a separate analysis excludes the entire record without claiming to know its correct amount. No source value is overwritten.

summarize_events <- function(d) {
  out <- aggregate(
    d[c("FATALITIES", "INJURIES", "HEALTH", "PROPERTY", "CROPS")],
    by = list(EVTYPE = d$EVTYPE), FUN = function(x) sum(x, na.rm = TRUE)
  )
  counts <- aggregate(
    data.frame(Records = rep(1L, nrow(d)),
               Unresolved = as.integer(is.na(d$PROPERTY) | is.na(d$CROPS))),
    by = list(EVTYPE = d$EVTYPE), FUN = sum
  )
  out <- merge(out, counts, by = "EVTYPE", sort = FALSE)
  out$ECONOMIC <- out$PROPERTY + out$CROPS
  out
}
overall <- summarize_events(storm)
recent <- summarize_events(storm[storm$YEAR >= 1996, ])
stopifnot(sum(overall$FATALITIES) == sum(storm$FATALITIES),
          sum(overall$INJURIES) == sum(storm$INJURIES),
          isTRUE(all.equal(sum(overall$ECONOMIC),
                          sum(storm$PROPERTY, na.rm = TRUE) +
                          sum(storm$CROPS, na.rm = TRUE))))

largest_i <- which.max(storm$PROPERTY)
largest_record <- storm[largest_i, c("REFNUM", "DATE", "STATE", "COUNTYNAME",
                                    "EVTYPE", "PROPDMG", "PROPDMGEXP", "CROPDMG",
                                    "CROPDMGEXP", "PROPERTY", "CROPS", "REMARKS")]
without_largest <- summarize_events(storm[-largest_i, ])
recent_without_largest <- summarize_events(
  storm[storm$YEAR >= 1996 & seq_len(nrow(storm)) != largest_i, ]
)

blank_sensitive <- storm[c("EVTYPE", "FATALITIES", "INJURIES", "HEALTH",
                            "PROPERTY", "CROPS")]
blank_sensitive$PROPERTY[trimws(storm$PROPDMGEXP) == ""] <- NA_real_
blank_sensitive$CROPS[trimws(storm$CROPDMGEXP) == ""] <- NA_real_
without_blanks <- summarize_events(blank_sensitive)
blank_difference <- sum(overall$ECONOMIC) - sum(without_blanks$ECONOMIC)
leaders <- function(d, period) {
  metrics <- c("FATALITIES", "INJURIES", "HEALTH", "ECONOMIC", "CROPS")
  data.frame(
    Period = period,
    Measure = c("Fatalities", "Injuries", "Fatalities + injuries",
                "Property + crop damage", "Crop damage"),
    Leader = vapply(metrics, function(m) rank_by(d, m)$EVTYPE[1], character(1)),
    Value = vapply(metrics, function(m) max(d[[m]]), numeric(1))
  )
}

Results

1. Events most harmful to population health

Tornadoes lead both recorded fatalities and injuries over the full period. They account for 5,633 fatalities and 91,346 injuries, or 62.3% of the combined recorded health outcomes. The table ranks the ten labels with the largest combined counts; the figure ranks deaths and injuries independently so a label with many deaths is not hidden by injury counts.

health_top <- head(rank_by(overall, "HEALTH"), 10)
health_table <- data.frame(
  EVTYPE = health_top$EVTYPE,
  Fatalities = fmt_n(health_top$FATALITIES),
  Injuries = fmt_n(health_top$INJURIES),
  `Combined outcomes` = fmt_n(health_top$HEALTH),
  check.names = FALSE
)
knitr::kable(health_table, row.names = FALSE,
             caption = "Table 4. Largest cumulative recorded health impacts, 1950-2011.")
Table 4. Largest cumulative recorded health impacts, 1950-2011.
EVTYPE Fatalities Injuries Combined outcomes
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
health_panel <- function(metric, title, color) {
  d <- head(rank_by(overall, metric), 10)
  d <- d[nrow(d):1, ]
  v <- d[[metric]]
  mids <- barplot(v, horiz = TRUE, names.arg = d$EVTYPE, las = 1,
                  col = color, border = NA, cex.names = 0.77,
                  xlim = c(0, max(v) * 1.27), axes = FALSE,
                  main = title, xlab = "Recorded people")
  ticks <- pretty(c(0, max(v)))
  axis(1, at = ticks, labels = fmt_n(ticks), cex.axis = 0.8)
  text(v + max(v) * 0.025, mids, labels = fmt_n(v), pos = 4, cex = 0.76)
}
old_par <- par(no.readonly = TRUE)
par(mfrow = c(1, 2), mar = c(4.3, 10.3, 3, 1), mgp = c(2.5, 0.7, 0))
health_panel("FATALITIES", "Fatalities", "#B14A35")
health_panel("INJURIES", "Injuries", "#237B8E")
Figure 1. Ten leading EVTYPE labels by recorded fatalities (left) and injuries (right), 1950-2011. Panels have different horizontal scales; counts are cumulative.

Figure 1. Ten leading EVTYPE labels by recorded fatalities (left) and injuries (right), 1950-2011. Panels have different horizontal scales; counts are cumulative.

par(old_par)

2. Events with the greatest economic consequences

FLOOD has the largest recorded combined damage total in the supplied data: $150.32 billion. HURRICANE/TYPHOON follows at $71.91 billion. Property damage dominates these totals. For crop damage alone, DROUGHT leads at $13.97 billion. All amounts are nominal, with no adjustment for inflation, population, or wealth.

economic_top <- head(rank_by(overall, "ECONOMIC"), 10)
economic_table <- data.frame(
  EVTYPE = economic_top$EVTYPE,
  `Property (USD bn)` = round(economic_top$PROPERTY / 1e9, 3),
  `Crops (USD bn)` = round(economic_top$CROPS / 1e9, 3),
  `Combined (USD bn)` = round(economic_top$ECONOMIC / 1e9, 3),
  `Unresolved records` = economic_top$Unresolved, check.names = FALSE
)
knitr::kable(economic_table, row.names = FALSE, digits = 3,
             caption = "Table 5. Largest recorded economic impacts, 1950-2011; interpretable components only.")
Table 5. Largest recorded economic impacts, 1950-2011; interpretable components only.
EVTYPE Property (USD bn) Crops (USD bn) Combined (USD bn) Unresolved records
FLOOD 144.658 5.662 150.320 0
HURRICANE/TYPHOON 69.306 2.608 71.914 0
TORNADO 56.937 0.415 57.352 14
STORM SURGE 43.324 0.000 43.324 0
HAIL 15.732 3.026 18.758 13
FLASH FLOOD 16.141 1.421 17.562 13
DROUGHT 1.046 13.973 15.019 0
HURRICANE 11.868 2.742 14.610 0
RIVER FLOOD 5.119 5.029 10.148 0
ICE STORM 3.945 5.022 8.967 1

A single damage entry changes the leading category. The largest property-damage entry is record 605,943, dated 2006-01-01, for FLOOD in NAPA, CA. Its encoded property damage is $115.00 billion and it contributes 76.5% of the full-period FLOOD total. The narrative below describes damage in millions, which conflicts with the billion-dollar suffix; this supports treating the record as uncertain. Excluding the record makes HURRICANE/TYPHOON the largest economic category at $71.91 billion; FLOOD then totals $35.29 billion. This is a sensitivity check rather than an asserted correction.

entry_fields <- setdiff(names(largest_record), "REMARKS")
entry_audit <- data.frame(
  Field = entry_fields,
  Value = vapply(largest_record[entry_fields], as.character, character(1))
)
knitr::kable(entry_audit, row.names = FALSE,
             caption = "Table 6. Largest encoded property-damage entry, retained in the primary analysis.")
Table 6. Largest encoded property-damage entry, retained in the primary analysis.
Field Value
REFNUM 605943
DATE 2006-01-01
STATE CA
COUNTYNAME NAPA
EVTYPE FLOOD
PROPDMG 115
PROPDMGEXP B
CROPDMG 32.5
CROPDMGEXP M
PROPERTY 115000000000
CROPS 32500000
cat(strwrap(gsub("[[:space:]]+", " ", largest_record$REMARKS), width = 84),
    sep = "\n")
## Major flooding continued into the early hours of January 1st, before the Napa River
## finally fell below flood stage and the water receeded. Flooding was severe in
## Downtown Napa from the Napa Creek and the City and Parks Department was hit with $6
## million in damage alone. The City of Napa had 600 homes with moderate damage, 150
## damaged businesses with costs of at least $70 million.
economic_panel <- function(summary, title) {
  d <- head(rank_by(summary, "ECONOMIC"), 10)
  d <- d[nrow(d):1, ]
  parts <- rbind(d$PROPERTY, d$CROPS) / 1e9
  totals <- colSums(parts)
  mids <- barplot(parts, horiz = TRUE, names.arg = d$EVTYPE, las = 1,
                  col = c("#246B83", "#DEA545"), border = NA,
                  cex.names = 0.72, xlim = c(0, max(totals) * 1.25),
                  main = title, xlab = "Damage (US$ billions)")
  text(totals + max(totals) * 0.02, mids,
       labels = sprintf("%.1f", totals), pos = 4, cex = 0.75)
  legend("bottomright", legend = c("Property", "Crops"),
         fill = c("#246B83", "#DEA545"), border = NA, bty = "n", cex = 0.8)
}
old_par <- par(no.readonly = TRUE)
par(mfrow = c(1, 2), mar = c(4.3, 10.7, 3, 1), mgp = c(2.5, 0.7, 0))
economic_panel(overall, "All reported records")
economic_panel(without_largest, "Largest damage record excluded")
Figure 2. Ten leading EVTYPE labels by reported property plus crop damage, 1950-2011: all records (left) and excluding the single largest property-damage record (right). Panel scales differ. Values are nominal U.S. dollars; unresolved components are omitted.

Figure 2. Ten leading EVTYPE labels by reported property plus crop damage, 1950-2011: all records (left) and excluding the single largest property-damage record (right). Panel scales differ. Values are nominal U.S. dollars; unresolved components are omitted.

par(old_par)

Robustness to period and blank damage suffixes

During 1996-2011, EXCESSIVE HEAT leads fatalities at 1,797, while TORNADO leads injuries at 20,667. The change in the fatality leader shows why full-period totals should not be interpreted as a fair comparison of all hazards over a common observation window. The economic leader is FLOOD with all recent records and HURRICANE/TYPHOON when the same unusually large entry is excluded.

period_leaders <- rbind(leaders(overall, "1950-2011"),
                        leaders(recent, "1996-2011"))
period_leaders$Value <- ifelse(
  period_leaders$Measure %in% c("Property + crop damage", "Crop damage"),
  fmt_b(period_leaders$Value), fmt_n(period_leaders$Value)
)
knitr::kable(period_leaders, row.names = FALSE,
             caption = "Table 7. Leading label for each outcome in two observation periods.")
Table 7. Leading label for each outcome in two observation periods.
Period Measure Leader Value
1950-2011 Fatalities TORNADO 5,633
1950-2011 Injuries TORNADO 91,346
1950-2011 Fatalities + injuries TORNADO 96,979
1950-2011 Property + crop damage FLOOD $150.32 billion
1950-2011 Crop damage DROUGHT $13.97 billion
1996-2011 Fatalities EXCESSIVE HEAT 1,797
1996-2011 Injuries TORNADO 20,667
1996-2011 Fatalities + injuries TORNADO 22,178
1996-2011 Property + crop damage FLOOD $148.92 billion
1996-2011 Crop damage DROUGHT $13.37 billion
blank_check <- data.frame(
  Assumption = c("Blank suffix = dollars", "Blank suffix omitted"),
  Leader = c(rank_by(overall, "ECONOMIC")$EVTYPE[1],
             rank_by(without_blanks, "ECONOMIC")$EVTYPE[1]),
  `Total interpretable damage (USD bn)` = c(sum(overall$ECONOMIC),
                                         sum(without_blanks$ECONOMIC)) / 1e9,
  check.names = FALSE
)
knitr::kable(blank_check, row.names = FALSE, digits = 6,
             caption = "Table 8. Sensitivity to the treatment of blank magnitude suffixes.")
Table 8. Sensitivity to the treatment of blank magnitude suffixes.
Assumption Leader Total interpretable damage (USD bn)
Blank suffix = dollars FLOOD 476.4228
Blank suffix omitted FLOOD 476.4228

Omitting blank-suffix components reduces the national damage sum by $538, with no change in the leading economic label. Unresolved numeric and punctuation suffixes remain excluded in both versions, so this check does not establish their correct amounts.

Interpretation and limitations

These are cumulative recorded impacts rather than predictions, annualized losses, or individual risk estimates. Underreporting, changing collection practices, inconsistent event labels, and uncertainty in damage estimates can affect rankings. The file ends in 2011 and does not describe current conditions. The dollar totals mix prices from different years, and zero reported damage can include unmeasured losses. Separate labels also split related hazards: hurricane wind, flood, and storm surge can arise from the same cyclone but remain separate here. The leading full-period health category is clear in these records, while the economic leader is materially sensitive to one questionable entry. Fatalities and injuries should therefore be read separately, and the economic ranking should be read alongside the sensitivity analysis.

Software environment

package_versions <- data.frame(
  Package = c("knitr", "rmarkdown"),
  Version = vapply(c("knitr", "rmarkdown"),
                   function(p) as.character(utils::packageVersion(p)), character(1))
)
knitr::kable(package_versions, row.names = FALSE)
Package Version
knitr 1.52
rmarkdown 2.32
sessionInfo()
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26300)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=Spanish_Spain.utf8  LC_CTYPE=Spanish_Spain.utf8   
## [3] LC_MONETARY=Spanish_Spain.utf8 LC_NUMERIC=C                  
## [5] LC_TIME=Spanish_Spain.utf8    
## 
## time zone: Europe/Madrid
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## loaded via a namespace (and not attached):
##  [1] digest_0.6.37     R6_2.6.1          fastmap_1.2.0     xfun_0.61        
##  [5] cachem_1.1.0      knitr_1.52        htmltools_0.5.9   rmarkdown_2.32   
##  [9] lifecycle_1.0.4   cli_3.6.5         sass_0.4.10       jquerylib_0.1.4  
## [13] compiler_4.5.1    rstudioapi_0.19.0 tools_4.5.1       evaluate_1.0.5   
## [17] bslib_0.12.0      yaml_2.3.10       rlang_1.3.0       jsonlite_2.0.0