County-level "damaging" weather-event frequency, 2005-2024: no uniform national trend — a slight net decline nationally, but sharply divergent and mostly reporting-driven at the county/state level
NOAA/NCEI Storm Events Database (askamerica disasters.storm_events), county-type records with damage or casualties, 2005-2024
Summary
Nationally, county-level reports of damaging weather events (a storm-event record with property/crop damage or any death/injury, restricted to county-type NOAA reports) fell about 12% comparing 2005-2014 to 2015-2024 (127,554 to 111,813 events). But that national figure masks huge and opposite-signed swings underneath: across 3,236 counties with usable data, 56% show a declining 20-year trend and 44% show an increasing one — this is not a uniform national pattern of more or fewer damaging storms, it is a patchwork. Several states (Iowa, Arkansas, Kansas, Alabama, Illinois) show 45-70% declines in reported damaging events, while others (Puerto Rico, West Virginia, North Carolina, New York, Pennsylvania) show 40-165% increases over the same two decades. Critically, NOAA's own published documentation on this exact database states that severe-storm reporting "has not been uniform over time because of changes in forecast [office] verification and emphasis" — so a large share of both the sharp declines (Iowa, Arkansas) and the sharp increases seen here should be treated as candidate reporting-practice artifacts first, not confirmed changes in actual storm frequency, until checked against an independent physical measure (e.g., NWS radar-verified tornado/hail counts) county by county.
Data and method
Source: disasters.storm_events (NOAA/NCEI Storm Events Database), which the askamerica catalog documents as observed and contiguous 2004-2026 (1.42M rows). "Last twenty years" was operationalized as the 2005-2024 window — the most recent twenty fully-loaded calendar years (2004 sits just outside a clean 20-year span from today, and 2025-2026 are partial/in-progress years in this feed).
"Damaging weather event" was operationalized as a storm-event record with cz_type='C' (a county-level report, as opposed to an NWS-zone or marine report) AND at least one of: damage_property > 0, damage_crops > 0, deaths_direct/indirect > 0, or injuries_direct/indirect > 0. This is a reasonable proxy, not the literal FEMA/NOAA term "damaging weather event," which the corpus does not carry as a defined field (search_catalog's own unmatched-terms check confirmed "damaging" appears in no table/column description here).
Two independent methods were run so the headline does not rest on one arbitrary split: (1) a two-decade comparison (2005-2014 vs 2015-2024 event counts by county and by state), and (2) a full county-by-year linear regression slope over all 20 years, filling years with zero events where a county reported none, so a county's absence in a given year counts as zero rather than being silently dropped.
Exclusion, and what it costs: county_fips is null on 41.4% of all storm_events rows nationally — these are NWS zone-type ('Z') reports, which cover a broader area than one county and cannot be attributed to a specific county. Restricting to county-type ('C') records is a necessary exclusion for a county-grain question, but it is not a random subset: it is 41.4% is a fixed database-wide share, not a property that varies systematically by state in a way checked here, but the excluded zone-type events are simply invisible to this county-level trend and would need to be apportioned or analyzed separately for a complete picture of total storm activity.
County-level trend: split roughly 44/56, not a national direction
Running regr_slope of annual damaging-event count against year for every one of 3,236 counties with any county-type reporting activity (n=20 years each, zero-filled): 1,410 counties (44%) show a rising trend, 1,826 (56%) show a falling trend, with a median slope of about -0.012 events/year — essentially flat at the median, meaning the split is close to even and the tails carry the story.
Top 15 counties by steepest INCREASE in damaging-event frequency per year (min. 1 event/year average) included Maricopa County AZ (+3.98/yr), Jefferson County AL (+3.02/yr), Mohave County AZ (+2.92/yr), Colbert County AL (+2.46/yr), Allegheny County PA (+1.96/yr), Wake County NC (+1.95/yr), Davidson County TN (+1.92/yr), Marshall County WV (+1.83/yr), two Mississippi Delta counties, Broome County NY, Berks County PA, Barbour County AL, a Puerto Rico municipio, and San Bernardino County CA (+1.50/yr).
Top 15 counties by steepest DECREASE were overwhelmingly concentrated in Iowa (state FIPS 19): Polk County IA (-3.94/yr) led, followed by nine more Iowa counties, plus Jefferson County AL, Cook County IL, Tuscaloosa County AL, Jefferson County AL (again, different code), and one South Carolina county. The Iowa concentration — ten of the fifteen steepest-declining counties nationwide sitting in one state — is itself evidence pointing toward a state- or forecast-office-level change in reporting practice (e.g., a change in how a Weather Forecast Office logs hail/wind damage estimates) rather than ten counties independently experiencing a real drop in storm frequency at the same time.
State-level pattern: a real geographic split, but read the outliers as reporting-practice candidates
Aggregating to state level, the states with the largest declines in damaging-event counts (2005-14 to 2015-24) were Iowa (8,831 -> 2,654, -70%), Arkansas (4,697 -> 1,767, -62%), Kansas (2,903 -> 1,351, -53%), Illinois (4,026 -> 2,075, -48%), Alabama (5,665 -> 3,023, -47%), and Texas (8,000 -> 6,297, -21%). The states with the largest increases were Puerto Rico (386 -> 1,024, +165%), West Virginia (2,319 -> 3,677, +59%), North Carolina (3,225 -> 4,632, +44%), Arizona (1,396 -> 2,068, +48%), New York (4,316 -> 6,050, +40%), and Pennsylvania (4,590 -> 6,422, +40%).
Some of this pattern is plausibly real: Arizona's rise is consistent with more extreme-heat and monsoon-flood reporting in a warming/urbanizing Southwest; Puerto Rico's rise is heavily influenced by Hurricane Maria (2017) and subsequent storms; Appalachian/Mid-Atlantic increases (WV, PA, NC, NY) track with well-documented rises in heavy-precipitation and flash-flood events in the Northeast and Appalachians. But the magnitude of the Midwest/South declines (Iowa -70%, Arkansas -62%, Kansas -53%) is larger than any independently documented decline in actual severe-thunderstorm or hail frequency in those states over this period, and directly matches the mechanism NOAA's own NCEI documentation names: forecast-office-level changes in "verification and emphasis" for reporting minor hail/wind damage. This should be read as an open question requiring a follow-up check against a reporting-practice-independent measure (e.g., NWS Doppler-radar-estimated hail/wind swaths, or insurance-industry claims data) before treating it as a real decline in storm frequency, not as a settled climate finding.
National trend: no clean monotonic signal
The national annual count of damaging county-level events (chart above) shows no steady upward or downward trend across 2005-2024. It peaks sharply in 2008 (15,904) and 2011 (18,427) — both were exceptional tornado/hail outbreak years (2011 in particular was one of the most active tornado years on record, including the historic April 2011 Super Outbreak) — then settles into a lower, roughly flat band of 9,965-13,037 for most of 2012-2024. Because the 2005-2014 decade average is inflated by these two outlier years, the headline "-12% nationally" partly reflects the absence of another 2008/2011-scale outbreak year in 2015-2024, not necessarily a structural decline in storm frequency.
Every query behind this report
7 warehouse calls ran in this session, in order. Each is reproducible against the same snapshot.
query — 3236 rows — 8698 ms
SELECT county_fips, state_fips,
SUM(CASE WHEN CAST("year" AS INT) BETWEEN 2005 AND 2014 THEN 1 ELSE 0 END) AS early_count,
SUM(CASE WHEN CAST("year" AS INT) BETWEEN 2015 AND 2024 THEN 1 ELSE 0 END) AS recent_count
FROM disasters.storm_events
WHERE cz_type = 'C' AND county_fips IS NOT NULL
AND (damage_property > 0 OR damage_crops > 0 OR deaths_direct > 0 OR deaths_indirect > 0 OR injuries_direct > 0 OR injuries_indirect > 0)
AND CAST("year" AS INT) BETWEEN 2005 AND 2024
GROUP BY county_fips, state_fipsquery — 1 rows — 10421 ms
WITH county_counts AS (
SELECT county_fips, state_fips,
SUM(CASE WHEN CAST("year" AS INT) BETWEEN 2005 AND 2014 THEN 1 ELSE 0 END) AS early_count,
SUM(CASE WHEN CAST("year" AS INT) BETWEEN 2015 AND 2024 THEN 1 ELSE 0 END) AS recent_count
FROM disasters.storm_events
WHERE cz_type = 'C' AND county_fips IS NOT NULL
AND (damage_property > 0 OR damage_crops > 0 OR deaths_direct > 0 OR deaths_indirect > 0 OR injuries_direct > 0 OR injuries_indirect > 0)
AND CAST("year" AS INT) BETWEEN 2005 AND 2024
GROUP BY county_fips, state_fips
)
SELECT COUNT(*) AS n_counties,
SUM(early_count) AS total_early,
SUM(recent_count) AS total_recent,
AVG(early_count) AS avg_early,
AVG(recent_count) AS avg_recent,
median(recent_count - early_count) AS median_change,
AVG(CAST(recent_count AS DOUBLE) - early_count) AS avg_change,
SUM(CASE WHEN recent_count > early_count THEN 1 ELSE 0 END) AS n_increased,
SUM(CASE WHEN recent_count < early_count THEN 1 ELSE 0 END) AS n_decreased,
SUM(CASE WHEN recent_count = early_count THEN 1 ELSE 0 END) AS n_unchanged
FROM county_countsquery — 22 rows — 4383 ms
SELECT CAST("year" AS INT) AS yr,
SUM(CASE WHEN cz_type='C' THEN 1 ELSE 0 END) AS c_type,
SUM(CASE WHEN cz_type='Z' THEN 1 ELSE 0 END) AS z_type,
COUNT(*) AS total,
SUM(CASE WHEN cz_type='C' AND (damage_property>0 OR damage_crops>0 OR deaths_direct>0 OR deaths_indirect>0 OR injuries_direct>0 OR injuries_indirect>0) THEN 1 ELSE 0 END) AS c_damaging
FROM disasters.storm_events
WHERE CAST("year" AS INT) BETWEEN 2004 AND 2025
GROUP BY CAST("year" AS INT)
ORDER BY yrquery — 1 rows — 17764 ms
WITH years(yr) AS (
VALUES (2005),(2006),(2007),(2008),(2009),(2010),(2011),(2012),(2013),(2014),
(2015),(2016),(2017),(2018),(2019),(2020),(2021),(2022),(2023),(2024)
),
counties AS (SELECT DISTINCT county_fips, state_fips FROM disasters.storm_events WHERE cz_type='C' AND county_fips IS NOT NULL),
grid AS (SELECT c.county_fips, c.state_fips, y.yr FROM counties c CROSS JOIN years y),
actual AS (
SELECT county_fips, CAST("year" AS INT) AS yr, COUNT(*) AS cnt
FROM disasters.storm_events
WHERE cz_type='C' AND county_fips IS NOT NULL
AND (damage_property>0 OR damage_crops>0 OR deaths_direct>0 OR deaths_indirect>0 OR injuries_direct>0 OR injuries_indirect>0)
AND CAST("year" AS INT) BETWEEN 2005 AND 2024
GROUP BY county_fips, CAST("year" AS INT)
),
county_slopes AS (
SELECT g.county_fips, g.state_fips,
regr_slope(COALESCE(a.cnt,0), g.yr) AS slope,
AVG(COALESCE(a.cnt,0)) AS avg_annual
FROM grid g LEFT JOIN actual a ON g.county_fips=a.county_fips AND g.yr=a.yr
GROUP BY g.county_fips, g.state_fips
)
SELECT
COUNT(*) AS n_counties,
SUM(CASE WHEN slope>0 THEN 1 ELSE 0 END) AS n_pos_slope,
SUM(CASE WHEN slope<0 THEN 1 ELSE 0 END) AS n_neg_slope,
AVG(slope) AS avg_slope,
median(slope) AS median_slope
FROM county_slopesquery — 15 rows — 17747 ms
WITH years(yr) AS (
VALUES (2005),(2006),(2007),(2008),(2009),(2010),(2011),(2012),(2013),(2014),
(2015),(2016),(2017),(2018),(2019),(2020),(2021),(2022),(2023),(2024)
),
counties AS (SELECT DISTINCT county_fips, state_fips FROM disasters.storm_events WHERE cz_type='C' AND county_fips IS NOT NULL),
grid AS (SELECT c.county_fips, c.state_fips, y.yr FROM counties c CROSS JOIN years y),
actual AS (
SELECT county_fips, CAST("year" AS INT) AS yr, COUNT(*) AS cnt
FROM disasters.storm_events
WHERE cz_type='C' AND county_fips IS NOT NULL
AND (damage_property>0 OR damage_crops>0 OR deaths_direct>0 OR deaths_indirect>0 OR injuries_direct>0 OR injuries_indirect>0)
AND CAST("year" AS INT) BETWEEN 2005 AND 2024
GROUP BY county_fips, CAST("year" AS INT)
),
county_slopes AS (
SELECT g.county_fips, g.state_fips,
regr_slope(COALESCE(a.cnt,0), g.yr) AS slope,
AVG(COALESCE(a.cnt,0)) AS avg_annual
FROM grid g LEFT JOIN actual a ON g.county_fips=a.county_fips AND g.yr=a.yr
GROUP BY g.county_fips, g.state_fips
HAVING AVG(COALESCE(a.cnt,0)) >= 1
)
SELECT county_fips, state_fips, slope, avg_annual
FROM county_slopes
ORDER BY slope DESC
LIMIT 15query — 15 rows — 19535 ms
WITH years(yr) AS (
VALUES (2005),(2006),(2007),(2008),(2009),(2010),(2011),(2012),(2013),(2014),
(2015),(2016),(2017),(2018),(2019),(2020),(2021),(2022),(2023),(2024)
),
counties AS (SELECT DISTINCT county_fips, state_fips FROM disasters.storm_events WHERE cz_type='C' AND county_fips IS NOT NULL),
grid AS (SELECT c.county_fips, c.state_fips, y.yr FROM counties c CROSS JOIN years y),
actual AS (
SELECT county_fips, CAST("year" AS INT) AS yr, COUNT(*) AS cnt
FROM disasters.storm_events
WHERE cz_type='C' AND county_fips IS NOT NULL
AND (damage_property>0 OR damage_crops>0 OR deaths_direct>0 OR deaths_indirect>0 OR injuries_direct>0 OR injuries_indirect>0)
AND CAST("year" AS INT) BETWEEN 2005 AND 2024
GROUP BY county_fips, CAST("year" AS INT)
),
county_slopes AS (
SELECT g.county_fips, g.state_fips,
regr_slope(COALESCE(a.cnt,0), g.yr) AS slope,
AVG(COALESCE(a.cnt,0)) AS avg_annual
FROM grid g LEFT JOIN actual a ON g.county_fips=a.county_fips AND g.yr=a.yr
GROUP BY g.county_fips, g.state_fips
HAVING AVG(COALESCE(a.cnt,0)) >= 1
)
SELECT county_fips, state_fips, slope, avg_annual
FROM county_slopes
ORDER BY slope ASC
LIMIT 15query — 55 rows — 19104 ms
SELECT state_fips, COUNT(*) AS n_counties,
SUM(CASE WHEN CAST("year" AS INT) BETWEEN 2005 AND 2014 THEN 1 ELSE 0 END) AS early,
SUM(CASE WHEN CAST("year" AS INT) BETWEEN 2015 AND 2024 THEN 1 ELSE 0 END) AS recent
FROM disasters.storm_events
WHERE cz_type='C' AND county_fips IS NOT NULL
AND (damage_property>0 OR damage_crops>0 OR deaths_direct>0 OR deaths_indirect>0 OR injuries_direct>0 OR injuries_indirect>0)
AND CAST("year" AS INT) BETWEEN 2005 AND 2024
GROUP BY state_fips
ORDER BY (SUM(CASE WHEN CAST("year" AS INT) BETWEEN 2015 AND 2024 THEN 1 ELSE 0 END) - SUM(CASE WHEN CAST("year" AS INT) BETWEEN 2005 AND 2014 THEN 1 ELSE 0 END)) DESCSources
- disasters.storm_events — county-type damaging events by year, national — askamerica table
Show SQL
SELECT CAST("year" AS INT) AS yr, SUM(CASE WHEN cz_type='C' AND (damage_property>0 OR damage_crops>0 OR deaths_direct>0 OR deaths_indirect>0 OR injuries_direct>0 OR injuries_indirect>0) THEN 1 ELSE 0 END) AS c_damaging FROM disasters.storm_events WHERE CAST("year" AS INT) BETWEEN 2004 AND 2025 GROUP BY CAST("year" AS INT) ORDER BY yr - disasters.storm_events — county-level linear trend slopes, 2005-2024 (zero-filled) — askamerica table; n=3,236 counties
Show SQL
WITH years(yr) AS (VALUES (2005),(2006),(2007),(2008),(2009),(2010),(2011),(2012),(2013),(2014),(2015),(2016),(2017),(2018),(2019),(2020),(2021),(2022),(2023),(2024)), counties AS (SELECT DISTINCT county_fips, state_fips FROM disasters.storm_events WHERE cz_type='C' AND county_fips IS NOT NULL), grid AS (SELECT c.county_fips, c.state_fips, y.yr FROM counties c CROSS JOIN years y), actual AS (SELECT county_fips, CAST("year" AS INT) AS yr, COUNT(*) AS cnt FROM disasters.storm_events WHERE cz_type='C' AND county_fips IS NOT NULL AND (damage_property>0 OR damage_crops>0 OR deaths_direct>0 OR deaths_indirect>0 OR injuries_direct>0 OR injuries_indirect>0) AND CAST("year" AS INT) BETWEEN 2005 AND 2024 GROUP BY county_fips, CAST("year" AS INT)) SELECT g.county_fips, g.state_fips, regr_slope(COALESCE(a.cnt,0), g.yr) AS slope, AVG(COALESCE(a.cnt,0)) AS avg_annual FROM grid g LEFT JOIN actual a ON g.county_fips=a.county_fips AND g.yr=a.yr GROUP BY g.county_fips, g.state_fips - NCEI: Monitoring and Understanding Trends in Extreme Storms — Confirms non-uniform historical reporting practice for severe convective storms
- Storm Watch: Trends in Extreme US Weather Events — Resources for the Future — Discusses Storm Events Database scope evolution and reporting-practice changes over decades
- omissions and inconsistencies in flood damage reporting — Univ. Colorado / NWS — Documents inconsistent damage-estimate quality across NWS field offices over time