2017-10-06 5 views
2

J'ai couru à travers quelque chose qui m'a semblé incohérent dans les tranches de Numpy. Plus précisément, s'il vous plaît considérer l'exemple suivant:Comportement de découpage apparemment incohérent dans les tableaux numpy

import numpy as np 
a = np.arange(9).reshape(3,3) # a 2d numpy array 
y = np.array([1,2,2])   # vector that will be used to index the array 

b = a[np.arange(len(a)),y]  # a vector (what I want) 
c = a[:,y]      # a matrix ?? 

Je voulais obtenir un vecteur tel que l'élément i-e est a[i,y[i]]. J'ai essayé deux choses (b et c ci-dessus) et j'ai été surpris que b et c ne soient pas les mêmes ... en fait l'un est un vecteur et l'autre est une matrice! J'avais l'impression que : était un raccourci pour "tous les éléments" mais apparemment le sens est un peu plus subtil.

Après essai et erreur, je comprends un peu la différence aujourd'hui (b == np.diag(c)), mais apprécierait des éclaircissements sur pourquoi ils sont différents, exactement ce que l'aide : implique, et comment comprendre quand utiliser les deux cas.

Merci!

+0

Pourquoi pensez-vous que cela soit incohérent? –

+0

Inconsistant parce que dans mon utilisation de Python auparavant, j'ai été entraîné à penser ':' pour signifier "tous les éléments". Quand j'indexe un tableau, j'attends 'myArr [:]' et 'myArr [np.arrage (len (myArr))]' pour obtenir le même résultat. Et c'est le cas, alors j'ai été surpris quand cela n'a pas généralisé à plusieurs dimensions. – Charles

+0

@BradSolomon Je sais qu'ils donnent la même chose, c'est pourquoi je m'attendais à ce que ':' signifie la même chose que 'np.arange (len (a))' :). Ce qu'il fait en 1 dimension, mais pas en deux ou plus – Charles

Répondre

0

Lorsque vous passez

np.arange(len(a)), y 

Vous pouvez voir le résultat comme étant toutes les paires indexées pour les éléments compressés vous avez passé. Dans ce cas, l'indexation par np.arange(len(a)) et y

np.arange(len(a)) 
# [0, 1, 2] 
y 
# [1, 2, 2] 

prend effectivement des éléments: (0, 1), (1, 2), et (2, 2).

print(a[0, 1], a[1, 2], a[2, 2]) # 0th, 1st, 2nd elements from each indexer 
# 1 5 8 

Dans le deuxième cas, vous prenez la coupe entière le long de la première dimension. (Rien avant les deux points.) Donc, ce sont tous les éléments le long de l'axe 0. Vous spécifiez ensuite avec y que vous voulez le 1er, 2ème et 2ème élément le long de chaque ligne. (0-indexé.)

Comme vous l'avez dit, il peut sembler un peu unintuitive que les résultats sont différents, étant donné que les éléments individuels de la tranche sont équivalentes:

a[:] == a[np.arange(len(a))] 

et

a[:y] == a[:y] 

Toutefois, NumPy advanced indexing prend en charge type de la structure de données que vous transmettez lors de l'indexation (tuples, entiers, etc). Les choses peuvent devenir velues très rapidement.

Le détail derrière c'est ceci: d'abord examiner tous indexation NumPy être de la forme générale x[obj], où obj est l'évaluation de ce que vous avez passé. Comment NumPy « se comporte » dépend de quel type d'objet obj est:

indexation avancée est déclenchée lorsque l'objet de sélection, obj, est un objet de séquence non tuple , un ndarray (de type entier de données ou bool) , ou un tuple avec au moins un objet de séquence ou ndarray (de type de données entier ou booléen). ... La définition de l'indexation avancée signifie que x [(1,2,3),] est fondamentalement différent de x [(1,2,3)]. Ce dernier est équivalent à x [1,2,3] qui déclenchera la sélection de base tandis que le premier déclenchera l'indexation avancée. Assurez-vous de comprendre pourquoi cela se produit.

Dans votre premier cas, obj = np.arange(len(a)),y, un uplet qui correspond à la facture en gras ci-dessus. Cela déclenche l'indexation avancée et force le comportement décrit ci-dessus.

En ce qui concerne le second cas, [:,y]

Quand il y a au moins une tranche (:), suspension (...) ou np.newaxis dans l'indice (ou la matrice comporte plus de dimensions que il existe des index avancés), alors le comportement peut être plus compliqué. C'est comme concaténant le résultat d'indexation pour chaque élément d'index avancé.

démontré:

# Concatenate the indexing result for each advanced index element. 
np.vstack((a[0, y], a[1, y], a[2, y])) 
2

Il est difficile de comprendre l'indexation avancée (avec des listes ou des tableaux) sans comprendre la radiodiffusion.

In [487]: a=np.arange(9).reshape(3,3) 
In [488]: idx = np.array([1,2,2]) 

avec un indice (3,) et (3), la production de la forme (3,) Résultat:

In [489]: a[np.arange(3),idx] 
Out[489]: array([1, 5, 8]) 

Indice de (3,1) et (3,), le résultat est (3,3)

In [490]: a[np.arange(3)[:,None],idx] 
Out[490]: 
array([[1, 2, 2], 
     [4, 5, 5], 
     [7, 8, 8]]) 

la tranche : fait essentiellement la même chose. Il y a des différences subtiles, mais ici c'est pareil.

In [491]: a[:,idx] 
Out[491]: 
array([[1, 2, 2], 
     [4, 5, 5], 
     [7, 8, 8]]) 

ix_ fait la même chose, la conversion du (3,) & (3,) à (3,1) et (1,3):

In [492]: np.ix_(np.arange(3),idx) 
Out[492]: 
(array([[0], 
     [1], 
     [2]]), array([[1, 2, 2]])) 

Une somme pourrait aider à visualiser diffusée les deux cas:

In [495]: np.arange(3)*10+idx 
Out[495]: array([ 1, 12, 22]) 
In [496]: np.sum(np.ix_(np.arange(3)*10,idx),axis=0) 
Out[496]: 
array([[ 1, 2, 2], 
     [11, 12, 12], 
     [21, 22, 22]])