连接不同大小的数组

Concatenating arrays of different sizes

我正在尝试对由 sklearn 中的 make_blobs 函数创建的 numpy 点数组执行四叉树算法。我正在尝试创建一个递归分区 KMeans,其中质心位于 space 的每个四叉树分区中。这是我的分区函数:

def partition(self, data):
    if data.size != 0:
        minX = np.min(data[:,0])
        maxX = np.max(data[:,0])
        minY = np.min(data[:,1])
        maxY = np.max(data[:,1])
        middleX = (maxX + minX)/2
        middleY = (maxY + minY)/2
        parts1 = np.array([i for i in data if i[0] < middleX and i[1] > middleY])
        parts2 = np.array([i for i in data if i[0] > middleX and i[1] > middleY])
        parts3 = np.array([i for i in data if i[0] < middleX and i[1] < middleY])
        parts4 = np.array([i for i in data if i[0] > middleX and i[1] < middleY])
        parts = np.array([parts1, parts2, parts3, parts4])
        return parts
    else:
        return np.array([[], [], [], []])            

我的 make_blobs 函数创建的数据集具有以下结构:

[[ 9.26360832 -9.18849755] [ 7.3971609 9.92622627] [ 7.29022892 -10.39359926] ... [ 8.66667995 -11.99184453] [ 5.80627027 10.53947197] [ 6.14214488 -0.73405016]]

此函数的示例输出可能是:

[array([[3.95348068, 4.74190848]]) array([[4.47174131, 4.67345222], [4.73856072, 4.68464296]]) array([], dtype=float64) array([[4.48952751, 4.38898038], [4.47734611, 4.34300488]])]

,也就是形状(4,)。然而,它也可以是形状 (4,1,2),如下所示:

[[[-7.17718091 -4.92636967]]

[[-6.66796907 -4.94025585]]

[[-7.03501112 -5.17783394]]

[[-6.45835039 -5.17271443]]]

然后我尝试连接分区,以便得到一个包含分区的大数组。这是负责串联的行:

part_data = np.hstack([self.partition(d) for d in part_data if np.shape(self.partition(d)) != (4,0)])

当分区为空或相等时出现问题,因此形状为(4,0)、(4,1,2) 或(4,2,2)。数组不能以这种方式连接。错误状态如下:

ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 10 has 3 dimension(s)

是否可以忽略这些形状或以某种方式将它们重塑为 (4,)?也许有一些技巧可以不作为数组而是作为对象附加?如有任何回应,我将不胜感激。 这是此示例的完整代码:

import numpy as np
from sklearn.datasets import make_blobs

def generateDataset(k, dimensions, n_samples):
    X, y_true = make_blobs(n_samples = n_samples, centers = k, n_features= dimensions, cluster_std = 1.1)
    return X, y_true
X, y_true = generateDataset(3,2,10000)

def partition(data):
    if data.size != 0:
        minX = np.min(data[:,0])
        maxX = np.max(data[:,0])
        minY = np.min(data[:,1])
        maxY = np.max(data[:,1])
        middleX = (maxX + minX)/2
        middleY = (maxY + minY)/2
        parts1 = np.array([i for i in data if i[0] < middleX and i[1] > middleY])
        parts2 = np.array([i for i in data if i[0] > middleX and i[1] > middleY])
        parts3 = np.array([i for i in data if i[0] < middleX and i[1] < middleY])
        parts4 = np.array([i for i in data if i[0] > middleX and i[1] < middleY])
        parts = np.array([parts1, parts2, parts3, parts4])
        return parts
    else:
        return np.array([[], [], [], []])

part_data = partition(X)
for i in range(6):
    if i >= 1:
        part_data = np.hstack([partition(d) for d in part_data if np.shape(partition(d)) != (4,0)])

当我第一次读到这个问题时,我以为你在尝试 hstack 形状为 (4,0), (4,1,2) or (4,2,2) 的数组。但是根据评论,似乎也有形状 (4,) 数组。

4部分来自连接4个元素

parts = np.array([parts1, parts2, parts3, parts4])

每一个都是表达式的结果:

parts1 = np.array([i for i in data if i[0] < middleX and i[1] > middleY])

您没有提供 data 的示例(不要指望我们从您的代码中重新创建它!),甚至没有 parts.[=45= 的示例]

当我构造一个示例二维数组时,猜测什么会起作用:

In [18]: data = np.array([[1,3],[2,4],[3,1]])
In [19]: [i for i in data]           # iterate on the rows
Out[19]: [array([1, 3]), array([2, 4]), array([3, 1])]

各种'range'测试:

In [20]: [i for i in data if i[0]<2 and i[1]>2]
Out[20]: [array([1, 3])]
In [21]: np.array(_)
Out[21]: array([[1, 3]])
In [22]: _.shape
Out[22]: (1, 2)
In [23]: [i for i in data if i[0]<2 and i[1]>3]
Out[23]: []
In [24]: [i for i in data if i[0]<2 and i[1]>1]
Out[24]: [array([1, 3])]
In [25]: [i for i in data if i[0]<1 and i[1]>1]
Out[25]: []
In [26]: [i for i in data if i[0]<3 and i[1]>1]
Out[26]: [array([1, 3]), array([2, 4])]
In [27]: np.array([i for i in data if i[0]<3 and i[1]>1])
Out[27]: 
array([[1, 3],
       [2, 4]])
In [29]: np.array([i for i in data if i[0]<3 and i[1]>3])
Out[29]: array([[2, 4]])

所以我可以获得一个 parts 数组,它是 (0,)、(1,2) 或 (2,2)(或更多的第一维)。

将其中的 4 个加入一个数组并得到一个 (4,1,2) 等。但是等等,这 4 个测试中的每一个都可以给出不同大小的数组,在这种情况下 np.array(parts....) 将产生一个 object 形状为 (4,) 的 dtype 数组。

这是怎么回事?您混合了大部分 (4,) 对象 dtype 数组以及一些 (4,0) 和 (4,n,2) 形状的数字 dtype 数组?

除了完整的代码或最小的示例,我们还应该要求您显示您正在尝试的列表 hstack:

[partition(d) for d in part_data if np.shape(partition(d)) != (4,0)]

让我们尝试从这些示例结果中的 4 个创建 partition 数组:

In [46]: [Out[20],Out[27],Out[25],Out[29]]
Out[46]: 
[[array([1, 3])],
 array([[1, 3],
        [2, 4]]),
 [],
 array([[2, 4]])]
In [47]: x1=np.array([Out[20],Out[27],Out[25],Out[29]])
<ipython-input-47-b04a5e3fb51c>:1: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  x1=np.array([Out[20],Out[27],Out[25],Out[29]])
In [48]: x1
Out[48]: 
array([list([array([1, 3])]), array([[1, 3],
                                     [2, 4]]), list([]), array([[2, 4]])],
      dtype=object)

你收到 ragged array 警告了吗?请注意,结果数组是 (4,) object dtype.

如果所有部分都是相同的形状,例如 (1,2):

In [49]: x2=np.array([Out[29],Out[29],Out[29],Out[29]])
In [50]: x2.shape
Out[50]: (4, 1, 2)
In [51]: x2
Out[51]: 
array([[[2, 4]],

       [[2, 4]],

       [[2, 4]],

       [[2, 4]]])

或 (4,0)

In [54]: x3=np.array([Out[23],Out[23],Out[23],Out[23]])
In [55]: x3
Out[55]: array([], shape=(4, 0), dtype=float64)

In [56]: x4=np.array([Out[27],Out[27],Out[27],Out[27]])
In [57]: x4.shape
Out[57]: (4, 2, 2)

即使没有 (4,0) 我们也会得到尺寸不匹配:

In [59]: np.hstack((x1,x2,x4))
Traceback (most recent call last):
  Input In [59] in <cell line: 1>
    np.hstack((x1,x2,x4))
  File <__array_function__ internals>:180 in hstack
  File /usr/local/lib/python3.8/dist-packages/numpy/core/shape_base.py:343 in hstack
    return _nx.concatenate(arrs, 0)
  File <__array_function__ internals>:180 in concatenate
ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 1 has 3 dimension(s)

我们可以将 (4,) 中的几个连接成一个新的对象 dtype 数组:

In [61]: np.hstack((x1,x1,x1)).shape
Out[61]: (12,)

关键问题是 np.array((part1,part2,...)) 不是制作 (4,) 对象 dtype 数组的可靠方法。有时如果生成 (4,) 并带有警告,有时它会生成 (4,0) 或 (4,n,2)。掩饰 ragged warning 你把你自己和我们都搞糊涂了!

如果我们定义一个辅助函数,我们就可以可靠地创建一个对象 dtype 数组,即使输入的形状完全相同:

In [62]: def foo(*args):
    ...:     res = np.empty(len(args),object)
    ...:     res[:] = args
    ...:     return res
    ...: 

用它重新创建 4 个部分:

In [63]: x1 = foo([Out[29],Out[29],Out[29],Out[29]])
In [64]: x1.shape,x1.dtype
Out[64]: ((1,), dtype('O'))
In [65]: x1 = foo(Out[29],Out[29],Out[29],Out[29])
In [66]: x1.shape, x1.dtype
Out[66]: ((4,), dtype('O'))
In [67]: x2=foo(Out[29],Out[29],Out[29],Out[29])
In [68]: x2.shape, x2.dtype
Out[68]: ((4,), dtype('O'))
In [69]: x3=foo(Out[23],Out[23],Out[23],Out[23])
In [70]: x3.shape, x3.dtype
Out[70]: ((4,), dtype('O'))
In [71]: x4=foo(Out[27],Out[27],Out[27],Out[27])
In [72]: x4.shape, x4.dtype
Out[72]: ((4,), dtype('O'))
In [73]: arr = np.hstack((x1,x2,x3,x4))
In [74]: arr.shape
Out[74]: (16,)

生成的数组有点乱,但值得一看。这真的是您想要的并且能够使用的吗:

In [75]: arr
Out[75]: 
array([array([[2, 4]]), array([[2, 4]]), array([[2, 4]]), array([[2, 4]]),
       array([[2, 4]]), array([[2, 4]]), array([[2, 4]]), array([[2, 4]]),
       list([]), list([]), list([]), list([]), array([[1, 3],
                                                      [2, 4]]),
       array([[1, 3],
              [2, 4]]), array([[1, 3],
                               [2, 4]]), array([[1, 3],
                                                [2, 4]])], dtype=object)

等效列表可能同样有用:

In [76]: arr.tolist()
Out[76]: 
[array([[2, 4]]),
 array([[2, 4]]),
 array([[2, 4]]),
 array([[2, 4]]),
 array([[2, 4]]),
 array([[2, 4]]),
 array([[2, 4]]),
 array([[2, 4]]),
 [],
 [],
 [],
 [],
 array([[1, 3],
        [2, 4]]),
 array([[1, 3],
        [2, 4]]),
 array([[1, 3],
        [2, 4]]),
 array([[1, 3],
        [2, 4]])]

输入全部为空列表的 x3 情况可能需要一些改进:

In [80]: x3
Out[80]: array([list([]), list([]), list([]), list([])], dtype=object)

编辑

您添加的示例数组是:

[array([[3.95348068, 4.74190848]]) 
 array([[4.47174131, 4.67345222], 
        [4.73856072, 4.68464296]]) 
 array([], dtype=float64) 
 array([[4.48952751, 4.38898038], 
        [4.47734611, 4.34300488]])]

即(4,)(不是(4,0)或(4,1)),和object dtype。这很像一个列表,包含对 4 个数组的引用。这些阵列的形状不同,(1,2),(2,2),(0,),(2,2)。由于不同的形状,它只能制作一个对象 dtype 数组(带有参差不齐的数组警告)。

下面的示例是 (4,1,2),通过将 np.array 应用于 4 个形状均为 (1,2) 的数组的列表而制成。 np.array优先制作多维数值数组。从该列表创建一个 (4,) 对象数组需要特殊操作,如我在 foo 函数中所示。