需要使用Numpy评估较大的3D阵列中的较小的3D阵列

时间:2018-11-24 15:28:20

标签: python arrays numpy

我必须采用一个随机的50x50x50整数数组,并确定其中的哪个连续3x3x3多维数据集具有最大的和。

除非将较小的多维数据集均匀地分成较大的多维数据集,否则Numpy中的许多拆分功能似乎无法正常工作。尝试通过思考过程,我制作了一个48x48x48的多维数据集,其大小从1到110,592。然后,我考虑使用以下代码将其重塑为4D数组,并评估哪个数组的总和最大?当我输入此代码时,尽管它以不理想的顺序拆分数组。我希望第一个数组是3x3x3多维数据集,该多维数据集应该位于48x48x48多维数据集的角落。我可以添加某种语法来实现这一点吗?

import numpy as np

arr1 = np.arange(0,110592)
arr2=np.reshape(arr1, (48,48,48))
arr3 = np.reshape(arr2, (4096, 3,3,3))
arr3

输出:

array([[[[     0,      1,      2],
         [     3,      4,      5],
         [     6,      7,      8]],

        [[     9,     10,     11],
         [    12,     13,     14],
         [    15,     16,     17]],

        [[    18,     19,     20],
         [    21,     22,     23],
         [    24,     25,     26]]],

所需的输出:

array([[[[     0,      1,      2],
         [    48,      49,      50],
         [     96,      97,      98]],

等等等

4 个答案:

答案 0 :(得分:1)

解决方案

There's a live version of this solution online you can try for yourself

对于您最初的问题有一个简单的(某种)解决方案,即在50x50x50的多维数据集中找到最大的3x3x3子多维数据集,该子多维数据集基于更改输入数组的步幅。此解决方案是完全矢量化的(意味着没有循环),因此应该从Numpy中获得最佳性能:

import numpy as np

def cubecube(arr, cshape):
    strides = (*arr.strides, *arr.strides)
    shape = (*np.array(arr.shape) - cshape + 1, *cshape)
    return np.lib.stride_tricks.as_strided(arr, shape=shape, strides=strides)

def maxcube(arr, cshape):
    cc = cubecube(arr, cshape)
    ccsums = cc.sum(axis=tuple(range(-arr.ndim, 0)))
    ix = np.unravel_index(np.argmax(ccsums), ccsums.shape)[:arr.ndim]
    return ix, cc[ix]

maxcube函数采用数组和子多维数据集的形状,并返回(first-index-of-largest-cube, largest-cube)的元组。这是一个如何使用maxcube的示例:

shape = (50, 50, 50)
cshape = (3, 3, 3)

# set up a 50x50x50 array
arr = np.arange(np.prod(shape)).reshape(*shape)

# set one of the subcubes as the largest
arr[37, 26, 11] = 999999

ix, cube = maxcube(arr, cshape)
print('first index of largest cube: {}'.format(ix))
print('largest cube:\n{}'.format(cube))

输出:

first index of largest cube: (37, 26, 11)
largest cube:
[[[999999  93812  93813]
  [ 93861  93862  93863]
  [ 93911  93912  93913]]

 [[ 96311  96312  96313]
  [ 96361  96362  96363]
  [ 96411  96412  96413]]

 [[ 98811  98812  98813]
  [ 98861  98862  98863]
  [ 98911  98912  98913]]]

深入解释

一个立方体的立方体

您拥有的是48x48x48的多维数据集,但是您想要的是一个较小的多维数据集的多维数据集。可以通过改变步幅将其转换为另一个。对于dtype int64的48x48x48数组,步幅最初将设置为(48*48*8, 48*8, 8)。每个不重叠的3x3x3子多维数据集的第一个值都可以以(3*48*48*8, 3*48*8, 3*8)的步幅进行迭代。结合这些步幅以获得立方体的步幅:

# Set up a 48x48x48 array, like in OP's example
arr = np.arange(48**3).reshape(48,48,48)

shape = (16,16,16,3,3,3)
strides = (3*48*48*8, 3*48*8, 3*8, 48*48*8, 48*8, 8)

# restride into a 16x16x16 array of 3x3x3 cubes
arr2 = np.lib.stride_tricks.as_strided(arr, shape=shape, strides=strides)

arr2arr形状的(16,16,16,3,3,3)的视图(表示它们共享数据,因此无需复制)。通过将索引传递到ijk,可以访问arr中的第arr2个3x3多维数据集:

i,j,k = 0,0,0
print(arr2[i,j,k])

输出:

[[[   0    1    2]
  [  48   49   50]
  [  96   97   98]]

 [[2304 2305 2306]
  [2352 2353 2354]
  [2400 2401 2402]]

 [[4608 4609 4610]
  [4656 4657 4658]
  [4704 4705 4706]]]

仅通过内轴求和就可以得到所有子立方体的总和:

sumOfSubcubes = arr2.sum(3,4,5)

这将产生一个16x16x16数组,其中每个值都是原始数组中不重叠的3x3x3子多维数据集的总和。这解决了OP询问的有关48x48x48阵列的特定问题。像上面的cubecube函数一样,重排也可以用来查找所有重叠的3x3x3立方体。

答案 1 :(得分:0)

这是一个没有许多numpy函数的解决方案,仅numpy.sum。首先定义一个平方矩阵,然后定义要在其中求和的立方体cs的大小。 只需更改cs即可调整多维数据集大小并找到其他解决方案。按照@Divakar的建议,我使用了4x4x4数组,我还存储了多维数据集所在的位置(只是多维数据集原点的顶点)

import numpy as np
np.random.seed(0)
a = np.random.randint(0,9,(4,4,4))
print(a)
cs = 2 # Cube size
my_sum = 0
idx = None
for i in range(a.shape[0]-cs+2):
  for j in range(a.shape[1]-cs+2):
    for k in range(a.shape[2]-cs+2):
      cube_sum = np.sum(a[i:i+cs, j:j+cs, k:k+cs])
      print(cube_sum)
      if cube_sum > my_sum:
        my_sum = cube_sum
        idx = (i,j,k)
print(my_sum, idx) # 42 (0, 0, 0)

这个3D数组a

[[[5 0 3 3]
  [7 3 5 2]
  [4 7 6 8]
  [8 1 6 7]]

 [[7 8 1 5]
  [8 4 3 0]
  [3 5 0 2]
  [3 8 1 3]]

 [[3 3 7 0]
  [1 0 4 7]
  [3 2 7 2]
  [0 0 4 5]]

 [[5 6 8 4]
  [1 4 8 1]
  [1 7 3 6]
  [7 2 0 3]]]

您将获得my_sum = 42的{​​{1}}和idx = (0, 0, 0)cs = 2的{​​{1}}和my_sum = 112

答案 2 :(得分:0)

您使用48x48x48多维数据集进行思考的过程朝着正确的方向发展,以至于50x50x50数组中存在48³个连续的3x3x3多维数据集,尽管我不明白您为什么要重塑它。

您可以做的是通过遍历相邻切片的所有27个置换并在其上找到最大值,将每个3x3x3多维数据集的全部27个值添加到48x48x48维数组中。找到的条目将为您提供最接近原始数组原点的多维数据集角的索引元组coordinate_index

import numpy as np
np.random.seed(0)
array_shape = np.array((50,50,50), dtype=int)
cube_dim = np.array((3,3,3), dtype=int)
original_array = np.random.randint(array_shape)
reduced_shape = array_shape - cube_dim + 1

sum_array = np.zeros(reduced shape, dtype=int)
for i in range(cube_dim[0]):
  for j in range(cube_dim[1]):
    for k in range(cube_dim[2]):
      sum_array += original_array[
          i:-cube_dim[0]+1+i, j:-cube_dim[1]+1+j, k:-cube_dim[2]+1+k
      ]
flat_index = np.argmax(sum_array)
coordinate_index = np.unravel_index(flat_index, reduced_shape)

此方法应该比遍历48³索引组合中的每个组合来查找所需的多维数据集更快,因为它使用了原位求和,但又需要更多的内存。我不确定,但是用切片定义(48³,27)数组并在第二个轴上使用np.sum可能会更快。

您可以轻松地更改以上代码,以查找具有任意边长的长方体。

答案 3 :(得分:0)

这是一个基于cumsum的快速解决方案:

import numpy as np

nd = 3
cs = 3
N = 50

# create indices [cs-1:, ...], [:, cs-1:, ...], ...
fromcsm = *zip(*np.where(np.identity(nd, bool), np.s_[cs-1:], np.s_[:])),
# create indices [cs:, ...], [:, cs:, ...], ...
fromcs = *zip(*np.where(np.identity(nd, bool), np.s_[cs:], np.s_[:])),
# create indices [:cs, ...], [:, :cs, ...], ...
tocs = *zip(*np.where(np.identity(nd, bool), np.s_[:cs], np.s_[:])),
# create indices [:-cs, ...], [:, :-cs, ...], ...
tomcs = *zip(*np.where(np.identity(nd, bool), np.s_[:-cs], np.s_[:])),
# create indices [cs-1, ...], [:, cs-1, ...], ...
atcsm = *zip(*np.where(np.identity(nd, bool), cs-1, np.s_[:])),

def windowed_sum(a):
    out = a.copy()
    for i, (fcsm, fcs, tcs, tmcs, acsm) \
            in enumerate(zip(fromcsm, fromcs, tocs, tomcs, atcsm)):
        out[fcs] -= out[tmcs]
        out[acsm] = out[tcs].sum(axis=i)
        out = out[fcsm].cumsum(axis=i)
    return out

这将返回所有子多维数据集的总和。然后,我们可以使用argmaxunravel_index来获取最大立方体的偏移量。示例:

np.random.seed(0)
a = np.random.randint(0,9,(N,N,N))
s = windowed_sum(a)
idx = np.unravel_index(np.argmax(s,), s.shape)