NumPy, вибираючи певний індекс стовпця на рядок, використовуючи список індексів


90

Я намагаюся вибрати конкретні стовпці для рядка NumPyматриці.

Припустимо, у мене є така матриця, яку я б назвав X:

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

У мене також є listіндекс стовпців для кожного рядка, який я б назвав Y:

[1, 0, 2]

Мені потрібно отримати значення:

[2]
[4]
[9]

Замість a listз індексами Yя також можу створити матрицю з тією ж формою, Xде кожен стовпець має значення a bool/ intу діапазоні 0-1, вказуючи, чи є це необхідним стовпцем.

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

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

Отже, мені було цікаво, чи є краще рішення?

Дякую.


Чи вам відповідь краща? stackoverflow.com/a/17081678/5046896
GoingMyWay

Відповіді:


102

Якщо у вас є логічний масив, ви можете зробити прямий вибір на основі цього приблизно так:

>>> a = np.array([True, True, True, False, False])
>>> b = np.array([1,2,3,4,5])
>>> b[a]
array([1, 2, 3])

Щоб продовжити свій початковий приклад, ви можете зробити наступне:

>>> a = np.array([[1,2,3], [4,5,6], [7,8,9]])
>>> b = np.array([[False,True,False],[True,False,False],[False,False,True]])
>>> a[b]
array([2, 4, 9])

Ви також можете додати arangeі зробити прямий вибір цього, хоча залежно від того, як ви створюєте свій логічний масив і як ваш код виглядає як YMMV.

>>> a = np.array([[1,2,3], [4,5,6], [7,8,9]])
>>> a[np.arange(len(a)), [1,0,2]]
array([2, 4, 9])

Сподіваюся, це допоможе, повідомте мене, якщо у вас є ще запитання.


11
+1 для прикладу з використанням arange. Це було особливо корисно для мене для отримання різних блоків з декількох матриць (так, в основному, це 3D-приклад цього прикладу)
Griddo

1
Привіт, не могли б ви пояснити, чому ми маємо використовувати arangeзамість цього :? Я знаю, що ваш спосіб працює, а мій - ні, але я хотів би зрозуміти, чому.
marcotama

@tamzord, оскільки це масив numpy, а не ванільний список пітонів, тому :синтаксис працює не однаково.
Слейтер Вікторов

1
@SlaterTyranus, дякую за відповідь. Я зрозумів, після деякого прочитання, таке змішування :з розширеним індексуванням означає: "для кожного підпростору разом :застосовуйте дане розширене індексування". Чи правильно розумію моє розуміння?
marcotama

@tamzord пояснити, що ви маєте на увазі під "підпростором"
Слейтер Вікторов

35

Ви можете зробити щось подібне:

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

In [8]: lst = [1, 0, 2]

In [9]: a[np.arange(len(a)), lst]
Out[9]: array([2, 4, 9])

Докладніше про індексування багатовимірних масивів: http://docs.scipy.org/doc/numpy/user/basics.indexing.html#indexing-multi-dimensional-arrays


1
намагається зрозуміти, навіщо потрібний arange, а не просто ':' або діапазон.
MadmanLee

@MadmanLee Привіт, при використанні :буде виводитись кілька len(a)разів результатів, натомість із зазначенням індексу кожного рядка будуть надруковані очікувані результати.
GoingMyWay

1
Я думаю, що це саме правильний та елегантний спосіб вирішити цю проблему.
GoingMyWay

6

Простий спосіб може виглядати так:

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

In [2]: y = [1, 0, 2]  #list of indices we want to select from matrix 'a'

range(a.shape[0]) повернеться array([0, 1, 2])

In [3]: a[range(a.shape[0]), y] #we're selecting y indices from every row
Out[3]: array([2, 4, 9])

1
Будь ласка, подумайте про додавання пояснень.
souki

@souki Я додав пояснення зараз. Дякую
Дхавал Маятра

6

Останні numpyверсії додали take_along_axis(та put_along_axis), який робить це індексування чисто.

In [101]: a = np.arange(1,10).reshape(3,3)                                                             
In [102]: b = np.array([1,0,2])                                                                        
In [103]: np.take_along_axis(a, b[:,None], axis=1)                                                     
Out[103]: 
array([[2],
       [4],
       [9]])

Він працює так само, як:

In [104]: a[np.arange(3), b]                                                                           
Out[104]: array([2, 4, 9])

але з різною обробкою осей. Він особливо спрямований на застосування результатів argsortта argmax.


3

Ви можете зробити це за допомогою ітератора. Подобається це:

np.fromiter((row[index] for row, index in zip(X, Y)), dtype=int)

Час:

N = 1000
X = np.zeros(shape=(N, N))
Y = np.arange(N)

#@Aशwini चhaudhary
%timeit X[np.arange(len(X)), Y]
10000 loops, best of 3: 30.7 us per loop

#mine
%timeit np.fromiter((row[index] for row, index in zip(X, Y)), dtype=int)
1000 loops, best of 3: 1.15 ms per loop

#mine
%timeit np.diag(X.T[Y])
10 loops, best of 3: 20.8 ms per loop

1
OP згадав, що він повинен працювати швидко на великих масивах, тому ваші показники не надто репрезентативні. Мені цікаво, як працює ваш останній метод для (набагато) більших масивів!

@moarningsun: оновлено. np.diag(X.T[Y])це так повільно ... Але np.diag(X.T)так швидко (10us). Не знаю чому.
Kei Minagawa

0

Інший розумний спосіб - спочатку транспонувати масив і потім індексувати його. Нарешті, візьміть діагональ, це завжди правильна відповідь.

X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
Y = np.array([1, 0, 2, 2])

np.diag(X.T[Y])

Крок за кроком:

Оригінальні масиви:

>>> X
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])

>>> Y
array([1, 0, 2, 2])

Транспонуйте, щоб можна було правильно його проіндексувати.

>>> X.T
array([[ 1,  4,  7, 10],
       [ 2,  5,  8, 11],
       [ 3,  6,  9, 12]])

Отримати рядки в порядку Y.

>>> X.T[Y]
array([[ 2,  5,  8, 11],
       [ 1,  4,  7, 10],
       [ 3,  6,  9, 12],
       [ 3,  6,  9, 12]])

Тепер діагональ повинна стати чіткою.

>>> np.diag(X.T[Y])
array([ 2,  4,  9, 12]

1
Це технічно працює і виглядає дуже елегантно. Однак я вважаю, що такий підхід повністю вибухає, коли ви маєте справу з великими масивами. У моєму випадку NumPy проковтнув 30 Гб обміну і заповнив мій SSD. Натомість я рекомендую використовувати розширений підхід до індексування.
5негідні
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.