Average EV Range Comes Out Too Low: Zero Means Not Researched

On this page

Problem statement

Average the Electric Range column of Washington's EV registrations and you get 36.1 miles, absurdly low for a fleet that is four-fifths battery-electric. 65.7% of rows have a range of 0, and a zero does not mean the car cannot drive on electricity. It means the state has not looked up that model's range. Since model year 2021 almost every new vehicle has been left at 0. This page shows how the zeros follow model year, how the eligibility column gives them away, and how to fill them from vehicles of the same make and model.

Quick answer

Replace Electric Range == 0 with missing, not zero. Where you need a value, fill it from the median range of the same make, model and vehicle type with a known range, and say how many rows remain without one. Never average the raw column.

r = ev.range_mi
print(f"mean electric range of all rows: {r.mean():.1f} miles; rows with range 0: {(r == 0).mean():.1%}; "
      f"mean where range > 0: {r[r > 0].mean():.1f} miles")
mean electric range of all rows: 36.1 miles; rows with range 0: 65.7%; mean where range > 0: 105.3 miles

The raw mean is 36.1 miles because 65.7% of rows are 0. Among rows with a recorded range the mean is 105.3 miles, which still mixes short-range plug-in hybrids with battery-electric cars.

Histogram on a log scale of the Electric Range column, with one enormous bar at zero labelled 197,085 vehicles and a spread of smaller bars across the rest of the range. Histogram on a log scale of the Electric Range column, with one enormous bar at zero labelled 197,085 vehicles and a spread of smaller bars across the rest of the range.
The raw range column: 197,085 zeros.

Finding the zeros and filling them

1. Zero follows the model year, not the car

z = ev.assign(zero=ev.range_mi == 0).groupby("model_year").zero.mean()
print(z.loc[2015:2027].round(3).to_dict())
{2015: 0.0, 2016: 0.0, 2017: 0.0, 2018: 0.0, 2019: 0.0, 2020: 0.006, 2021: 0.754, 2022: 0.832, 2023: 0.879, 2024: 0.781, 2025: 0.795, 2026: 0.884, 2027: 0.951}

No vehicle up to model year 2019 has a zero range, and only 0.6% of 2020 models. From 2021 the share jumps to 75.4% and stays near 80–95% (88.4% for 2026, 95.1% for 2027). The zeros mark when the state stopped researching ranges for new models, not anything about the cars.

2. The eligibility column says so

print(pd.crosstab(ev.range_mi == 0, ev.cafv).to_string())
cafv      Clean Alternative Fuel Vehicle Eligible  Eligibility unknown as battery range has not been researched  Not eligible due to low battery range
range_mi                                                                                                                                              
False                                       78933                                                             0                                  24124
True                                            0                                                        197085                                      0

The eligibility column confirms it. Every one of the 197,085 zero-range rows is "Eligibility unknown as battery range has not been researched", and no row with a range has that label. The two columns are generated together: a zero range is a missing range.

Bar chart of the share of vehicles with Electric Range of zero by model year: none up to 2019, then 75% to 95% from 2021 on. Bar chart of the share of vehicles with Electric Range of zero by model year: none up to 2019, then 75% to 95% from 2021 on.
Zero range by model year: from 2021 almost every model is 'not researched'.

3. Fill from the same model where you can

known = ev[ev.range_mi > 0].groupby(["make", "model", "kind"]).range_mi.median()
key = pd.MultiIndex.from_frame(ev[["make", "model", "kind"]])
filled = ev.range_mi.where(ev.range_mi > 0, pd.Series(known.reindex(key).values, index=ev.index))
print(f"after filling from the same make and model: {filled.notna().mean():.1%} have a range, mean {filled.mean():.1f} miles")
after filling from the same make and model: 71.0% have a range, mean 184.0 miles

Filling each zero from the median range of the same make, model and type gives 71.0% of rows a range, with a mean of 184.0 miles, a plausible figure for a mostly battery-electric fleet. The rest are models introduced after 2020 that never had a researched range on any vehicle, such as most 2021-and-later Tesla Model Ys.

One model, and missing values

The data used on this page

The Washington State Department of Licensing's Electric Vehicle Population Data, saved on this site on 15 October 2026.

from pathlib import Path

import geopandas as gpd
import numpy as np
import pandas as pd
import requests

Path("data").mkdir(exist_ok=True)
# Washington State Department of Licensing, via data.wa.gov: Electric Vehicle Population Data (f6w7-q2d2), one row per
# registered battery-electric or plug-in hybrid vehicle, and Electric Vehicle Population Size History By County (3d5d-sdqb),
# monthly counts since 2017. Both are replaced monthly, so they are read from copies saved on this site on 15 October 2026.
FILES = ["wa-ev-population-2026-10-15.zip", "wa-ev-population-history-2026-10-15.zip"]
for name in FILES:
    if not Path(f"data/{name}").exists():
        Path(f"data/{name}").write_bytes(requests.get(f"https://data.spatialworkflow.io/{name}", timeout=600).content)
NAMES = {"VIN (1-10)": "vin10", "County": "county", "City": "city", "State": "state", "Postal Code": "zip", "Model Year": "model_year",
         "Make": "make", "Model": "model", "Electric Vehicle Type": "ev_type", "Clean Alternative Fuel Vehicle (CAFV) Eligibility": "cafv",
         "Electric Range": "range_mi", "Legislative District": "district", "DOL Vehicle ID": "dol_id", "Vehicle Location": "location",
         "Electric Utility": "utility", "2020 GEOID": "tract"}
ev = pd.read_csv("data/wa-ev-population-2026-10-15.zip", dtype={"Postal Code": str, "2020 GEOID": str}).rename(columns=NAMES)
ev["kind"] = ev.ev_type.str.extract(r"\((BEV|PHEV)\)")[0]
hist = pd.read_csv("data/wa-ev-population-history-2026-10-15.zip")
hist["date"] = pd.to_datetime(hist.Date, format="%B %d %Y")
print(f"{len(ev):,} electric vehicles registered with Washington DOL; history: {hist.date.min():%b %Y} to {hist.date.max():%b %Y}")
300,142 electric vehicles registered with Washington DOL; history: Jan 2017 to Sep 2026

Example 1: the same model, two answers

my = ev[(ev.make == "TESLA") & (ev.model == "MODEL Y")]
print(my.groupby("model_year").range_mi.agg(lambda s: f"{(s == 0).mean():.0%} zero, max {s.max()}").loc[2020:2026].to_dict())
{2020: '0% zero, max 291.0', 2021: '100% zero, max 0.0', 2022: '100% zero, max 0.0', 2023: '100% zero, max 0.0', 2024: '100% zero, max 0.0', 2025: '100% zero, max 0.0', 2026: '100% zero, max 0.0'}

The Tesla Model Y shows the pattern exactly. Its 2020 model year has a researched range (up to 291 miles) and no zeros. From 2021 every Model Y is 0. Filling across model years would give 2026 cars the 2020 range, which is wrong in a different way. For range-dependent analysis, a separate table of EPA ranges by model and year is the reliable source.

Bar chart of registered Tesla Model Ys by model year from 2020 to 2026, the 2020 bar labelled 0% zero and every later bar 100% zero. Bar chart of registered Tesla Model Ys by model year from 2020 to 2026, the 2020 bar labelled 0% zero and every later bar 100% zero.
The Tesla Model Y: a researched range for 2020, zero for every later model year.

Example 2: missing, not zero

clean = ev.range_mi.replace(0, np.nan)
print(f"blank ranges: {ev.range_mi.isna().sum()}; zeros turned to missing: {clean.isna().sum() - ev.range_mi.isna().sum():,}")
print(clean.groupby(ev.kind).median().to_dict())
blank ranges: 35; zeros turned to missing: 197,085
{'BEV': 215.0, 'PHEV': 32.0}

35 rows are blank, distinct from the zeros. Treating the 197,085 zeros as missing gives median ranges of 215 miles for BEVs and 32 for PHEVs, the figures to quote.

Why the range stopped being filled

The range column fed Washington's Clean Alternative Fuel Vehicle eligibility, which required 30 miles of all-electric range for a sales-tax exemption. The state looked up each model's EPA range to decide eligibility. For nearly all models from 2021 on, that lookup has not been done, and the file records them as 0 with the "not researched" label rather than leaving the field blank. The data is correct for its original purpose and misleading for any other.

Related traps

  • Grouping by kind first. Fill BEVs from BEVs; a model sold in both versions (some Kia and Hyundai models) has very different ranges.
  • Model year changes range. Battery upgrades mean a 2018 and a 2024 car of the same model differ.
  • Range is EPA-rated, not real-world, and falls in cold weather.
  • Any average of the raw column published from this file is wrong; check the source before quoting one.

FAQ

Why do so many EVs in the Washington data have an electric range of 0?

Because the state has not researched the range for most models since model year 2021. All 197,085 zero-range rows are labelled "battery range has not been researched".

What is the average range of electric vehicles in Washington?

The median recorded range is 215 miles for battery-electric vehicles and 32 for plug-in hybrids, once the zeros are treated as missing.