Как поменять местами элементы массива Nx2 numpy, если условие выполнено? - PullRequest
0 голосов
/ 21 января 2020

Я бы хотел поменять местами элементы i-й строки в массив Nx2 numpy my_array, если условие swap[i] равно True.

Моя попытка:

def swap_positions_conditionally(my_array, swap):
    for i in range(np.shape(my_array)[0]):
        if swap[i]:
            my_array[i] = my_array[i][::-1]
    return my_array

работает нормально, например, учитывая,

my_array = array([[0, 1],
                  [2, 3],
                  [4, 5],
                  [6, 7],
                  [8, 9]])

swap = array([0, 0, 1, 1, 0])

дает ожидаемый результат

[[0 1]
 [2 3]
 [5 4]
 [7 6]
 [8 9]]

Однако, вероятно, существует более идиоматическое выражение c для перезаписи моего swap_position_conditionally.
Каков был бы лучший (и более эффективный) способ написать это?

Ответы [ 3 ]

1 голос
/ 21 января 2020

И вот один из них, использующий логическое индексирование Numpy напрямую:

import numpy as np

my_array = np.asarray([[0, 1],
                  [2, 3],
                  [4, 5],
                  [6, 7],
                  [8, 9]])

swap = np.array([0, 0, 1, 1, 0], dtype=bool)

my_array[swap, :] = my_array[swap,:][:,(1,0)]

Разбивка ключевой строки:

  • my_array[swap, :] = означает «Назначить строки, где swap истина "
  • my_array[swap,:] означает" выбрать всю строку, где swap истина "
  • [:,(1,0)] означает" для каждой строки слева ", поменяйте местами столбцы 0 и 1 "

О" более эффективной "части вопроса ...

Общая настройка для всех тестов (начальное число гарантирует, что последовательности идентичны):

import timeit
setup= '''
import numpy as np

np.random.seed(42)
my_array = np.random.random([10000,2])
swap = np.random.random([10000]) > 0.5
'''

Все тесты выполняются за 1000 итераций

Исходный код: 5,621 секунды

timeit.timeit('swap_positions_conditionally(my_array, swap)', setup=setup, number=1000)

Добавлено определение от swap_positions_conditionally до setup, как показано в вопросе.

Этот ответ: 0,2657 секунды

timeit.timeit('my_array[swap, :] = my_array[swap,:][:,(1,0)]', setup=setup, number=1000)

Ответ Дивакара: 0,176 секунды

timeit.timeit('np.where(swap[:,None]!=1,my_array,my_array[:,::-1])', setup=setup, number=1000)

Первый ответ Yatu: 0,214 секунды

timeit.timeit('np.take_along_axis(my_array, np.c_[swap, 1-swap], axis=1)', setup=setup, number=1000)

Второй ответ Yatu : 0,2547 секунды

timeit.timeit('my_array[swap,:] = my_array[swap,::-1]', setup=setup, number=1000)

Выводы

Профилирование показывает, что версия Divakar является самой быстрой. Какой бы из интуитивно понятных или удобных для чтения файлов - вопрос вкусов, вы можете выбрать тот, который вы предпочитаете (хотя я лично являюсь поклонником читаемости индексации ...)

1 голос
/ 21 января 2020

Вот один из способов замены массива Nx2 на большее количество столбцов с нарезкой отрицательного шага по размеру при попытке -

In [56]: np.where(swap[:,None]==1, my_array[:,::-1], my_array)
Out[56]: 
array([[0, 1],
       [2, 3],
       [5, 4],
       [7, 6],
       [8, 9]])

Синтаксис: np.where(conditional_statement, choose_for_True, choose_for_False). Итак, в нашем случае мы хотим перевернуть / поменять местами, когда swap равно 1, иначе нет. Эта [:,None] часть необходима, чтобы сделать это поэлементно в каждой строке. Если swap уже является логическим массивом, пропустите часть сравнения.

1 голос
/ 21 января 2020

Вот один из них, использующий np.take_along_axis:

np.take_along_axis(my_array, np.c_[swap, 1-swap], axis=1)

array([[0, 1],
       [2, 3],
       [5, 4],
       [7, 6],
       [8, 9]])

Или логическое индексирование:

swap = swap.astype(bool)
my_array[swap,:] = my_array[swap,::-1]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...