#python #arrays #numpy #sorting
Вопрос:
У меня есть массив, который представляет (x, y, z)
точки
x = np.array([
[0, 1, 0],
[0, 2, 1],
[1, 1, 0],
[1, 2, 1],
[2, 1, 0],
[2, 2, 1]
])
Я хотел бы реализовать некоторую схему упорядочения на этих точках, используя numpy.sort
. Если я sort
использую эти точки без реализации некоторых специализированных полей/dtype, numpy
переупорядочивает оси точек
x.sort(axis=0)
результаты в
[[0 1 0]
[0 1 0]
[1 1 0]
[1 2 1]
[2 2 1]
[2 2 1]]
Хотя, если я создам специализированную dtype
на основе некоторого определения поля
point_dtype = [('x', float), ('y', float), ('z', float)]
и определите массив с помощью этого dtype
, а затем отсортируйте
point_dtype = [('x', float), ('y', float), ('z', float)]
x = np.array([
(0, 1, 0),
(0, 2, 1),
(1, 1, 0),
(1, 2, 1),
(2, 1, 0),
(2, 2, 1)
], dtype=point_dtype)
x.sort(axis=0, order=['x', 'y'])
В результате получается упорядочение точек, которое имеет смысл
[(0., 1., 0.) (0., 2., 1.) (1., 1., 0.) (1., 2., 1.) (2., 1., 0.) (2., 2., 1.)]
Проблема в том, что у меня уже есть свой x
из приведенного выше примера, и он довольно большой ( (18M, 3)
). Я не совсем уверен, как я могу «применить» point_dtype
к этому массиву, чтобы я мог sort
это сделать, как описано.
Если я попытаюсь astype
открыть свой массив с point_dtype
помощью, то numpy
, похоже, «распакую» каждое значение в массиве
point_dtype = [('x', float), ('y', float), ('z', float)]
x = np.array([
[0, 1, 0],
[0, 2, 1],
[1, 1, 0],
[1, 2, 1],
[2, 1, 0],
[2, 2, 1]
])
x = x.astype(point_dtype)
результаты x
выглядят так
[[(0., 0., 0.) (1., 1., 1.) (0., 0., 0.)]
[(0., 0., 0.) (2., 2., 2.) (1., 1., 1.)]
[(1., 1., 1.) (1., 1., 1.) (0., 0., 0.)]
[(1., 1., 1.) (2., 2., 2.) (1., 1., 1.)]
[(2., 2., 2.) (1., 1., 1.) (0., 0., 0.)]
[(2., 2., 2.) (2., 2., 2.) (1., 1., 1.)]]
вместо
[(0., 1., 0.) (0., 2., 1.) (1., 1., 0.) (1., 2., 1.) (2., 1., 0.) (2., 2., 1.)]
Как я могу ретроактивно применить специализацию dtype
к уже существующему numpy
массиву?
Я проверил документацию numpy.astype
, хотя ни один из возможных параметров, похоже, не решает эту ситуацию. Я также нашел numpy
документацию по структурированным массивам, хотя, опять же, там, похоже, ничего не говорится о ретроактивном применении a dtype
.
Кроме того, как я могу удалить это dtype
, чтобы получить «сырое» представление массива?
Ответ №1:
Создайте представление для эффективного создания объекта, совместно использующего базовый буфер:
>>> x = np.array([
... [0, 1, 0],
... [0, 2, 1],
... [1, 1, 0],
... [1, 2, 1],
... [2, 1, 0],
... [2, 2, 1]
... ], dtype=np.float64)
>>> x
array([[0., 1., 0.],
[0., 2., 1.],
[1., 1., 0.],
[1., 2., 1.],
[2., 1., 0.],
[2., 2., 1.]])
>>> point_dtype = [('x', float), ('y', float), ('z', float)]
>>> x.view(point_dtype)
array([[(0., 1., 0.)],
[(0., 2., 1.)],
[(1., 1., 0.)],
[(1., 2., 1.)],
[(2., 1., 0.)],
[(2., 2., 1.)]], dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])
Обратите внимание, что представление является независимым объектом python:
>>> x is view
False
Но он использует один и тот же базовый буфер:
>>> x[0,0] = 99
>>> view
array([[(99., 1., 0.)],
[( 0., 2., 1.)],
[( 1., 1., 0.)],
[( 1., 2., 1.)],
[( 2., 1., 0.)],
[( 2., 2., 1.)]], dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])
>>>
Другой подход состоит в том, чтобы просто изменить тип dtype вашего исходного объекта:
>>> x.dtype = point_dtype
>>> x
array([[(99., 1., 0.)],
[( 0., 2., 1.)],
[( 1., 1., 0.)],
[( 1., 2., 1.)],
[( 2., 1., 0.)],
[( 2., 2., 1.)]], dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])
>>> x.dtype = np.float64
>>> x
array([[99., 1., 0.],
[ 0., 2., 1.],
[ 1., 1., 0.],
[ 1., 2., 1.],
[ 2., 1., 0.],
[ 2., 2., 1.]])
Комментарии:
1. Возможно, пока я привлекаю ваше внимание. Я сталкиваюсь с проблемой с моим массивом
points
со следующими характеристиками [points.shape: (18928298, 3) dtype: float64 type: <class 'numpy.ndarray'> points[0, :]: [ 2.72499860e 05 3.28972911e 06 -1.54100000e 01]
]. // Когда я пытаюсь сгенерироватьview
points_view = points.view(dtype=[('x', points.dtype), ('y', points.dtype), ('z', points.dtype)])
или даже жесткоdtype
закодировать, как толькоnp.float64
я получаю «При переходе на больший тип dtype его размер должен быть делителем общего размера в байтах последней оси массива».2. Насколько я могу судить, я следую той же схеме, что и в приведенном вами примере. Единственное различие заключается в том, что
x
оно определено явно, в то время как mypoints
создается с помощью различныхhstack
иvstack
после загрузки некоторых файлов данных. // Связано ли это с тем, как организована памятьpoints
(или в примереx
)?3. Ааа. Похоже, в этом и была проблема. Я исправил проблему,
points = points.astype(points.dtype, order='C')
Ответ №2:
np.lexsort
делает то, что ты хочешь (я думаю):
In [23]: np.lexsort((x[:,1],x[:,0]))
Out[23]: array([0, 1, 2, 3, 4, 5])
In [24]: x[_,:]
Out[24]:
array([[0, 1, 0],
[0, 2, 1],
[1, 1, 0],
[1, 2, 1],
[2, 1, 0],
[2, 2, 1]])
В последних версиях numpy добавлена unstructured_to_structured
функция, которая должна упростить создание структурированного массива. Это не так, хотя и создает представление.
In [40]: import numpy.lib.recfunctions as rf
In [41]: X = rf.unstructured_to_structured(x, np.dtype([('x', '<f8'), ('y', '<f8'), ('z', '<f8')]))
In [42]: X
Out[42]:
array([(0., 1., 0.), (0., 2., 1.), (1., 1., 0.), (1., 2., 1.),
(2., 1., 0.), (2., 2., 1.)],
dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])
In [43]: X.sort(order=['x','y'])
In [44]: X
Out[44]:
array([(0., 1., 0.), (0., 2., 1.), (1., 1., 0.), (1., 2., 1.),
(2., 1., 0.), (2., 2., 1.)],
dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])