【问题标题】:mean till 25 percentile for rows of an array containing nan包含 nan 的数组行的平均值直到 25%
【发布时间】:2017-04-04 11:15:53
【问题描述】:

我有一个二维数组 x,每一行都有不同数量的 nan 值:

array([[   nan, -0.355, -0.036, ...,    nan,    nan],
       [   nan, -0.341, -0.047, ...,    nan,  0.654],
       [  .016, -1.147, -0.667, ...,    nan,    nan],
       ..., 
       [   nan,  0.294, -0.235, ...,    0.65,   nan]])

给定这个数组,对于每一行,我想计算前 25 个百分位数内所有值的平均值。我正在执行以下操作:

limit = np.nanpercentile(x, 25, axis=1) # output 1D array
ans = np.nanmean(x * (x < limit[:,None]), axis=1)

但这给出了错误的结果——特别是计数(np.nansum/np.nanmean)无论我选择什么百分位数都保持不变,因为比较在不正确的地方产生零,并被视为有效值意思是。我不能简单地使用x[x&gt;limit[:,None]],因为它给出了一个一维数组,我需要一个二维结果。

我通过以下方式解决了它:

f = x.copy()
f[f > limit[:,None]] = np.nan
ans = np.nanmean(f, axis=1) 

有更好的方法吗?

【问题讨论】:

  • 你的意思是limit,你在哪里写了low?如果是这样,那么我认为这正是我会采用的程序。您正在寻找哪种更好的方法?
  • 谢谢,是的,这就是极限。最好想要一种没有太多中间步骤的方法,因为有了这个方法,我每次想要另一个百分位数时都需要创建一个副本。

标签: numpy multidimensional-array vectorization nan array-broadcasting


【解决方案1】:

方法#1:您可以创建一个无效掩码,即来自原始数组的NaNs 和来自f &gt; limit[:,None] 的掩码。然后,使用此掩码执行np.nanmean 等效方法,仅考虑具有masking 的有效方法。使用masks/boolean arrays 的好处在于内存方面,因为它占用的内存比浮动 pt 数组少 8 倍。因此,我们会有这样的实现 -

# Create mask of non-NaNs and thresholded ones
mask = ~np.isnan(x) & (x <= limit[:,None])

# Get the row, col indices. Use the row indices for bin-based summing and
# finally averaging by using those indices to get the group lengths.
r,c = np.where(mask)
out = np.bincount(r,x[mask])/np.bincount(r)

方法#2:我们也可以使用np.add.reduceat,这在此处很有帮助,因为垃圾箱已经根据掩码进行了排序。所以,效率更高一点就是这样 -

# Get the valid mask as before
mask = ~np.isnan(x) & (x <= limit[:,None])

# Get valid row count. Use np.add.reduceat to perform grouped summations
# at intervals separated by row indices.
rowc = mask.sum(1)
out = np.add.reduceat(x[mask],np.append(0,rowc[:-1].cumsum()))/rowc

基准测试

函数定义-

def original_app(x, limit):
    f = x.copy()
    f[f > limit[:,None]] = np.nan
    ans = np.nanmean(f, axis=1) 
    return ans

def proposed1_app(x, limit):
    mask = ~np.isnan(x) & (x <= limit[:,None])
    r,c = np.where(mask)
    out = np.bincount(r,x[mask])/np.bincount(r)
    return out

def proposed2_app(x, limit):
    mask = ~np.isnan(x) & (x <= limit[:,None])
    rowc = mask.sum(1)
    out = np.add.reduceat(x[mask],np.append(0,rowc[:-1].cumsum()))/rowc
    return out

时间和验证 -

In [402]: # Setup inputs
     ...: x = np.random.randn(400,500)
     ...: x.ravel()[np.random.randint(0,x.size,x.size//4)] = np.nan # Half as NaNs
     ...: limit = np.nanpercentile(x, 25, axis=1)
     ...: 

In [403]: np.allclose(original_app(x, limit),proposed1_app(x, limit))
Out[403]: True

In [404]: np.allclose(original_app(x, limit),proposed2_app(x, limit))
Out[404]: True

In [405]: %timeit original_app(x, limit)
100 loops, best of 3: 5 ms per loop

In [406]: %timeit proposed1_app(x, limit)
100 loops, best of 3: 4.02 ms per loop

In [407]: %timeit proposed2_app(x, limit)
100 loops, best of 3: 2.18 ms per loop

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-19
    • 1970-01-01
    • 2013-09-12
    • 2019-03-21
    • 2021-08-28
    • 2016-04-06
    相关资源
    最近更新 更多