Вибір конкретних рядків і стовпців із масиву NumPy


96

Я збожеволів, намагаючись зрозуміти, яка дурість я тут роблю не так.

Я використовую NumPy, і у мене є певні індекси рядків та конкретні індекси стовпців, з яких я хочу вибрати. Ось суть моєї проблеми:

import numpy as np

a = np.arange(20).reshape((5,4))
# array([[ 0,  1,  2,  3],
#        [ 4,  5,  6,  7],
#        [ 8,  9, 10, 11],
#        [12, 13, 14, 15],
#        [16, 17, 18, 19]])

# If I select certain rows, it works
print a[[0, 1, 3], :]
# array([[ 0,  1,  2,  3],
#        [ 4,  5,  6,  7],
#        [12, 13, 14, 15]])

# If I select certain rows and a single column, it works
print a[[0, 1, 3], 2]
# array([ 2,  6, 14])

# But if I select certain rows AND certain columns, it fails
print a[[0,1,3], [0,2]]
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# ValueError: shape mismatch: objects cannot be broadcast to a single shape

Чому це відбувається? Звичайно, я мав би змогу вибрати 1-й, 2-й і 4-й рядки, 1-й і 3-й стовпці? Результат, який я очікую:

a[[0,1,3], [0,2]] => [[0,  2],
                      [4,  6],
                      [12, 14]]

Tagged NumPy-нарізка для поліпшення знаходять. (Крім того, терміни "нарізка" та "нарізка" не зустрічаються у відкритому тексті, ми могли б використати кілька дублікатів із цими термінами, закритими в цьому)
smci

Відповіді:


86

Химерне індексування вимагає, щоб ви вказали всі індекси для кожного виміру. Ви надаєте 3 індекси для першого, і лише 2 для другого, звідси і помилка. Ви хочете зробити щось подібне:

>>> a[[[0, 0], [1, 1], [3, 3]], [[0,2], [0,2], [0, 2]]]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

Звичайно, це важко писати, тому ви можете дозволити мовлення допомогти вам:

>>> a[[[0], [1], [3]], [0, 2]]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

Це набагато простіше зробити, якщо індексувати масиви, а не списки:

>>> row_idx = np.array([0, 1, 3])
>>> col_idx = np.array([0, 2])
>>> a[row_idx[:, None], col_idx]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

4
Дякую, я не знав, що ти можеш це зробити! Трансляція дивна і чудова ... Після двох років безглуздості я все ще звикаю.
Правен

2
Дякую! Хоча інші відповіді відповідали правильно на моє запитання з точки зору повернення обраної матриці, ця відповідь стосувалася цього питання, одночасно вирішуючи питання про присвоєння (як встановити [[0,1,3], [0,2]] = 0 , наприклад).
Mike C

1
@Jaime - Тільки вчора я виявив одну гільзи вбудованої , щоб зробити саме мовний трюк ви пропонуєте: np.ix_
Praveen

1
Хтось може дати пояснення, чому синтаксис працює так? У чому причина, що це працює для обох перших прикладів, але не для третього. А також, як це вирішує інкапсуляція шуканих індексів у їх власні списки? Дякую
Аетос,

2
Чому рядки потрібно вкладати, а стовпці - ні?
AturSams

86

Як припускає Тоан, простим хаком буде просто виділити рядки спочатку, а потім виділити стовпці над цим .

>>> a[[0,1,3], :]            # Returns the rows you want
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [12, 13, 14, 15]])
>>> a[[0,1,3], :][:, [0,2]]  # Selects the columns you want as well
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

[Редагувати] Вбудований метод: np.ix_

Нещодавно я виявив, що numpy дає вам вбудований одношаровий вкладиш, щоб робити саме те , що запропонував @Jaime, але без необхідності використовувати синтаксис трансляції (що страждає від недостатньої читабельності). З документів:

За допомогою ix_ можна швидко побудувати масиви індексів, які будуть індексувати перехресний добуток. a[np.ix_([1,3],[2,5])]повертає масив [[a[1,2] a[1,5]], [a[3,2] a[3,5]]].

Отже, ви використовуєте його так:

>>> a = np.arange(20).reshape((5,4))
>>> a[np.ix_([0,1,3], [0,2])]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

І спосіб його роботи полягає в тому, що він дбає про вирівнювання масивів так, як запропонував Хайме, щоб мовлення відбувалося належним чином:

>>> np.ix_([0,1,3], [0,2])
(array([[0],
        [1],
        [3]]), array([[0, 2]]))

Крім того, як каже MikeC у коментарі, np.ix_перевага полягає у поверненні подання, чого моя перша (попередня редагування) відповідь не зробила. Це означає, що тепер ви можете призначити індексованому масиву:

>>> a[np.ix_([0,1,3], [0,2])] = -1
>>> a    
array([[-1,  1, -1,  3],
       [-1,  5, -1,  7],
       [ 8,  9, 10, 11],
       [-1, 13, -1, 15],
       [16, 17, 18, 19]])

4
У кількох тестах я також виявив np.ix_швидший спосіб вибору спочатку стовпців, а потім рядків (зазвичай це приблизно вдвічі швидше у моїх тестах квадратних масивів розмірами 1K-10K, де ви переіндексуєте всі рядки та стовпці).
Натан

7

ВИКОРИСТАННЯ:

 >>> a[[0,1,3]][:,[0,2]]
array([[ 0,  2],
   [ 4,  6],
   [12, 14]])

АБО:

>>> a[[0,1,3],::2]
array([[ 0,  2],
   [ 4,  6],
   [12, 14]])

10
Хоча це правильно, вам слід розглянути можливість розміщення трохи додаткової інформації, що пояснює, чому це правильно.
ebarr

2

Використання np.ix_- це найзручніший спосіб зробити це (як відповіли інші), але ось ще один цікавий спосіб зробити це:

>>> rows = [0, 1, 3]
>>> cols = [0, 2]

>>> a[rows].T[cols].T

array([[ 0,  2],
       [ 4,  6],
       [12, 14]])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.