Сортировка на месте по столбцу не работает на срезах

Я пытаюсь отсортировать массив numpy по определенному столбцу (на месте), используя решение из этого ответа. По большей части это работает, но не работает с любым массивом, который является представлением другого массива:

In [35]: columnnum = 2

In [36]: a = np.array([[1,2,3], [4,7,5], [9,0,1]])

In [37]: a
Out[37]: 
array([[1, 2, 3],
       [4, 7, 5],
       [9, 0, 1]])

In [38]: b = a[:,(0, 2)]

In [39]: b
Out[39]: 
array([[1, 3],
       [4, 5],
       [9, 1]])

In [40]: a.view(','.join([a.dtype.str] * a.shape[1])).sort(order=['f%d' % columnnum], axis=0)

In [41]: a
Out[41]: 
array([[9, 0, 1],
       [1, 2, 3],
       [4, 7, 5]])

In [42]: b.view(','.join([b.dtype.str] * b.shape[1])).sort(order=['f%d' % columnnum], axis=0)
ValueError: new type not compatible with array.

Похоже, numpy не поддерживает представления представлений, что имеет определенный смысл, но теперь я не могу понять, как получить нужное мне представление для любого массива, будь то сам вид или нет. До сих пор я не смог найти способ получить необходимую информацию о представлении, которое мне нужно, чтобы построить новое, которое мне нужно.

На данный момент я использую метод сортировки на месте l = l[l[:,columnnum].argsort()], который отлично работает, но, поскольку я работаю с большими наборами данных, я хотел бы избежать дополнительных затрат памяти на вызов argsort() (список индексов). Есть ли способ получить необходимую информацию о представлении или сделать сортировку по столбцу?


person Linuxios    schedule 29.11.2016    source источник
comment
Я предполагаю, что вам не повезет. Я не так много знаю о внутренностях numpy, но мне кажется, что вы пытаетесь вывести представление за рамки того, что можно сделать без копирования данных.   -  person BrenBarn    schedule 29.11.2016
comment
@BrenBarn: Возможно. Я просто хотел посмотреть, есть ли у кого-нибудь здесь умные лайфхаки. Мое текущее решение состоит в том, чтобы просто ошибиться в данных, которые нельзя просмотреть, и сказать вызывающему абоненту сначала сделать копию... Некрасиво, но, по крайней мере, я могу предоставить полезное сообщение об ошибке.   -  person Linuxios    schedule 29.11.2016


Ответы (1)


In [1019]: a=np.array([[1,2,3],[4,7,5],[9,0,1]])
In [1020]: b=a[:,(0,2)]

Это a, который вы сортируете; структурированный массив с 3 полями. Он использует тот же буфер данных, но интерпретирует группы из 3 целых чисел как поля, а не столбцы.

In [1021]: a.view('i,i,i')
Out[1021]: 
array([[(1, 2, 3)],
       [(4, 7, 5)],
       [(9, 0, 1)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4')])

По той же логике вы пытаетесь view b:

In [1022]: b.view('i,i')
/usr/local/bin/ipython3:1: DeprecationWarning: Changing the shape of non-C contiguous array by
descriptor assignment is deprecated. To maintain
the Fortran contiguity of a multidimensional Fortran
array, use 'a.T.view(...).T' instead
  #!/usr/bin/python3
....
ValueError: new type not compatible with array.

Но если я использую 3 поля вместо 2, это работает (но с тем же предупреждением):

In [1023]: b.view('i,i,i')
/usr/local/bin/ipython3:1: DeprecationWarning:...
Out[1023]: 
array([[(1, 4, 9), (3, 5, 1)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4')])

Проблема в том, что b это Fortran order. Проверьте b.flags или

In [1026]: a.strides
Out[1026]: (12, 4)
In [1027]: b.strides
Out[1027]: (4, 12)

b — это копия, а не представление. Я не знаю, навскидку, почему эта конструкция b изменила порядок.

Прислушиваясь к предупреждению, я могу сделать:

In [1047]: b.T.view('i,i,i').T
Out[1047]: 
array([[(1, 4, 9), (3, 5, 1)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4')])

Копию по умолчанию (порядок c) b можно рассматривать как 2 поля:

In [1042]: b1=b.copy()
In [1043]: b1.strides
Out[1043]: (8, 4)
In [1044]: b1.view('i,i')
Out[1044]: 
array([[(1, 3)],
       [(4, 5)],
       [(9, 1)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4')])

Сноска на: https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html

Структура памяти результатов расширенного индексирования оптимизируется для каждой операции индексирования, и нельзя предполагать какой-либо конкретный порядок памяти.

====================

b в этом случае был построен с расширенной индексацией и, следовательно, является копией, даже истинное представление может быть недоступно для просмотра таким образом:

In [1052]: a[:,:2].view('i,i')
....
ValueError: new type not compatible with array.

In [1054]: a[:,:2].copy().view('i,i')
Out[1054]: 
array([[(1, 2)],
       [(4, 7)],
       [(9, 0)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4')])

Представление выбирает подмножество значений: "i,i,x,i,i,x,i,i,x...", и это не преобразуется в структурированный dtype.

Структурированное представление a делает: '(i,i,i),(i,i,i),...'

Вы можете выбрать подмножество полей структурированного массива:

In [1059]: a1=a.view('i,i,i')
In [1060]: a1
Out[1060]: 
array([[(1, 2, 3)],
       [(4, 7, 5)],
       [(9, 0, 1)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4')])
In [1061]: b1=a1[['f0','f2']]
In [1062]: b1
Out[1062]: 
array([[(1, 3)],
       [(4, 5)],
       [(9, 1)]], 
      dtype=[('f0', '<i4'), ('f2', '<i4')])

Но есть пределы того, что вы можете сделать с таким представлением. Значения можно изменить в a1 и увидеть в a и b1. Но я получаю сообщение об ошибке, если пытаюсь изменить значения в b1. Это на грани развития.

person hpaulj    schedule 29.11.2016
comment
Спасибо за подробный ответ! Прав ли я тогда, предполагая, что проверки того, что a.flags.c_contiguous является True, достаточно, чтобы гарантировать, что массив будет отображаться таким образом? - person Linuxios; 29.11.2016
comment
Может быть; Я не использовал такой тест. - person hpaulj; 29.11.2016
comment
Спасибо. Я предполагаю, что нет другого способа сортировать по столбцам, не так ли? - person Linuxios; 29.11.2016
comment
Как насчет np.lexsort? - person hpaulj; 29.11.2016
comment
Возможно, но у этого все еще есть проблема создания массива индексов. Это работает, но я предпочел бы избежать этого. - person Linuxios; 29.11.2016