在3d蒙版中对4d阵列进行修饰和标准化?

时间:2018-07-26 04:27:22

标签: python arrays numpy matrix masked-array

import numpy as np

ts = np.random.rand(40,45,40,1000)
mask = np.random.randint(2, size=(40,45,40),dtype=bool)

#creating a masked array
ts_m = np.ma.array(ts, mask=ts*~mask[:,:,:,np.newaxis])
#demeaning
ts_md = ts_m - ts_m.mean(axis=3)[:,:,:,np.newaxis]
#standardisation
ts_mds = ts_md / ts_md.std(ddof=1,axis=3)[:,:,:,np.newaxis]

我想对ts(沿3轴)进行测量,并除以其标准偏差(沿3轴),全部都在面罩内。

我正确地做到了吗?

有更快的方法吗?

2 个答案:

答案 0 :(得分:1)

您有几个可用的选项。

第一种是在使用过程中使用masked arrays,但要提供适当的掩码并使用掩码的功能。现在,您的代码正在计算所有均值和标准差,并在结果上加上掩码。要跳过被屏蔽的元素,请使用np.ma.meannp.ma.std,从而避免做很多额外的工作。

正如您正确理解的那样,掩码的大小必须与数据的大小匹配。虽然乘以数据可以得到正确的大小,但通常情况下代价昂贵,而且会给出错误的结果,因为只要 数据或掩码为零,掩码就会为零。更好的方法是创建沿最后(新)尺寸重复的蒙版视图。如果您获得的拖尾尺寸首先要匹配,则可以使用np.broadcast_to

ts = np.random.rand(40, 45, 40, 1000)
mask = np.random.randint(2, size=(40, 45, 40), dtype=np.bool)

#creating a masked array
ts_m = np.ma.array(ts, mask=np.broadcast_to(mask[..., None], ts.shape)
#demeaning
ts_md = ts_m - np.ma.mean(ts_m, axis=3)[..., None]
#standardisation
ts_mds = ts_md / np.ma.std(ts_m, ddof=1,axis=3)[..., None]

该蒙版是只读的,并且由于它的尺寸可能为零步幅,因此有时可以做意外的事情。这里的广播版本大致相当于

np.lib.stride_tricks.as_strided(mask, ts.shape, (*mask.strides, 0), writeable=False)

两个版本都创建原始数据的视图,因此非常快。他们只是分配一个指向现有数据的新数组对象,该数据不会被复制。请记住,np.lib.stride_tricks.as_strided是一把大锤,应格外小心。如果您允许的话,它将随时崩溃。

注意:被掩码数组中的掩码被解释为True被掩码,而布尔索引数组被解释为False被掩码。根据获取方式及其在实际代码中的含义,您可能需要反转掩码

mask=np.broadcast_to(~mask[..., None], ...)

另一个选择是自己实施掩蔽。有两种方法可以做到这一点。如果您先进行此操作,则掩码将应用于数据的主要尺寸:

ts = np.random.rand(40, 45, 40, 1000)
mask = np.random.randint(2, size=(40, 45, 40), dtype=np.bool)

#creating a masked array
mask = ~mask  # optional, see note above
ts_m = ts[mask]
#demeaning
ts_md = ts_m - ts_m.mean(axis=-1)
#standardisation
ts_mds = ts_md / ts_md.std(ddof=1,axis=-1)
# reshaping
result = np.empty_like(ts)  # alternatively, np.zeros_like
result[mask] = ts_mds

此选项可能比掩码数组便宜,因为初始掩码步骤会创建一个40*45*40-mask_size x 1000数组,并且仅在完成后才将其替换为结果的掩码区域,而不是对全尺寸数据进行操作并保留形状。

仅当您仅屏蔽了少量元素时,第三个选项才真正有用。本质上,这就是原始代码的工作:执行所有换向并将掩码应用于结果。

更多提示

Ellipsis是一个特殊的对象,表示“所有剩余尺寸”。切片符号中通常缩写为...np.newaxisNone的别名。结合这些信息,您就可以将[: :, :, np.newaxis]更加简洁地写成[..., None]。后者更为通用,因为它适用于任意数量的尺寸。

Numpy允许使用负轴索引。更好的说法是“ {最后一个轴”}通常是axis=-1

答案 1 :(得分:0)

import numpy as np

ts = np.random.rand(40,45,40,1000)
mask = np.random.randint(2, size=(40,45,40)).astype(bool)

#creating a masked array
ts_m = np.ma.array(ts, mask=np.broadcast_to(~mask.reshape(40,45,40,1),ts.shape))
#demeaning
ts_md = ts_m - ts_m.mean(axis=3)[:,:,:,np.newaxis]
#standardisation
ts_mds = ts_md / ts_md.std(ddof=1,axis=3)[:,:,:,np.newaxis]