Я пытаюсь выполнить алгоритм quadtree для массива точек numpy, созданного функцией make_blobs из sklearn. Я пытаюсь создать KMeans рекурсивного раздела, в котором центроиды находятся в каждом разделе дерева квадрантов пространства. Вот моя функция разбиения:
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)])
Рассмотрите возможность повторного запуска np.zeros((4,0,2), dtype) вместо np.array([[], [], [], []]) (что дает (4,0)).
@hpaulj Проблема в том, что не все разделы имеют одинаковый размер, поэтому общая форма массива - (4,). Также я игнорирую (4,0) в строке конкатенации.
Как весь код должен помочь? Вы знаете, как должны совпадать фигуры, чтобы их можно было соединить. Если массивы (4,0) не проблема, то какие?
@hpaulj Проблема в (4,1,2), (4,2,2) и т. д. Их нельзя объединить с (4,) массивами, но они мне нужны
@Ali_Sh Кажется, я объяснил, каков мой ожидаемый результат. Функция разбиения возвращает 4 части пространства. В каждой части количество точек может быть разным, поэтому ожидаемая форма (4,). Однако, когда количество точек в каждой части одинаково, форма определяется, например, как (4,1,2). Извините, но я не могу представить это в 5-10 строк. Если вы запустите код, который я предоставил, станет ясно, в чем проблема.






Когда я впервые прочитал вопрос, я подумал, что вы пытаетесь создать массив 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.
Когда я создам образец 2d-массива, угадайте, что будет работать:
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])]
различные «диапазонные» тесты:
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 dtype с формой (4,).
Это то, что происходит? У вас есть сочетание в основном (4,) массивов объектов dtype, а также некоторых (4,0) и (4,n,2) числовых dtype в форме?
Мы должны были потребовать от вас показать не только полный код или минимальный пример, но и список, который вы пытаетесь hstack:
[partition(d) for d in part_data if np.shape(partition(d)) != (4,0)]
Давайте попробуем создать массив partition из 4 результатов примера:
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,) 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.
hstackобъединит массивы во 2-м измерении, например. (4,1,2) с (4,2,2) возвращает (4,3,2). Все фигуры должны быть в форме (4, n, 2). Вероятно, проще всего удалить массивы типа (4,0) из списка передhstack. (4,0,2) будет работать, но не изменит результат.