Python 正在寻找使用numpy根据发生情况对3d阵列进行降采样的最快方法

Python 正在寻找使用numpy根据发生情况对3d阵列进行降采样的最快方法,python,numpy,numpy-ndarray,downsampling,Python,Numpy,Numpy Ndarray,Downsampling,给定一个类型为“uint8”的大型3d numpy数组(不太大,无法放入内存),我想在每个维度中使用给定的scalefactor缩小此数组的规模。您可以假设数组的形状可按比例因子分割 数组的值在[0,1,…max]中,其中max始终小于6。我想缩小它,这样每个具有“scale_factor”形状的3d块都将返回该块中出现最多的数字。什么时候平等回报第一(我不在乎) 我已经尝试了以下有效的方法 import numpy as np array = np.random.randint(0, 4,

给定一个类型为“uint8”的大型3d numpy数组(不太大,无法放入内存),我想在每个维度中使用给定的scalefactor缩小此数组的规模。您可以假设数组的形状可按比例因子分割

数组的值在[0,1,…max]中,其中max始终小于6。我想缩小它,这样每个具有“scale_factor”形状的3d块都将返回该块中出现最多的数字。什么时候平等回报第一(我不在乎)

我已经尝试了以下有效的方法

import numpy as np

array = np.random.randint(0, 4, ((128, 128, 128)), dtype='uint8')
scale_factor = (4, 4, 4)
bincount = 3

# Reshape to free dimension of size scale_factor to apply scaledown method to
m, n, r = np.array(array.shape) // scale_factor
array = array.reshape((m, scale_factor[0], n, scale_factor[1], r, scale_factor[2]))


# Making histogram, first over last axis, then sum over other two
array = np.apply_along_axis(lambda x: np.bincount(x, minlength=bincount),
                            axis=5, arr=array)
array = np.apply_along_axis(lambda x: np.sum(x), axis=3, arr=array)
array = np.apply_along_axis(lambda x: np.sum(x), axis=1, arr=array).astype('uint8')

array = np.argmax(array , axis=3)
这是可行的,但是bincount的速度非常慢。也得到了np.histogram的工作,但也很慢。我确实认为我尝试的两种方法都不是完全为我的目的而设计的,它们提供了更多的特性,这会减慢方法的速度


我的问题是,有人知道更快的方法吗?如果有人能深度学习lib库的方法,我也会很高兴,但这不是正式的问题。

< P>。这里有一个类似的方法,但是速度更快。它仅根据您的用例将bincount函数替换为一个更简单的实现:
lambda x:max(set(x),key=lambda y:list(x).count(y))
其中首先对数组进行了重塑,以便该方法可以直接用于一维

在我的128x128x128笔记本电脑上,速度快了4倍:

import time
import numpy as np

array = np.random.randint(0, 4, ((128, 128, 128)), dtype='uint8')
scale_factor = (4, 4, 4)
bincount = 4

start_time = time.time()
N = 10
for i in range(N):

    # Reshape to free dimension of size scale_factor to apply scaledown method to
    m, n, r = np.array(array.shape) // scale_factor
    arr = array.reshape((m, scale_factor[0], n, scale_factor[1], r, scale_factor[2]))
    arr = np.swapaxes(arr, 1, 2).swapaxes(2, 4)
    arr = arr.reshape((m, n, r, np.prod(scale_factor)))

    # Obtain the element that occurred the most
    arr = np.apply_along_axis(lambda x: max(set(x), key=lambda y: list(x).count(y)),
                              axis=3, arr=arr)

print((time.time() - start_time) / N)

例如,与np.mean()之类的内置方法仍有很大差距。

@F.Wessels的思路是正确的,但答案还不太清楚。如果您亲自动手而不是使用“沿轴应用”,则速度可以提高100倍以上

首先,将3D阵列空间划分为块时,尺寸从128x128x128更改为32x4x32x2x4x32x4。试着直观地理解这一点:您实际上拥有大小为4x4x4的32x32x32块。不必将块保持为4x4x4大小,只需将其压缩为64大小,即可找到最常用的项。诀窍在于:如果块不是按32x32x232x64排列,而是按32768x64排列,这也无关紧要。基本上,我们又回到了二维空间,在二维空间中,一切都变得更容易

现在,使用32768x64大小的2D阵列,您可以通过列表理解和numpy操作自己计算垃圾箱;速度会快10倍

import time
import numpy as np

array = np.random.randint(0, 4, ((128, 128, 128)), dtype='uint8')
scale_factor = (4, 4, 4)
bincount = 4

def prev_func(array):
    # Reshape to free dimension of size scale_factor to apply scaledown method to
    m, n, r = np.array(array.shape) // scale_factor
    arr = array.reshape((m, scale_factor[0], n, scale_factor[1], r, scale_factor[2]))
    arr = np.swapaxes(arr, 1, 2).swapaxes(2, 4)
    arr = arr.reshape((m, n, r, np.prod(scale_factor)))
    # Obtain the element that occurred the most
    arr = np.apply_along_axis(lambda x: max(set(x), key=lambda y: list(x).count(y)),
                              axis=3, arr=arr)
    return arr

def new_func(array):
    # Reshape to free dimension of size scale_factor to apply scaledown method to
    m, n, r = np.array(array.shape) // scale_factor
    arr = array.reshape((m, scale_factor[0], n, scale_factor[1], r, scale_factor[2]))
    arr = np.swapaxes(arr, 1, 2).swapaxes(2, 4)
    arr = arr.reshape((m, n, r, np.prod(scale_factor)))
    # Collapse dimensions
    arr = arr.reshape(-1,np.prod(scale_factor))
    # Get blockwise frequencies -> Get most frequent items
    arr = np.array([(arr==b).sum(axis=1) for b in range(bincount)]).argmax(axis=0)
    arr = arr.reshape((m,n,r))
    return arr

N = 10

start1 = time.time()
for i in range(N):
    out1 = prev_func(array)
end1 = time.time()
print('Prev:',(end1-start1)/N)

start2 = time.time()
for i in range(N):
    out2 = new_func(array)
end2 = time.time()
print('New:',(end2-start2)/N)

print('Difference:',(out1-out2).sum())
输出:


正如你所看到的,结果没有什么不同,尽管我已经改变了尺寸。Numpy的重塑函数在我转到2D时保持了值的顺序,因为最后一个维度64被保留。当我把形状改回m,n,r时,这个顺序会重新建立。您给出的原始方法在我的机器上运行大约需要5秒钟,因此根据经验,速度提高了500倍。

apply_沿轴应用是我理解的一个奇特的python for循环。我要说的是,不是bincount使您的下降速度变慢=np.zeros_like(arr);np.add.at(counts.reformate(-1),(arr+np.arange(len(arr))[:,None]*bincount.flatten(),1);return counts.argmax(axis=1)上面只生成一个大小为128的alloc,bincount;从而避免了O(bincount**2)的复杂性。也许对小宾克来说没什么大不了的;但它可能会很快累积起来。
Prev: 1.4244404077529906
New: 0.01667332649230957
Difference: 0