数据使用说明
>
NetCDF文件short-to-float解压操作指南
Appearance
Appearance
本文档以 CDO 工具和 Python 脚本语言两种处理方式为例,详细说明将 NetCDF 数据中以 short(int16)整型存储、经 scale_factor / add_offset 编码的变量还原为 float 浮点型数据的操作方法。
真实值 = 原始整型值 × scale_factor + add_offsetncdump -h file.nc 查看变量属性,若同时存在以下属性,即为打包存储的 short 数据: scale_factor:缩放比例(浮点数)add_offset:偏移量(浮点数)_FillValue 或 missing_value:缺测值标记为 shortshortscale_factor、add_offset、_FillValue 等打包属性,缺测点转为 NaN 或按目标约定保留。-b 32 强制输出为 32 位浮点型(-b 64 为双精度)。# 解压为 32 位 float,输出 NetCDF4 格式
cdo -b 32 -f nc4 copy input.nc output.nc参数说明:
-b 32:输出精度为 32 位浮点(float)-b 64:输出精度为 64 位浮点(double)copy:原样复制并完成解码/类型转换-f nc4:输出 NetCDF4 格式(可省略,默认即为 NetCDF3/4 由环境决定)# 变量类型应由 short 变为 float,且不再有 scale_factor/add_offset 属性
ncdump -h output.ncxarray 默认按 CF 约定解码(自动应用 scale_factor/add_offset、将 _FillValue 转为 NaN),读入即为 float,直接写出即可:
import xarray as xr
def unpack_nc(input_nc, output_nc):
ds = xr.open_dataset(input_nc) # 默认 decode_cf=True,读入即为 float
ds.to_netcdf(output_nc) # 写出后打包属性已被移除
ds.close()
if __name__ == "__main__":
input_nc = "./u100m.nc" # 需要解压的 nc 数据
output_nc = "./u100m_float.nc" # 解压后的输出文件
unpack_nc(input_nc, output_nc)若需要自行控制缺测值处理方式,可用 netCDF4 库直接读取原始 short 数据,手工套用公式还原后写入新文件:
from netCDF4 import Dataset
import numpy as np
def unpack_nc_manual(input_nc, output_nc, var_name):
with Dataset(input_nc, "r") as src, \
Dataset(output_nc, "w", format="NETCDF4") as dst:
# 复制全局属性
dst.setncatts({
k: src.getncattr(k)
for k in src.ncattrs()
})
# 复制维度
for name, dim in src.dimensions.items():
dst.createDimension(
name,
None if dim.isunlimited() else len(dim)
)
for name, var in src.variables.items():
attrs = {
k: var.getncattr(k)
for k in var.ncattrs()
}
# 关闭自动 mask 和 scale,确保读取原始存储值
var.set_auto_maskandscale(False)
data = var[:]
if name == var_name:
# 从原始打包数据中识别缺测值
missing_values = []
for key in ("_FillValue", "missing_value"):
if key in attrs:
missing_values.extend(
np.asarray(attrs[key]).ravel().tolist()
)
valid = np.ones(data.shape, dtype=bool)
for value in missing_values:
valid &= data != value
# 读取打包参数
scale = attrs.pop("scale_factor", 1.0)
offset = attrs.pop("add_offset", 0.0)
attrs.pop("_FillValue", None)
attrs.pop("missing_value", None)
# 转换为浮点值并解包
unpacked = data.astype(np.float32)
unpacked *= scale
unpacked += offset
# 缺测值直接设为 NaN
unpacked[~valid] = np.nan
# 创建浮点变量,显式将 NaN 设为填充值
new_var = dst.createVariable(
name,
"f4",
var.dimensions,
fill_value=np.float32(np.nan)
)
new_var.setncatts(attrs)
new_var[:] = unpacked
else:
# 其他变量按原始存储值复制
fill_value = attrs.pop("_FillValue", None)
kwargs = {}
if fill_value is not None:
kwargs["fill_value"] = fill_value
new_var = dst.createVariable(
name,
var.dtype,
var.dimensions,
**kwargs
)
new_var.setncatts(attrs)
new_var[:] = data
if __name__ == "__main__":
unpack_nc_manual(
input_nc="./u100m.nc",
output_nc="./u100m_manual.nc",
var_name="u100m",
)说明:
var[:] 返回的即原始 short 整型值,因此乘加公式是手动执行"f4" 表示 32 位双精度,如需 64 位浮点可改为 "f8"var.dimensions 沿用了原变量的维度名,配合前面复制的维度,坐标一致性不受影响import xarray as xr
src = xr.open_dataset("./u100m.nc") # 解压前
dst = xr.open_dataset("./u100m_manual.nc") # 解压后
print(src["u100m"].dtype) # int16 (short)
print(dst["u100m"].dtype) # float32 / float64
print(dst["u100m"].attrs) # 不再包含 scale_factor / add_offset先 concat 再解压有风险——各文件打包参数不一致时慎重直接拼接:scale_factor / add_offset 是逐文件写入变量属性的,不同批次/时次的文件完全可能取值不同(例如前期文件用 0.1/0,后期文件用 0.02/-50)。若用 NCO ncrcat 等方式先把多个 short 文件合并成一个,再统一解压,最终文件只会保留一份属性,先拼进来的打包参数会"覆盖"其他文件的——除第一批数据外,其余时次的数值都会被错误还原。
from netCDF4 import Dataset
for f in file_list:
with Dataset(f, "r") as ds:
v = ds["u100m"]
print(f, v.scale_factor, v.add_offset, getattr(v, "_FillValue", None))scale_factor / add_offset。不要重复解压:若文件属性中已没有 scale_factor / add_offset,不要再执行解压,否则等于对已还原的数据再次乘加,结果会出错。
缺测值处理:2.1 节自动解码会把 _FillValue 转为 NaN;若下游工具不接受 NaN,可用 ds.fillna(-9999) 等方式替换后再写出。
只解压部分变量:xarray 可用 ds[[var_name]].to_netcdf(...) 仅转换指定变量,其余变量保持 short 不变。
大文件处理:数据量大时建议先裁剪区域/时次再解压,或使用 xr.open_dataset(..., chunks={...}) 配合 dask 分块处理,避免内存不足。
属性保留:解压后 units、long_name 等描述性属性会原样保留,无需额外处理。