【发布时间】:2021-08-23 17:25:14
【问题描述】:
物理背景
我正在开发一个函数,该函数可以计算最多四维温度场(时间、经度、纬度、压力作为高度测量)中每个垂直剖面的一些指标。我有一个工作函数,可以在单个位置获取压力和温度并返回指标(对流层顶信息)。我想用一个函数包装它,将它应用于传递的数据中的每个垂直配置文件。
问题的技术描述
我希望我的函数将另一个函数应用于与我的 N 维数组中的最后一个维度相对应的每个一维数组,其中 N
我为什么要提出一个新问题
我知道有几个问题(例如,iterating over some dimensions of a ndarray、Iterating over the last dimensions of a numpy array、Iterating over 3D numpy using one dimension as iterator remaining dimensions in the loop、Iterating over a numpy matrix with unknown dimension)询问如何迭代特定维度或如何迭代数组尺寸未知。据我所知,这两个问题的结合是新的。以 numpy.nditer 为例,我还没有发现如何只排除最后一个维度,而不管剩下的维度数量如何。
编辑
我试着做一个最小的、可重现的例子:
import numpy as np
def outer_function(array, *args):
"""
Array can be 1D, 2D, 3D, or 4D. Regardless the inner_function
should be applied to all 1D arrays spanned by the last axis
"""
# Unpythonic if-else solution
if array.ndim == 1:
return inner_function(array)
elif array.ndim == 2:
return [inner_function(array[i,:]) for i in range(array.shape[0])]
elif array.ndim == 3:
return [[inner_function(array[i,j,:]) for i in range(array.shape[0])] for j in range(array.shape[1])]
elif array.ndim == 4:
return [[[inner_function(array[i,j,k,:]) for i in range(array.shape[0])] for j in range(array.shape[1])] for k in range(array.shape[2])]
else:
return -1
def inner_function(array_1d):
return np.interp(2, np.arange(array_1d.shape[0]), array_1d), np.sum(array_1d)
请假设实际的 inner_function 不能修改为应用于多维,而只能应用于一维数组。
编辑结束
如果它有助于我拥有/想要拥有的代码结构:
def tropopause_ds(ds):
"""
wraps around tropopause profile calculation. The vertical coordinate has to be the last one.
"""
t = ds.t.values # numpy ndarray
p_profile = ds.plev.values # 1d numpy ndarray
len_t = ds.time.size
len_lon = ds.lon.size
len_lat = ds.lat.size
nlevs = ds.plev.size
ttp = np.empty([len_t, len_lon, len_lat])
ptp = np.empty([len_t, len_lon, len_lat])
ztp = np.empty([len_t, len_lon, len_lat])
dztp = np.empty([len_t, len_lon, len_lat, nlevs])
# Approach 1: use numpy.ndindex - doesn't work in a list comprehension, slow
for idx in np.ndindex(*t.shape[:-1]):
ttp[idx], ptp[idx], ztp[idx], dztp[idx] = tropopause_profile(t[idx], p_profile)
# Approach 2: use nested list comprehensions - doesn't work for different number of dimensions
ttp, ptp, ztp, dztp = [[[tropopause_profile(t[i,j,k,:], p_profile) for k in range(len_lat)]
for j in range(len_lon)] for i in range(len_t)]
return ttp, ptp, ztp, dztp
内部函数结构如下:
def tropopause_profile(t_profile, p_profile):
if tropopause found:
return ttp, ptp, ztp, dztp
return np.nan, np.nan, np.nan, np.nan
我已经尝试了几个选项。定时案例中的测试数据形状为(2, 360, 180, 105):
- xarray's apply_ufunc 似乎将整个数组传递给函数。然而,我的内部函数基于获取一维数组,并且很难重新编程以处理多维数据
- 嵌套的列表推导工作并且似乎相当快,但如果一个维度(例如时间)只有一个值(timed:8.53 s ±每个循环 11.9 毫秒(平均值 ± 7 次运行的标准偏差,每次 1 个循环))
- 使用 numpy's nditer 在标准 for 循环中工作,该循环使用列表理解加速。然而,使用这种方法,该函数不会返回 4 个 ndarray,而是一个包含每个索引的四个返回值作为列表元素的列表。 (定时列表理解:每循环 1 分钟 4 秒 ± 740 毫秒(平均值 ± 标准偏差,7 次运行,每次 1 次循环))
解决这个问题的一个丑陋方法是检查我的数据有多少维,然后对正确数量的列表推导进行 if else 选择,但我希望 python 有一个更流畅的方法来解决这个问题。如果有帮助,可以轻松更改尺寸的顺序。我在 2 核、10 GB 内存的 jupyterhub 服务器上运行代码。
【问题讨论】:
-
另外,我认为先检查维数并没有什么不好的地方,除非有一些性能损失。
-
你查看
np.apply_along_axis了吗? -
@hilberts_drinking_problem 不,我没有,但它看起来很有希望!已经谢谢了!
-
@hilberts_drinking_problem 我刚刚实现了它,它以一种意想不到的方式保存了结果。但是,有可能解决这个问题。然而,这种方法甚至比 np.ndindex 更慢(对于相同的数据,每个循环 1 分钟 7 秒 ± 1.29 秒(平均值 ± 标准偏差,7 次运行,每个循环 1 次))
-
即使一维大小为 1,显式迭代和/或列表推导也应该起作用(但如果它是“标量”而不是可迭代的,则不起作用)。但是如果除了最后一个维度之外的所有维度都被重新整形为一个,例如,嵌套迭代可以被简化。
reshape(-1,n)。apply_along_axis也简化了迭代,但(在我的测试中)但有时间成本。我也没有看到使用nditer的任何时间优势。nditer也很难使用;我不推荐它。
标签: python arrays numpy iterator iteration