改善最小/最大下采样

Aer*_*ngy 7 python numpy python-3.x numba

我有一些大型数组(约1亿个点),我需要以交互方式绘制.我当前正在使用Matplotlib.按原样绘制数组变得非常慢并且是浪费,因为无论如何你都无法想象那么多点.

所以我做了一个min/max抽取函数,我将其绑定到轴的'xlim_changed'回调.我采用最小/最大方法,因为数据包含快速尖峰,我不想通过单步执行数据.有更多的包装器可以裁剪到x限制,并在某些条件下跳过处理,但相关部分如下:

def min_max_downsample(x,y,num_bins):
    """ Break the data into num_bins and returns min/max for each bin"""
    pts_per_bin = x.size // num_bins    

    #Create temp to hold the reshaped & slightly cropped y
    y_temp = y[:num_bins*pts_per_bin].reshape((num_bins, pts_per_bin))
    y_out      = np.empty((num_bins,2))
    #Take the min/max by rows.
    y_out[:,0] = y_temp.max(axis=1)
    y_out[:,1] = y_temp.min(axis=1)
    y_out = y_out.ravel()

    #This duplicates the x-value for each min/max y-pair
    x_out = np.empty((num_bins,2))
    x_out[:] = x[:num_bins*pts_per_bin:pts_per_bin,np.newaxis]
    x_out = x_out.ravel()
    return x_out, y_out
Run Code Online (Sandbox Code Playgroud)

这种方法效果非常好并且足够快(在1e8点和2k箱上约80ms).由于它会定期重新计算和更新线的x和y数据,所以滞后很少.

但是,我唯一的抱怨是x数据.此代码复制每个bin的左边缘的x值,并且不返回y min/max对的真实x位置.我通常将箱数设置为轴像素宽度的两倍.所以你不能真正看到差异,因为垃圾箱太小了......但我知道它在那里......它让我烦恼.

因此尝试编号2确实返回每个最小/最大对的实际x值.然而它大约慢了5倍.

def min_max_downsample_v2(x,y,num_bins):
    pts_per_bin = x.size // num_bins
    #Create temp to hold the reshaped & slightly cropped y
    y_temp = y[:num_bins*pts_per_bin].reshape((num_bins, pts_per_bin))
    #use argmax/min to get column locations
    cc_max = y_temp.argmax(axis=1)
    cc_min = y_temp.argmin(axis=1)    
    rr = np.arange(0,num_bins)
    #compute the flat index to where these are
    flat_max = cc_max + rr*pts_per_bin
    flat_min = cc_min + rr*pts_per_bin
    #Create a boolean mask of these locations
    mm_mask  = np.full((x.size,), False)
    mm_mask[flat_max] = True
    mm_mask[flat_min] = True  
    x_out = x[mm_mask]    
    y_out = y[mm_mask]  
    return x_out, y_out
Run Code Online (Sandbox Code Playgroud)

这在我的机器上花了大约400多毫秒,这变得非常明显.所以我的问题基本上是有一种方法可以更快地提供相同的结果吗?瓶颈主要是在numpy.argminnumpy.argmax它比慢好位功能numpy.minnumpy.max.

答案可能是只使用版本#1,因为它在视觉上并不重要.或者尝试加速像cython这样的东西(我从未使用过).

仅供参考在Windows上使用Python 3.6.4 ...示例用法将是这样的:

x_big = np.linspace(0,10,100000000)
y_big = np.cos(x_big )
x_small, y_small = min_max_downsample(x_big ,y_big ,2000) #Fast but not exactly correct.
x_small, y_small = min_max_downsample_v2(x_big ,y_big ,2000) #correct but not exactly fast.
Run Code Online (Sandbox Code Playgroud)

a_g*_*est 3

我设法通过直接使用输出arg(min|max)来索引数据数组来提高性能。这是以额外调用为代价的,np.sort但要排序的轴只有两个元素(最小/最大索引),并且整个数组相当小(箱数):

\n\n
def min_max_downsample_v3(x, y, num_bins):\n    pts_per_bin = x.size // num_bins\n\n    x_view = x[:pts_per_bin*num_bins].reshape(num_bins, pts_per_bin)\n    y_view = y[:pts_per_bin*num_bins].reshape(num_bins, pts_per_bin)\n    i_min = np.argmin(y_view, axis=1)\n    i_max = np.argmax(y_view, axis=1)\n\n    r_index = np.repeat(np.arange(num_bins), 2)\n    c_index = np.sort(np.stack((i_min, i_max), axis=1)).ravel()\n\n    return x_view[r_index, c_index], y_view[r_index, c_index]\n
Run Code Online (Sandbox Code Playgroud)\n\n

我检查了你的例子的时间,我得到了:

\n\n
    \n
  • min_max_downsample_v1: 110 毫秒 \xc2\xb1 5 毫秒
  • \n
  • min_max_downsample_v2: 240 毫秒 \xc2\xb1 8.01 毫秒
  • \n
  • min_max_downsample_v3: 164 毫秒 \xc2\xb1 1.23 毫秒
  • \n
\n\n

我还检查了在致电后直接返回arg(min|max),结果同样为 164 毫秒,即此后不再有真正的开销。

\n