← All studies · askamerica.ai

County-level damaging weather event frequency shows no broad-based national trend over 2005-2024 — outcomes are highly county-specific

NOAA NCEI Storm Events Database (disasters.storm_events), county-attributed events with recorded property damage, 2005-2024

County-Level Damaging Weather Event Frequency, 2005-2024 NOAA NCEI Storm Events Database, county-attributed events with recorded property damage (cz_type='C') National linear trend, 2005-2024 -55 events/yr r2=0.02 (no reliable trend) Counties: increasing vs decreasing 1,084 up / 1,195 down of 2,280 counties with 10+ years data; median slope -0.01/yr National county-attributed damaging events per year 0 5,000 10,000 15,000 20,000 Year Event count 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 No sustained trend nationally; year-to-year variation driven by individual major storm seasons (e.g., 2011, 2008). Top 10 counties, steepest increase (events/year trend slope) 0 1 2 3 4 County Slope (events/yr) Maricopa AZ Lauderdale AL Colbert AL Allegheny PA Davidson TN Kanawha WV Rankin MS Hinds MS Susquehanna PA Broome NY Top 10 counties, steepest decrease (events/year trend slope) -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 County Slope (events/yr) Polk IA Cook IL Black Hawk IA Lonoke AR Story IA Charleston SC Dallas IA Berkeley SC Webster IA Warren IA AskAmerica · askamerica.ai
SVG

Summary

At the county grain, the frequency of damaging weather events (NOAA NCEI Storm Events, county-attributed records with recorded property damage) shows no broad-based national trend over 2005-2024: the national count is essentially flat/noisy (regression slope -55 events/year, r2=0.02 — statistically negligible), and of 2,280 counties with at least 10 years of data, 1,195 show a declining trend and 1,084 show an increasing one (median slope -0.01 events/year, i.e. flat). Change is highly local rather than a nationwide climate signal in this measure: some counties (Maricopa AZ, Lauderdale AL, Allegheny PA) show a strong 20-year increase of 2-4 additional damaging events per year, while others (several Iowa counties, Cook IL, Charleston/Berkeley SC) show an equally strong decline. This contrasts with NOAA's separate, coarser billion-dollar-disaster metric, which shows a clear national increase — the difference is explained below.

Data and method

Source: disasters.storm_events (NOAA NCEI Storm Events Database), 2004-2026 declared and observed coverage, 1.4M rows. Restricted to cz_type='C' (county-coded records; the database also logs NWS-zone and marine records that cannot be mapped to a single county) and damage_property > 0 as the operational definition of "damaging." Window used: full calendar years 2005-2024 (2004 and 2026 are partial in this table). For each county, computed a per-year event count, then a linear trend (regr_slope of count on year) across all years with data (2280 counties had 10+ of the 20 years), and separately the 2005-2009 vs 2020-2024 five-year average change for counties with a substantial early-period base (≥3 events/yr average).

Data-quality caveats

Every query behind this report

8 warehouse calls ran in this session, in order. Each is reproducible against the same snapshot.

query — 1 rows — 2331 ms
WITH county_year AS (
  SELECT county_fips, CAST("year" AS INTEGER) AS yr, COUNT(*) AS n_events
  FROM disasters.storm_events
  WHERE cz_type = 'C' AND county_fips IS NOT NULL
    AND CAST("year" AS INTEGER) BETWEEN 2005 AND 2024
    AND damage_property > 0
  GROUP BY county_fips, CAST("year" AS INTEGER)
)
SELECT COUNT(DISTINCT county_fips) AS n_counties, SUM(n_events) AS total_events, MIN(yr) AS min_yr, MAX(yr) AS max_yr
FROM county_year
query — 20 rows — 2510 ms
SELECT CAST("year" AS INTEGER) AS yr, COUNT(*) AS n_events, COUNT(DISTINCT county_fips) AS n_counties
FROM disasters.storm_events
WHERE cz_type='C' AND county_fips IS NOT NULL AND damage_property>0
  AND CAST("year" AS INTEGER) BETWEEN 2005 AND 2024
GROUP BY CAST("year" AS INTEGER)
ORDER BY yr
query — 20 rows — 2820 ms
SELECT CAST("year" AS INTEGER) AS yr,
  COUNT(*) AS total_rows,
  SUM(CASE WHEN county_fips IS NULL THEN 1 ELSE 0 END) AS null_fips,
  SUM(CASE WHEN cz_type='C' THEN 1 ELSE 0 END) AS cz_c,
  SUM(CASE WHEN cz_type='C' AND county_fips IS NOT NULL AND damage_property>0 THEN 1 ELSE 0 END) AS damaging_c
FROM disasters.storm_events
WHERE CAST("year" AS INTEGER) BETWEEN 2005 AND 2024
GROUP BY CAST("year" AS INTEGER)
ORDER BY yr
query — 20 rows — 2678 ms
SELECT CAST("year" AS INTEGER) AS yr, COUNT(*) AS n_events
FROM disasters.storm_events
WHERE cz_type='C' AND county_fips IS NOT NULL
  AND CAST("year" AS INTEGER) BETWEEN 2005 AND 2024
GROUP BY CAST("year" AS INTEGER)
ORDER BY yr
query — 1 rows — 3453 ms
SELECT regr_slope(n_events, yr) AS national_slope, regr_r2(n_events, yr) AS r2, count(*) AS n
FROM (
  SELECT CAST("year" AS INTEGER) AS yr, COUNT(*) AS n_events
  FROM disasters.storm_events
  WHERE cz_type='C' AND county_fips IS NOT NULL AND damage_property>0
    AND CAST("year" AS INTEGER) BETWEEN 2005 AND 2024
  GROUP BY CAST("year" AS INTEGER)
) t
query — 15 rows — 18044 ms
WITH cy AS (
  SELECT county_fips, CAST("year" AS INTEGER) AS yr, COUNT(*) AS n_events
  FROM disasters.storm_events
  WHERE cz_type='C' AND county_fips IS NOT NULL AND damage_property>0
    AND CAST("year" AS INTEGER) BETWEEN 2005 AND 2024
  GROUP BY county_fips, CAST("year" AS INTEGER)
),
early AS (
  SELECT county_fips, SUM(n_events)*1.0/5 AS avg_early
  FROM cy WHERE yr BETWEEN 2005 AND 2009 GROUP BY county_fips
),
late AS (
  SELECT county_fips, SUM(n_events)*1.0/5 AS avg_late
  FROM cy WHERE yr BETWEEN 2020 AND 2024 GROUP BY county_fips
),
slopes AS (
  SELECT county_fips, regr_slope(n_events, yr) AS slope, COUNT(*) AS n_years
  FROM cy GROUP BY county_fips
  HAVING COUNT(*) >= 10
),
cnames AS (
  SELECT county_fips, MAX(county_name) AS county_name, MAX(state_fips) AS state_fips
  FROM geo.counties GROUP BY county_fips
)
SELECT cn.county_name, sr.state_abbr, e.county_fips,
  ROUND(e.avg_early,2) AS avg_early_2005_09,
  ROUND(l.avg_late,2) AS avg_late_2020_24,
  ROUND(l.avg_late - e.avg_early,2) AS abs_change,
  ROUND(100.0*(l.avg_late-e.avg_early)/e.avg_early,1) AS pct_change,
  ROUND(s.slope,3) AS trend_slope_per_year
FROM early e
JOIN late l ON e.county_fips = l.county_fips
JOIN slopes s ON e.county_fips = s.county_fips
LEFT JOIN cnames cn ON cn.county_fips = e.county_fips
LEFT JOIN geo.state_ref sr ON sr.state_fips = LEFT(e.county_fips,2)
WHERE e.avg_early >= 3
ORDER BY s.slope DESC
LIMIT 15
query — 10 rows — 18153 ms
WITH cy AS (
  SELECT county_fips, CAST("year" AS INTEGER) AS yr, COUNT(*) AS n_events
  FROM disasters.storm_events
  WHERE cz_type='C' AND county_fips IS NOT NULL AND damage_property>0
    AND CAST("year" AS INTEGER) BETWEEN 2005 AND 2024
  GROUP BY county_fips, CAST("year" AS INTEGER)
),
early AS (
  SELECT county_fips, SUM(n_events)*1.0/5 AS avg_early
  FROM cy WHERE yr BETWEEN 2005 AND 2009 GROUP BY county_fips
),
late AS (
  SELECT county_fips, SUM(n_events)*1.0/5 AS avg_late
  FROM cy WHERE yr BETWEEN 2020 AND 2024 GROUP BY county_fips
),
slopes AS (
  SELECT county_fips, regr_slope(n_events, yr) AS slope, COUNT(*) AS n_years
  FROM cy GROUP BY county_fips
  HAVING COUNT(*) >= 10
),
cnames AS (
  SELECT county_fips, MAX(county_name) AS county_name FROM geo.counties GROUP BY county_fips
)
SELECT cn.county_name, sr.state_abbr, e.county_fips,
  ROUND(e.avg_early,2) AS avg_early_2005_09,
  ROUND(l.avg_late,2) AS avg_late_2020_24,
  ROUND(100.0*(l.avg_late-e.avg_early)/e.avg_early,1) AS pct_change,
  ROUND(s.slope,3) AS trend_slope_per_year
FROM early e
JOIN late l ON e.county_fips = l.county_fips
JOIN slopes s ON e.county_fips = s.county_fips
LEFT JOIN cnames cn ON cn.county_fips = e.county_fips
LEFT JOIN geo.state_ref sr ON sr.state_fips = LEFT(e.county_fips,2)
WHERE e.avg_early >= 3
ORDER BY s.slope ASC
FETCH FIRST 10 ROWS ONLY
query — 1 rows — 6222 ms
WITH cy AS (
  SELECT county_fips, CAST("year" AS INTEGER) AS yr, COUNT(*) AS n_events
  FROM disasters.storm_events
  WHERE cz_type='C' AND county_fips IS NOT NULL AND damage_property>0
    AND CAST("year" AS INTEGER) BETWEEN 2005 AND 2024
  GROUP BY county_fips, CAST("year" AS INTEGER)
),
slopes AS (
  SELECT county_fips, regr_slope(n_events, yr) AS slope, COUNT(*) AS n_years, AVG(n_events) AS avg_n
  FROM cy GROUP BY county_fips
  HAVING COUNT(*) >= 10
)
SELECT
  COUNT(*) AS n_counties_analyzed,
  SUM(CASE WHEN slope > 0 THEN 1 ELSE 0 END) AS n_increasing,
  SUM(CASE WHEN slope < 0 THEN 1 ELSE 0 END) AS n_decreasing,
  SUM(CASE WHEN slope = 0 THEN 1 ELSE 0 END) AS n_flat,
  ROUND(median(slope),4) AS median_slope,
  ROUND(avg(slope),4) AS mean_slope
FROM slopes

Sources

  1. disasters.storm_events national yearly county-attributed damaging event counts, 2005-2024
    Show tool call
    query(sql="SELECT CAST(\"year\" AS INTEGER) AS yr, COUNT(*) AS n_events FROM disasters.storm_events WHERE cz_type='C' AND county_fips IS NOT NULL AND damage_property>0 AND CAST(\"year\" AS INTEGER) BETWEEN 2005 AND 2024 GROUP BY CAST(\"year\" AS INTEGER) ORDER BY yr")
  2. County-level trend slopes (regr_slope) and up/down split, 2280 counties, 2005-2024
    Show tool call
    query(sql="WITH cy AS (SELECT county_fips, CAST(year AS INTEGER) AS yr, COUNT(*) AS n_events FROM disasters.storm_events WHERE cz_type='C' AND county_fips IS NOT NULL AND damage_property>0 AND CAST(year AS INTEGER) BETWEEN 2005 AND 2024 GROUP BY county_fips, CAST(year AS INTEGER)), slopes AS (SELECT county_fips, regr_slope(n_events, yr) AS slope, COUNT(*) AS n_years FROM cy GROUP BY county_fips HAVING COUNT(*)>=10) SELECT COUNT(*), SUM(CASE WHEN slope>0 THEN 1 ELSE 0 END), SUM(CASE WHEN slope<0 THEN 1 ELSE 0 END), median(slope), avg(slope) FROM slopes")
  3. NOAA Climate.gov / Climate Central — U.S. billion-dollar weather disaster frequency by decade
  4. Climate Central — U.S. Billion-Dollar Disasters 1980-2024