首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >你怎么知道我深蹲100斤了?学习笔记 | metpy插值气压层时需要注意什么

你怎么知道我深蹲100斤了?学习笔记 | metpy插值气压层时需要注意什么

作者头像
用户11172986
发布2026-04-24 18:54:56
发布2026-04-24 18:54:56
780
举报
文章被收录于专栏:气python风雨气python风雨

metpy 插值到等压面总是报错?90 %的人都忽略了单位这件事——一次 Xarray 维度踩坑全记录

环境设置

安装依赖

代码语言:javascript
复制
!pip install metpy

正文

近来又潮又热,去江苏出差的朋友感慨江苏怎么还没北京潮,还有读者在评论区发了北京已有广东特有的美洲大蠊,真是奇哉怪哉。

言归正传,如果你在气象数据处理圈子里混,多多少少肯定碰过这段代码。没碰过也没关系,现在碰过了—— 把模式层数据插值到指定等压面:

代码语言:javascript
复制
import metpy.interpolate as mpint  

plevs = np.array([1000, 850, 700, 500])*100    # Pa  
new_u = mpint.log_interpolate_1d(  
            plevs, pressure, u, axis=1)  

很多时候,跑得好好的脚本,某天突然蹦出一段让人摸不着头脑的报错:

代码语言:javascript
复制
TypeError: no implementation found for 'numpy.apply_along_axis'  

或者:

代码语言:javascript
复制
ValueError: Cannot apply units on ...  

别慌,这其实不是 bug,而是 90 % 的人都没有掌握的单位+维度“潜规则”。实际上就是metpy老掉牙的单位问题。今天咱们一次性说清楚。


01 场景复现:到底哪一步炸裂?

官方 issue #1889 给了一个最简单、却最容易忽略的复现:

  • Xarray DataArray ➜ 4 维(time, lev, lat, lon)
  • Numpy ndarray ➜ 同样形状

前者直接炸;后者顺溜过关。

这是为什么呢

02 背后真相:Xarray 带来的隐藏属性

输入类型

has units?

has coords?

apply_along_axis 是否能识别

ndarray

✅ 直接上裸数组

DataArray

maybe

⚠️ metpy 必须拆 unit,否则 numpy 拒接

metpy 为了保持单位一致性,会在内部把 pressure 先变成 pint.Quantity。 但 new_levels 是裸 numpy,两者单位不统一 → _strip_matching_units() 拆不掉 → 最后一步 log_interpolate_1d 掉到 apply_along_axis,numpy 见到带 unit 的数组直接罢工。


03 三分钟排查清单

所有输入都要有单位

代码语言:javascript
复制
from metpy.units import units  
plevs = (np.array([...,...,...])*100) * units.Pa   # 千万别省 * units  

如果懒得写单位?那就全部无量纲

代码语言:javascript
复制
plevs = plevs * units('')  
pressure = pressure.metpy.quantify()  

.metpy.dequantify() 随时把数据剥干净 仅当你确定后续不再依赖单位,或者喂给其它裸 numpy 库。

维度顺序要一致

代码语言:javascript
复制
pressure = pressure.transpose(*u.dims)  

04 实战示例

代码语言:javascript
复制
import xarray as xr
import numpy as np
from metpy.interpolate import log_interpolate_1d
from metpy.units import units

# 读取数据并确保单位系统
ds = xr.open_dataset('atmos.nc', decode_times=False)
print(ds)
代码语言:javascript
复制
<xarray.Dataset> Size: 58MB
Dimensions:      (ilev: 31, lev: 30, lat: 64, lon: 128, time: 1)
Coordinates:
  * lev          (lev) float32 120B 3.643 7.595 14.36 ... 957.5 976.3 992.6
  * ilev         (ilev) float32 124B 2.255 5.032 10.16 ... 967.5 985.1 1e+03
  * lat          (lat) float32 256B -87.86 -85.1 -82.31 ... 82.31 85.1 87.86
  * lon          (lon) float32 512B 0.0 2.812 5.625 8.438 ... 351.6 354.4 357.2
  * time         (time) float64 8B 791.0
Data variables: (12/130)
    P0           float32 4B ...
    ntrm         int32 4B ...
    ntrn         int32 4B ...
    ntrk         int32 4B ...
    ndbase       int32 4B ...
    nsbase       int32 4B ...
    ...           ...
    TAUX         (time, lat, lon) float32 33kB ...
    TAUY         (time, lat, lon) float32 33kB ...
    SNOWH        (time, lat, lon) float32 33kB ...
    UTGW         (time, lev, lat, lon) float32 983kB ...
    VTGW         (time, lev, lat, lon) float32 983kB ...
    VVPUU        (time, lev, lat, lon) float32 983kB ...
Attributes:
    Conventions:            NCAR-CSM
    source:                 Data converted from CCM History Tape Format
    case:                   prgcld01
    title:                  Phils prognostic cld water + tendency physics code
    hybrid_sigma_pressure:  \nPressure at a grid point (lon(i),lat(j),lev(k))...
    history:                \nMon May 17 12:31:49 1999> 
代码语言:javascript
复制

u = ds.U * units('m/s')  # 强制添加单位(如果原始数据没有)
ps = ds.PS * units.Pa    # 表面气压必须带单位

# 系数处理(CAM模式常用)
hyam = ds.hyam * units('dimensionless')
hybm = ds.hybm * units('dimensionless')
p0 = 1000 * units.hPa

# 计算全层气压
pressure = hyam * p0 + hybm * ps
pressure = pressure.transpose(*u.dims) 

# 创建等压面层级(hPa转Pa)
new_levels = np.array([1000, 950, 800, 700, 600, 500, 400,200]) * units.hPa
# 对数插值
u_isobaric = log_interpolate_1d(new_levels, pressure, u.metpy.quantify(), axis=1)
# 验证结果
print("插值结果单位:", u_isobaric.units)  # 应该显示 "meter / second"
print("NaN值数量:", np.isnan(u_isobaric).sum())  # 检查异常值
代码语言:javascript
复制
插值结果单位: meter / second
NaN值数量: 7466
代码语言:javascript
复制
print(u_isobaric[0,0])
代码语言:javascript
复制
[[nan nan nan ... nan nan nan] [nan nan nan ... nan nan nan] [nan nan nan ... nan nan nan] ... [-2.44014092908361 -2.5727052585667027 -2.6622389092978307 ... nan  -2.010691574429729 -2.256805604584203] [-2.30256332842527 -2.2468413152222126 -2.181935846149208 ...  -2.3940958331870448 -2.3783463214362417 -2.3471175183966806] [-2.0253718473951325 -1.9608527770423514 -1.8944898069814329 ...  -2.203676466735914 -2.1471989814030255 -2.0876272126679476]] meter / second
代码语言:javascript
复制

代码语言:javascript
复制
插值成功

05 一句话总结

metpy 插值到等压面要成功: “单位先行,维度并肩。numpy 才能开心当打工人。”

把上面的排查清单贴进 README,妈妈再也不担心我插值报错了!

话说回来妈妈也不用担心这个就是了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-08-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 气python风雨 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • metpy 插值到等压面总是报错?90 %的人都忽略了单位这件事——一次 Xarray 维度踩坑全记录
    • 环境设置
      • 安装依赖
    • 正文
    • 01 场景复现:到底哪一步炸裂?
    • 02 背后真相:Xarray 带来的隐藏属性
    • 03 三分钟排查清单
    • 04 实战示例
    • 05 一句话总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档