Объединение массивов разного размера

Я пытаюсь выполнить алгоритм 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)])
hstack объединит массивы во 2-м измерении, например. (4,1,2) с (4,2,2) возвращает (4,3,2). Все фигуры должны быть в форме (4, n, 2). Вероятно, проще всего удалить массивы типа (4,0) из списка перед hstack. (4,0,2) будет работать, но не изменит результат.
hpaulj 07.05.2022 17:35

Рассмотрите возможность повторного запуска np.zeros((4,0,2), dtype) вместо np.array([[], [], [], []]) (что дает (4,0)).

hpaulj 07.05.2022 17:38

@hpaulj Проблема в том, что не все разделы имеют одинаковый размер, поэтому общая форма массива - (4,). Также я игнорирую (4,0) в строке конкатенации.

Kuba Głowacz 08.05.2022 11:38

Как весь код должен помочь? Вы знаете, как должны совпадать фигуры, чтобы их можно было соединить. Если массивы (4,0) не проблема, то какие?

hpaulj 08.05.2022 13:31

@hpaulj Проблема в (4,1,2), (4,2,2) и т. д. Их нельзя объединить с (4,) массивами, но они мне нужны

Kuba Głowacz 08.05.2022 13:51

@Ali_Sh Кажется, я объяснил, каков мой ожидаемый результат. Функция разбиения возвращает 4 части пространства. В каждой части количество точек может быть разным, поэтому ожидаемая форма (4,). Однако, когда количество точек в каждой части одинаково, форма определяется, например, как (4,1,2). Извините, но я не могу представить это в 5-10 строк. Если вы запустите код, который я предоставил, станет ясно, в чем проблема.

Kuba Głowacz 08.05.2022 17:16
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
6
85
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Когда я впервые прочитал вопрос, я подумал, что вы пытаетесь создать массив 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.

Другие вопросы по теме