我有一个hape (365, x, y
的3维数组,其中36对应于每日数据。在某些情况下,沿时间轴axis=0
的所有元素都是np.nan
。
axis=0
中每个点的时间序列如下所示:
我需要找到出现最大值(峰值数据)的索引,然后找到峰两边的两个最小值。
import numpy as np
a = np.random.random(365, 3, 3) * 10
a[:, 0, 0] = np.nan
peak_mask = np.ma.masked_array(a, np.isnan(a))
peak_indexes = np.nanargmax(peak_mask, axis=0)
我可以使用以下方法找到峰值之前的最小值:
early_minimum_indexes = np.full_like(peak_indexes, fill_value=0)
for i in range(peak_indexes.shape[0]):
for j in range(peak_indexes.shape[1]):
if peak_indexes[i, j] == 0:
early_minimum_indexes[i, j] = 0
else:
early_mask = np.ma.masked_array(a, np.isnan(a))
early_loc = np.nanargmin(early_mask[:peak_indexes[i, j], i, j], axis=0)
early_minimum_indexes[i, j] = early_loc
结果峰和谷的绘制如下:
对于大型数组(1m +个元素),这种方法在时间上非常不合理。有没有更好的方法可以使用numpy做到这一点?
答案 0 :(得分:0)
这里是一种方法
代码:
INVALINT = -9999
t,x,y = a.shape
t,x,y = np.ogrid[:t,:x,:y]
inval = np.isnan(a)
b = np.where(inval,np.nanmin(a)-1,a)
pk = b.argmax(axis=0)
pkval = b[pk,x,y]
b -= pkval
b[inval] = 0
b[t>pk[None]] *= -1
ltr = b.argmin(axis=0)
rtr = b.argmax(axis=0)
del b
inval = inval.all(axis=0)
pk[inval] = INVALINT
ltr[inval] = INVALINT
rtr[inval] = INVALINT
# result is now in ltr ("left trough"), pk ("peak") and rtr
答案 1 :(得分:0)
虽然在这种情况下使用遮罩数组可能不是最有效的解决方案,但它可以让您在特定的轴上执行遮罩操作,而几乎保留形状,这非常方便。请记住,在许多情况下,被屏蔽的功能仍将最终复制被屏蔽的数据。
在当前代码中,您基本上有正确的主意,但错过了一些技巧,例如能够否定和组合蒙版。另外,将掩码作为布尔值预先分配的事实更加有效,并且像np.full(..., 0) -> np.zeros(..., dtype=bool)
这样的小挑剔。
让我们从此倒退。假设您有一个行为良好的一维数组,其峰值为a1
。您可以使用遮罩轻松找到最大值和最小值(或索引),如下所示:
peak_index = np.nanargmax(a1)
mask = np.zeros(a1.size, dtype=np.bool)
mask[peak:] = True
trough_plus = np.nanargmin(np.ma.array(a1, mask=~mask))
trough_minus = np.nanargmin(np.ma.array(a1, mask=mask))
这尊重这样一个事实,即相对于普通的numpy布尔索引,被掩码的数组会颠倒掩码的含义。最大值也可以出现在trough_plus
的计算中,因为可以保证它不会是最小值(除非您遇到全纳情况)。
现在,如果a1
已经是一个蒙版数组(但仍然是1D),则可以执行相同的操作,但是可以暂时合并蒙版。例如:
a1 = np.ma.array(a1, mask=np.isnan(a1))
peak_index = a1.argmax()
mask = np.zeros(a1.size, dtype=np.bool)
mask[peak:] = True
trough_plus = np.ma.masked_array(a1, mask=a.mask | ~mask).argmin()
trough_minus (np.ma.masked_array(a1, mask=a.mask | mask).argmin()
同样,由于掩码数组具有反向掩码,因此像使用普通的numpy布尔掩码一样,使用|
而不是&
组合掩码非常重要。在这种情况下,无需调用argmax
和argmin
的nan版本,因为所有nan都已被屏蔽。
希望,鉴于numpy函数中普遍使用axis
关键字,从这里可以清楚地实现对多维的概括:
a = np.ma.array(a, mask=np.isnan(a))
peak_indices = a.argmax(axis=0).reshape(1, *a.shape[1:])
mask = np.arange(a.shape[0]).reshape(-1, *(1,) * (a.ndim - 1)) >= peak_indices
trough_plus = np.ma.masked_array(a, mask=~mask | a.mask).argmin(axis=0)
trough_minus = np.ma.masked_array(a, mask=mask | a.mask).argmin(axis=0)
N维掩蔽技术来自Fill mask efficiently based on start indices,正是出于这一目的而被要求的。