2012-06-06 6 views
11

Простите меня, если это избыточное или супер базовое. Я прихожу на Python/Numpy из R и с трудом переворачиваю вещи в голове.Numpy: Сортировка многомерного массива многомерным массивом

У меня есть n-мерный массив, который я хочу отсортировать, используя другой n-мерный массив значений индекса. Я знаю, что могу обернуть это в цикле, но кажется, что должен быть очень сжатый способ Numpyonic избить это в подчинении. Вот мой пример кода, чтобы установить проблему, где п = 2:

a1 = random.standard_normal(size=[2,5]) 
index = array([[0,1,2,4,3] , [0,1,2,3,4] ]) 

так что теперь у меня есть 2 х 5 массив случайных чисел и индекс 2 х 5. Я прочитал справку для take() примерно 10 раз, но, по-моему, мой мозг не грохнул ею.

Я думал, что это может заставить меня есть:

take(a1, index) 

array([[ 0.29589188, -0.71279375, -0.18154864, -1.12184984, 0.25698875], 
     [ 0.29589188, -0.71279375, -0.18154864, 0.25698875, -1.12184984]]) 

но это явно переназначение только первый элемент (я полагаю, из-за уплощение).

Любые советы о том, как я получаю, от того, где я к решению, которое сортирует элемент 0 из a1 по элементу 0 индекса ... элемент n?

+0

Итак, если я правильно понимаю, вы хотите переупорядочить каждую «строку» «a1», используя индексы в каждой строке «index»? другими словами a1.take (index), если вы были 1D, но делаете это для каждой строки? –

+0

yup. Поэтому закажите первую строку a1 по первой строке индекса и второй строке a1 по второй строке индекса. А так как a1 растет до размера n, то индекс также будет. –

ответ

11

Я не могу думать о том, как работать это в N измерениях еще, но здесь версия 2D:

>>> a = np.random.standard_normal(size=(2,5)) 
>>> a 
array([[ 0.72322499, -0.05376714, -0.28316358, 1.43025844, -0.90814293], 
     [ 0.7459107 , 0.43020728, 0.05411805, -0.32813465, 2.38829386]]) 
>>> i = np.array([[0,1,2,4,3],[0,1,2,3,4]]) 
>>> a[np.arange(a.shape[0])[:,np.newaxis],i] 
array([[ 0.72322499, -0.05376714, -0.28316358, -0.90814293, 1.43025844], 
     [ 0.7459107 , 0.43020728, 0.05411805, -0.32813465, 2.38829386]]) 

Здесь N-мерная версия:

>>> a[list(np.ogrid[[slice(x) for x in a.shape]][:-1])+[i]] 

Вот как это работает:

Хорошо, давайте начнем с 3-мерного массива для иллюстрации.

>>> import numpy as np 
>>> a = np.arange(24).reshape((2,3,4)) 
>>> a 
array([[[ 0, 1, 2, 3], 
     [ 4, 5, 6, 7], 
     [ 8, 9, 10, 11]], 

     [[12, 13, 14, 15], 
     [16, 17, 18, 19], 
     [20, 21, 22, 23]]]) 

Вы можете получить доступ к элементам этого массива, указав индекс по каждой оси следующим образом:

>>> a[0,1,2] 
6 

Это эквивалентно a[0][1][2], который, как вы бы получить доступ и тот же элемент, если бы мы имели дело с вместо массива.

Numpy позволяет получить даже любителю, когда нарезка массивы:

>>> a[[0,1],[1,1],[2,2]] 
array([ 6, 18]) 
>>> a[[0,1],[1,2],[2,2]] 
array([ 6, 22]) 

Эти примеры были бы эквивалентны [a[0][1][2],a[1][1][2]] и [a[0][1][2],a[1][2][2]], если мы имеем дело со списками.

Вы можете даже отказаться от повторяющихся индексов, и numpy будет определять, что вы хотите. Например, приведенные выше примеры можно было бы то же самое написано:

>>> a[[0,1],1,2] 
array([ 6, 18]) 
>>> a[[0,1],[1,2],2] 
array([ 6, 22]) 

Форма массива (или список) вы отрезаете с в каждом измерении влияет только на форму возвращаемого массива. Другими словами, numpy не заботится о том, чтобы вы пытались индексировать массив массивом формы (2,3,4), когда он вытаскивает значения, за исключением того, что он вернет вам массив формы (2,3,4).Например:

>>> a[[[0,0],[0,0]],[[0,0],[0,0]],[[0,0],[0,0]]] 
array([[0, 0], 
     [0, 0]]) 

В этом случае мы захватывая один и тот же элемент, a[0,0,0] снова и снова, но NumPy возвращает массив с одной и той же форме, как мы прошли в

Ok, на. твоя проблема. Вы хотите проиндексировать массив вдоль последней оси с числами в вашем массиве index. Итак, для примера в вашем вопросе вы хотели бы [[a[0,0],a[0,1],a[0,2],a[0,4],a[0,3]],a[1,0],a[1,1],...

Тот факт, что ваш индексный массив является многомерным, как я уже говорил ранее, не говорит о том, что вы хотите извлечь из этих индексов ничего лишнего; он просто определяет форму выходного массива. Итак, в вашем примере вам нужно указать numpy, что первые 5 значений нужно вытащить из a[0], а последние 5 из a[1]. Легко!

>>> a[[[0]*5,[1]*5],index] 

Это усложняется в N измерениях, но давайте сделаем это для 3 одномерного массива a я определенным образом выше. Предположим, что мы имеем следующий индексный массив:

>>> i = np.array(range(4)[::-1]*6).reshape(a.shape) 
>>> i 
array([[[3, 2, 1, 0], 
     [3, 2, 1, 0], 
     [3, 2, 1, 0]], 

     [[3, 2, 1, 0], 
     [3, 2, 1, 0], 
     [3, 2, 1, 0]]]) 

Таким образом, эти значения являются значениями для индексов вдоль последней оси. Нам нужно указать numpy, какие индексы вдоль первой и второй осей должны быть взяты из этих чисел; то есть мы должны сказать NumPy, что индексы для первой оси являются:

i1 = [[[0, 0, 0, 0], 
     [0, 0, 0, 0], 
     [0, 0, 0, 0]], 

     [[1, 1, 1, 1], 
     [1, 1, 1, 1], 
     [1, 1, 1, 1]]] 

и индексы для второй оси являются:

i2 = [[[0, 0, 0, 0], 
     [1, 1, 1, 1], 
     [2, 2, 2, 2]], 

     [[0, 0, 0, 0], 
     [1, 1, 1, 1], 
     [2, 2, 2, 2]]] 

Тогда мы можем просто сделать:

>>> a[i1,i2,i] 
array([[[ 3, 2, 1, 0], 
     [ 7, 6, 5, 4], 
     [11, 10, 9, 8]], 

     [[15, 14, 13, 12], 
     [19, 18, 17, 16], 
     [23, 22, 21, 20]]]) 

Удобная функция numpy, которая генерирует i1 и i2, называется np.mgrid. Я использую np.ogrid в своем ответе, который в этом случае эквивалентен из-за магии numpy, о которой я говорил ранее.

Надеюсь, что это поможет!

+0

Я думаю, вы прибивали то, что я хочу. Большое большое спасибо! Не быть чрезмерно жадным, но можете ли вы объяснить, что делает n-мерная версия? Я продолжаю играть с ним, но я не собираюсь делать это. –

+0

Нет проблем. Я добавил объяснение, которое, кстати, занимало больше времени, чем писать, чтобы понять ответ! – user545424

+0

Вы, сэр, заслуживаете медали!Спасибо за фантастический ответ. –

3

После игры с этим какой-то более сегодня я понял, что если я использовал функцию сопоставителя вместе с взятием я мог бы решить 2 одномерный вариант действительно просто, как это:

a1 = random.standard_normal(size=[2,5]) 
index = array([[0,1,2,4,3] , [0,1,2,3,4] ]) 
map(take, a1, index) 

мне нужно map()take() к каждый элемент в a1

Конечно, принятый ответ решает n-мерную версию. Однако в ретроспективе я решил, что мне действительно не нужно n-мерное решение, а только двухмерная версия.

Смежные вопросы