Применение полей/dtype к массиву, представляющему точки для сортировки, а затем удаление полей/dtype?

#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 оно определено явно, в то время как my points создается с помощью различных 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')])