KeyError: 'lon',解决这个问题
时间: 2025-08-01 18:26:54 浏览: 16
<think>我们遇到了KeyError:'lon',这意味着在数据集ds中不存在名为'lon'的变量。这通常是因为不同的数据集对经度、纬度的命名可能不同。常见的替代名称有:-经度:'longitude','x','long'-纬度:'latitude','y','lat'-时间:'time','t'-气压层:'level','pressure','lev'解决方法:1.检查数据集中的变量名:我们可以打印出数据集的所有变量名,然后选择正确的经度、纬度变量名。2.使用通用方法:尝试常见的变量名。我们可以先打印出数据集中的变量名,然后根据打印结果调整代码。修改后的代码(在读取数据后添加打印):```pythonprint(ds.data_vars)#打印所有数据变量print(ds.coords)#打印所有坐标变量```通常,经度和纬度可能是坐标变量(coords)也可能是数据变量(data_vars)。我们可以根据打印结果调整变量名。假设打印结果中经度名为'longitude',纬度名为'latitude',那么我们需要将代码中的'lon'改为'longitude','lat'改为'latitude'。如果经度纬度变量名确实不同,我们需要修改提取部分的代码。以下是修改后的代码示例(假设变量名为'longitude'和'latitude'):```pythonimportxarrayasxrimportnumpyasnpimportmatplotlib.pyplotaspltimportcartopy.crsasccrsimportcartopy.featureascfeatureimportmetpy.calcasmpcalcfrommetpy.unitsimportunits#读取NetCDF文件file_path=r'C:/Users/25636/Desktop/bi/ziji/zhengduanfx/first/ziliao/200307.nc'ds=xr.open_dataset(file_path)#打印变量名以便调试print("数据变量:",ds.data_vars.keys())print("坐标变量:",ds.coords.keys())#根据实际变量名调整#假设经度名为'longitude',纬度名为'latitude'lons=ds['longitude'].valueslats=ds['latitude'].values#提取其他变量,同样需要根据实际名称调整q=ds['q'].isel(time=0)*units('kg/kg')u=ds['u'].isel(time=0)*units('m/s')v=ds['v'].isel(time=0)*units('m/s')levels=ds['level'].values#选择850hPa层level_idx=np.where(levels==850)[0][0]q_850=q[level_idx]u_850=u[level_idx]v_850=v[level_idx]#...其余代码保持不变...```如果变量名确实不同,请根据打印结果替换相应的变量名。如果经度纬度在坐标变量中,我们也可以使用ds.coords['longitude']来获取。另外,如果经度纬度是多维的(例如,可能是二维数组),那么处理方法会有所不同。但在大多数气象数据中,经度和纬度通常是一维的。如果经度纬度是二维的,我们可以这样获取:```pythonlons=ds['longitude'].valueslats=ds['latitude'].values#注意:此时lons和lats是二维数组,需要确保后续计算网格间距时使用正确的坐标#metpy的lat_lon_grid_deltas函数可以处理二维经纬度数组```如果问题仍然存在,请根据具体的错误信息调整。###另一种方法:重命名变量如果不想修改后续代码中的变量名,我们可以重命名数据集中的变量:```pythonds=ds.rename({'longitude':'lon','latitude':'lat'})```这样,后续就可以继续使用`ds['lon']`和`ds['lat']`了。###完整修改后的代码(使用重命名方法):```pythonimportxarrayasxrimportnumpyasnpimportmatplotlib.pyplotaspltimportcartopy.crsasccrsimportcartopy.featureascfeatureimportmetpy.calcasmpcalcfrommetpy.unitsimportunits#读取NetCDF文件file_path=r'C:/Users/25636/Desktop/bi/ziji/zhengduanfx/first/ziliao/200307.nc'ds=xr.open_dataset(file_path)#尝试重命名变量rename_dict={}#检查常见的经度名称if'longitude'inds.coordsor'longitude'inds.data_vars:rename_dict['longitude']='lon'if'long'inds.coordsor'long'inds.data_vars:rename_dict['long']='lon'if'x'inds.coordsor'x'inds.data_vars:rename_dict['x']='lon'#检查常见的纬度名称if'latitude'inds.coordsor'latitude'inds.data_vars:rename_dict['latitude']='lat'if'y'inds.coordsor'y'inds.data_vars:rename_dict['y']='lat'if'lat'inds.coordsor'lat'inds.data_vars:#如果已经是lat,就不需要重命名passelse:#如果上面都没有,那么尝试其他可能,这里只是示例,需要根据实际情况添加pass#执行重命名ifrename_dict:ds=ds.rename(rename_dict)#现在尝试提取lon和lattry:lons=ds['lon'].valueslats=ds['lat'].valuesexceptKeyErrorase:#如果重命名后还是没有,打印可用变量print("重命名后仍找不到经度或纬度变量。")print("可用坐标变量:",list(ds.coords.keys()))print("可用数据变量:",list(ds.data_vars.keys()))raisee#提取其他变量,同样需要根据实际名称调整q=ds['q'].isel(time=0)*units('kg/kg')u=ds['u'].isel(time=0)*units('m/s')v=ds['v'].isel(time=0)*units('m/s')levels=ds['level'].values#选择850hPa层level_idx=np.where(levels==850)[0][0]q_850=q[level_idx]u_850=u[level_idx]v_850=v[level_idx]#计算水汽通量qu=q_850*u_850*100#乘以100转换为常用单位kg/(m·s)qv=q_850*v_850*100#计算水汽通量大小qv_magnitude=np.sqrt(qu**2+qv**2)#计算水汽通量散度dx,dy=mpcalc.lat_lon_grid_deltas(lons,lats)#计算网格间距div_qv=mpcalc.divergence(qu,qv,dx=dx,dy=dy)*1e6#转换为10^-6kg/(m²·s)#创建地图投影proj=ccrs.PlateCarree()#绘制水汽通量图plt.figure(figsize=(12,8))ax=plt.axes(projection=proj)ax.add_feature(cfeature.COASTLINE,linewidth=0.8)ax.add_feature(cfeature.BORDERS,linestyle=':',linewidth=0.5)ax.set_extent([lons.min(),lons.max(),lats.min(),lats.max()],crs=proj)#使用Matplotlib的'Blues'colormapcf=ax.contourf(lons,lats,qv_magnitude,levels=30,cmap='Blues',transform=proj)plt.colorbar(cf,ax=ax,orientation='horizontal',pad=0.05,aspect=50,label='WaterVaporFluxMagnitude(kg/(m·s))')#绘制风矢量(每5个点取一个)step=5ax.quiver(lons[::step],lats[::step],qu.values[::step,::step],qv.values[::step,::step],scale=300,color='black',transform=proj)ax.set_title('850hPaWaterVaporFluxandVector',fontsize=16)plt.savefig('water_vapor_flux.png',dpi=300,bbox_inches='tight')#绘制水汽通量散度图plt.figure(figsize=(12,8))ax2=plt.axes(projection=proj)ax2.add_feature(cfeature.COASTLINE,linewidth=0.8)ax2.add_feature(cfeature.BORDERS,linestyle=':',linewidth=0.5)ax2.set_extent([lons.min(),lons.max(),lats.min(),lats.max()],crs=proj)#绘制散度填色图div_levels=np.linspace(-5,5,21)cf2=ax2.contourf(lons,lats,div_qv,levels=div_levels,cmap='RdBu_r',extend='both',transform=proj)plt.colorbar(cf2,ax=ax2,orientation='horizontal',pad=0.05,aspect=50,label='WaterVaporFluxDivergence(10$^{-6}$kg/(m$^{2}$·s))')#添加零散度线ax2.contour(lons,lats,div_qv,levels=[0],colors='k',linewidths=1.5,transform=proj)ax2.set_title('850hPaWaterVaporFluxDivergence',fontsize=16)plt.savefig('water_vapor_flux_divergence.png',dpi=300,bbox_inches='tight')plt.close('all')```###解释:1.**重命名策略**:我们创建了一个重命名字典,将常见的经度纬度名称(如'longitude','latitude')重命名为代码中使用的'lon'和'lat'。2.**错误处理**:如果重命名后仍然找不到,则打印出所有可用的变量名,并抛出异常。3.**通用性**:这种方法可以处理多种命名情况,提高了代码的通用性。###注意事项:-如果数据集的经度纬度是二维的,上述代码也能处理,因为`mpcalc.lat_lon_grid_deltas`函数可以处理二维经纬度数组。-如果数据集中的变量名与预期相差很大,可能需要根据打印的变量名手动调整。§§相关问题§§:1.在xarray中如何处理不同命名约定的气象数据变量?2.如何自动检测和重命名数据集中的坐标变量?3.当气象数据集的经度纬度是二维数组时,如何正确计算网格间距?4.在Cartopy中如何设置不同区域的地图投影?</think>当遇到 `KeyError: 'lon'` 错误时,这通常表示你的 NetCDF 文件中没有名为 'lon' 的变量。这可能是由于文件使用了不同的命名约定。以下是详细的解决方案:
### 解决方案
#### 1. 检查文件中的实际变量名
首先,我们需要查看文件中实际存在的变量名:
```python
import xarray as xr
file_path = r'C:/Users/25636/Desktop/bi/ziji/zhengduanfx/first/ziliao/200307.nc'
ds = xr.open_dataset(file_path)
# 打印所有变量名
print("数据集变量:", list(ds.variables.keys()))
# 打印坐标变量
print("\n坐标变量:", list(ds.coords.keys()))
# 打印数据变量
print("\n数据变量:", list(ds.data_vars.keys()))
```
#### 2. 根据实际变量名调整代码
根据上一步的输出结果,调整变量名。以下是常见的替代名称:
```python
# 常见的经度变量名替代方案
possible_lon_names = ['longitude', 'lon', 'x', 'long', 'Longitude', 'LONGITUDE']
possible_lat_names = ['latitude', 'lat', 'y', 'Latitude', 'LATITUDE']
# 找到实际使用的变量名
actual_lon_name = None
actual_lat_name = None
for name in possible_lon_names:
if name in ds.variables:
actual_lon_name = name
break
for name in possible_lat_names:
if name in ds.variables:
actual_lat_name = name
break
if actual_lon_name is None or actual_lat_name is None:
print("错误: 找不到经度或纬度变量")
print(f"可用变量: {list(ds.variables.keys())}")
exit(1)
# 使用找到的变量名
lons = ds[actual_lon_name].values
lats = ds[actual_lat_name].values
# 其他变量同理
q = ds['q'].isel(time=0) * units('kg/kg')
u = ds['u'].isel(time=0) * units('m/s')
v = ds['v'].isel(time=0) * units('m/s')
levels = ds['level'].values
```
#### 3. 完整修正后的代码
将上述解决方案整合到完整代码中:
```python
import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import metpy.calc as mpcalc
from metpy.units import units
# 读取NetCDF文件
file_path = r'C:/Users/25636/Desktop/bi/ziji/zhengduanfx/first/ziliao/200307.nc'
ds = xr.open_dataset(file_path)
# 打印所有变量名(调试用)
print("数据集变量:", list(ds.variables.keys()))
# 查找经纬度变量
possible_lon_names = ['longitude', 'lon', 'x', 'long', 'Longitude', 'LONGITUDE']
possible_lat_names = ['latitude', 'lat', 'y', 'Latitude', 'LATITUDE']
actual_lon_name = next((name for name in possible_lon_names if name in ds.variables), None)
actual_lat_name = next((name for name in possible_lat_names if name in ds.variables), None)
if actual_lon_name is None:
raise KeyError("找不到经度变量。可用变量: " + ", ".join(ds.variables.keys()))
if actual_lat_name is None:
raise KeyError("找不到纬度变量。可用变量: " + ", ".join(ds.variables.keys()))
# 提取变量
lons = ds[actual_lon_name].values
lats = ds[actual_lat_name].values
# 检查维度顺序(有时纬度在前,有时经度在前)
if len(lons.shape) == 1:
# 一维坐标
pass
elif len(lons.shape) == 2:
# 二维坐标网格
if lons.shape[0] == lats.shape[0]:
# 假设第一维度是纬度
lons = lons[0, :] if lons.shape[0] > 1 else lons
lats = lats[:, 0] if lats.shape[1] > 1 else lats
else:
# 转置数组
lons = lons.T[0, :]
lats = lats.T[:, 0]
# 提取其他变量
q = ds['q'].isel(time=0) * units('kg/kg')
u = ds['u'].isel(time=0) * units('m/s')
v = ds['v'].isel(time=0) * units('m/s')
levels = ds['level'].values
# 选择850hPa层
level_idx = np.where(levels == 850)[0][0]
q_850 = q[level_idx]
u_850 = u[level_idx]
v_850 = v[level_idx]
# 计算水汽通量
qu = q_850 * u_850 * 100
qv = q_850 * v_850 * 100
qv_magnitude = np.sqrt(qu**2 + qv**2)
# 计算水汽通量散度
dx, dy = mpcalc.lat_lon_grid_deltas(lons, lats)
div_qv = mpcalc.divergence(qu, qv, dx=dx, dy=dy) * 1e6
# 创建地图投影
proj = ccrs.PlateCarree()
# 绘制水汽通量图
plt.figure(figsize=(12, 8))
ax = plt.axes(projection=proj)
ax.add_feature(cfeature.COASTLINE, linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linestyle=':', linewidth=0.5)
ax.set_extent([lons.min(), lons.max(), lats.min(), lats.max()], crs=proj)
# 绘制水汽通量
cf = ax.contourf(lons, lats, qv_magnitude, levels=30, cmap='Blues', transform=proj)
plt.colorbar(cf, ax=ax, orientation='horizontal', pad=0.05, aspect=50,
label='Water Vapor Flux Magnitude (kg/(m·s))')
# 绘制风矢量
step = max(1, len(lons) // 30) # 自适应步长
ax.quiver(lons[::step], lats[::step],
qu.values[::step, ::step], qv.values[::step, ::step],
scale=300, color='black', transform=proj)
ax.set_title('850hPa Water Vapor Flux and Vector', fontsize=16)
plt.savefig('water_vapor_flux.png', dpi=300, bbox_inches='tight')
# 绘制水汽通量散度图
plt.figure(figsize=(12, 8))
ax2 = plt.axes(projection=proj)
ax2.add_feature(cfeature.COASTLINE, linewidth=0.8)
ax2.add_feature(cfeature.BORDERS, linestyle=':', linewidth=0.5)
ax2.set_extent([lons.min(), lons.max(), lats.min(), lats.max()], crs=proj)
# 绘制散度
div_levels = np.linspace(-5, 5, 21)
cf2 = ax2.contourf(lons, lats, div_qv, levels=div_levels, cmap='RdBu_r',
extend='both', transform=proj)
plt.colorbar(cf2, ax=ax2, orientation='horizontal', pad=0.05, aspect=50,
label='Water Vapor Flux Divergence (10$^{-6}$ kg/(m$^{2}$·s))')
# 添加零散度线
ax2.contour(lons, lats, div_qv, levels=[0], colors='k', linewidths=1.5, transform=proj)
ax2.set_title('850hPa Water Vapor Flux Divergence', fontsize=16)
plt.savefig('water_vapor_flux_divergence.png', dpi=300, bbox_inches='tight')
plt.close('all')
print("绘图完成!")
```
### 问题原因及解释
1. **KeyError 原因**:
- NetCDF 文件使用不同的命名约定(如 'longitude' 代替 'lon')
- 文件可能使用缩写(如 'x'、'y')或大写形式(如 'LON')
- 数据可能使用非标准坐标系
2. **解决方案要点**:
- **变量名探测**:自动尝试常见经纬度变量名
- **维度处理**:处理一维或二维坐标系统
- **错误处理**:提供明确的错误信息,列出可用变量
- **自适应步长**:根据数据密度自动调整矢量密度
3. **特殊情况处理**:
- 如果经纬度是二维数组(网格坐标),代码会自动提取主要维度
- 添加了维度顺序检查,防止纬度经度顺序颠倒
- 使用 `next()` 函数简化变量名查找逻辑
### 其他可能的问题及解决
1. **如果找不到 'q', 'u', 'v' 等变量**:
```python
# 类似方法查找其他变量
possible_q_names = ['q', 'shum', 'specific_humidity', 'SPECHUM']
actual_q_name = next((name for name in possible_q_names if name in ds.variables), None)
if actual_q_name is None:
raise KeyError("找不到比湿变量")
q = ds[actual_q_name].isel(time=0) * units('kg/kg')
```
2. **如果时间维度有问题**:
```python
# 选择第一个时间步长
if 'time' not in ds.dims:
# 尝试其他常见时间维度名
time_dim = next((dim for dim in ['time', 't', 'Time', 'TIME'] if dim in ds.dims), 'time')
else:
time_dim = 'time'
q = ds['q'].isel(**{time_dim: 0}) * units('kg/kg')
```
3. **如果气压层不是 'level'**:
```python
possible_level_names = ['level', 'lev', 'pressure', 'plev']
actual_level_name = next((name for name in possible_level_names if name in ds.dims), 'level')
levels = ds[actual_level_name].values
```
阅读全文
相关推荐



















