- 积分
- 26378
- 贡献
-
- 精华
- 在线时间
- 小时
- 注册时间
- 2017-9-4
- 最后登录
- 1970-1-1
|
登录后查看更多精彩内容~
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
本帖最后由 灭火器 于 2025-1-15 22:44 编辑
已知一系列离散点,用经纬度和变量值描述,可以保存为如下的二维表格:
二维分箱(2d binning)指规划好经纬度的正交网格,根据每个格子里落入的离散点计算格子的统计值,最后得到各个变量的格点数据,然后可以保存为 netCDF 文件。将非规则网格的 L2 卫星数据处理成格点数据时,经常会利用二维分箱的方法计算格子里的平均值。大概类似 NCL 官网这张图的效果:
NCL 里直接有现成的 bin_avg 函数,但 python 里要实现就更麻烦一些。
这里举出我常用的两种方法,就当抛砖引玉,想看看大家都是怎么实现的。简单粗暴的二维循环就忽略了。
方法一:scipy.stats.binned_statistic_2d,注意为了得到形如 (nlat, nlon) 的结果,需要颠倒 x 和 y 参数。
- from scipy.stats import binned_statistic_2d
- def binning2d(
- x: ArrayLike,
- y: ArrayLike,
- values: ArrayLike | list[ArrayLike],
- xbins: int | ArrayLike,
- ybins: int | ArrayLike,
- ) -> tuple[NDArray, NDArray, NDArray]:
- def nanmean(arr: NDArray) -> float:
- arr = arr[~np.isnan(arr)]
- if arr.size == 0:
- return np.nan
- return arr.mean()
- binned, ybins, xbins, _ = binned_statistic_2d(
- y, x, values, bins=[ybins, xbins], statistic=nanmean
- )
- xlabels = get_bin_centers(xbins)
- ylabels = get_bin_centers(ybins)
- return xlabels, ylabels, binned
- df = pd.read_csv("test.csv")
- lon_bins, lon_labels = make_evenly_bins(-180, 180, 0.1)
- lat_bins, lat_labels = make_evenly_bins(-90, 90, 0.1)
- varnames = df.columns[2:]
- _, _, binned = binning2d(
- x=df["lon"],
- y=df["lat"],
- values=[df[varname] for varname in varnames],
- xbins=lon_bins,
- ybins=lat_bins,
- )
- ds = xr.Dataset(
- {varname: (["lat", "lon"], data) for varname, data in zip(varnames, binned)},
- coords={"lon": lon_labels, "lat": lat_labels},
- )
复制代码
方法二:pd.cut,更加简洁,速度也比 scipy 快很多。
更新:根据墨家大宝的回复修改了 observed 参数,并使用 reindex。
- import numpy as np
- import pandas as pd
- import xarray as xr
- from numpy.typing import ArrayLike, NDArray
- def get_bin_centers(bins: ArrayLike) -> NDArray:
- bins = np.asarray(bins)
- assert len(bins) >= 2
- return (bins[1:] + bins[:-1]) / 2
- def linspace2(x0: float, x1: float, dx: float) -> NDArray:
- assert dx > 0
- nx = int(abs(x1 - x0) / dx) + 1
- x = np.linspace(x0, x1, nx)
- return x
- def make_evenly_bins(x0: float, x1: float, dx: float) -> tuple[NDArray, NDArray]:
- bins = linspace2(x0, x1, dx)
- labels = get_bin_centers(bins)
- return bins, labels
- lon_bins, lon_labels = make_evenly_bins(-180, 180, 0.1)
- lat_bins, lat_labels = make_evenly_bins(-90, 90, 0.1)
- df = pd.read_csv("test.csv")
- # 实际运算部分
- ds = (
- df.assign(
- lon=pd.cut(df["lon"], lon_bins, labels=lon_labels, include_lowest=True),
- lat=pd.cut(df["lat"], lat_bins, labels=lat_labels, include_lowest=True),
- )
- .groupby(["lat", "lon"], observed=True)
- .mean()
- .to_xarray()
- .reindex(lon=lon_labels, lat=lat_labels)
- )
- ds.to_netcdf("test.nc")
复制代码
另外还有两种我尝试失败的 xarray 方法,看有没有坛友知道原因:
- lon_cat = pd.cut(df['lon'], lon_bins, labels=lons, include_lowest=True)
- lat_cat = pd.cut(df['lat'], lat_bins, labels=lats, include_lowest=True)
- ds = df.to_xarray().assign(lon=('index', lon_cat), lat=('index', lat_cat)).groupby(['lat', 'lon']).mean()
- # v2024.07.0 版本以上
- from xarray.groupers import BinGrouper
- lon_grouper = BinGrouper(lon_bins, labels=lons, include_lowest=True)
- lat_grouper = BinGrouper(lat_bins, labels=lats, include_lowest=True)
- ds = df.to_xarray().groupby(lon=lon_grouper, lat=lat_grouper).mean()
复制代码
|
|