web-dev-qa-db-fra.com

Numpy: moyen efficace de convertir les indices d'une matrice carrée en ses indices triangulaires supérieurs

Question: étant donné un nuplet d’index, retourne son ordre dans les index triangulaires supérieurs. Voici un exemple:

Supposons que nous ayons une matrice carrée A de forme (3, 3).

A a 6 indices triangulaires supérieurs, à savoir, (0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2).

Maintenant, je connais un élément à l'index (1, 2), qui est un index qui appartient à la partie triangulaire supérieure de A. Je voudrais renvoyer 4 (ce qui signifie que c'est le 5ème élément de tous les indices triangulaires supérieurs.)

Des idées sur la façon de faire cela en général?

Meilleur, Zhihao 

7
Zhihao Cui

On peut écrire la formule explicite:

def utr_idx(N, i, j):
    return (2*N+1-i)*i//2 + j-i

Démo:

>>> N = 127
>>> X = np.transpose(np.triu_indices(N))
>>> utr_idx(N, *X[2123])
2123
5
Paul Panzer

Pour une matrice n × n , le (i, j) - ème élément du triangle supérieur est le i × (2 × n-i + 1)/2 + ji - ème élément de la matrice.

Nous pouvons également faire le calcul à l’inverse et obtenir l’élément (i, j) pour le k - ème élément avec:

i = ⌊ (-√ ((2n + 1)2-8k) + 2n + 1)/2⌋ et j = k + i-i × (2 × n-i + 1)/2

Donc par exemple:

from math import floor, sqrt

def coor_to_idx(n, i, j):
    return i*(2*n-i+1)//2+j-i

def idx_to_coor(n, k):
    i = floor((-sqrt((2*n+1)*(2*n+1)-8*k)+2*n+1)/2)
    j = k + i - i*(2*n-i+1)//2
    return i, j

Par exemple:

>>> [idx_to_coor(4, i) for i in range(10)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]
>>> [coor_to_idx(4, i, j) for i in range(4) for j in range(i, 4)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Etant donné que les nombres ne sont pas énormes (enfin si ceux-ci sont énormes, les calculs ne sont plus effectués en temps constant), nous pouvons donc calculer la coordonnée k - de dans O(1) , par exemple:

>>> idx_to_coor(1234567, 123456789)
(100, 5139)

ce qui équivaut à l'obtenir par énumération:

>>> next(islice(((i, j) for i in range(1234567) for j in range(i, 1234567)), 123456789, None))
(100, 5139)

Dans ce cas, la conversion d'index en une coordonnée peut également comporter, pour les grands nombres, des erreurs d'arrondi dues à une imprécision en virgule flottante.

4
Willem Van Onsem

IIUC, vous pouvez obtenir les index en utilisant itertools combinaisons avec remplacement

>>> ind = Tuple(itertools.combinations_with_replacement(range(3),2))
((0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2))

Pour récupérer l'index, utilisez simplement index method 

>>> ind.index((1,2))
4
2
RafaelC

Vous pouvez utiliser np.triu_indices et un dictionary :

import numpy as np

iu1 = np.triu_indices(3)
table = {(i, j): c for c, (i, j) in enumerate(Zip(*iu1))}
print(table[(1, 2)])

Sortie

4
2
Daniel Mesejo

Construire des indices supérieurs serait coûteux. Nous pouvons directement obtenir l'index correspondant comme ceci -

def triu_index(N, x, y):
    # Get index corresponding to (x,y) in upper triangular list
    idx = np.r_[0,np.arange(N,1,-1).cumsum()]
    return idx[x]+y-x

Exemple de cycle -

In [271]: triu_index(N=3, x=1, y=2)
Out[271]: 4
1
Divakar

Semblable à @DanielMesejo, vous pouvez utiliser np.triu_indices avec argwhere ou nonzero:

my_index = (1,2)

>>> np.nonzero((np.stack(np.triu_indices(3), axis=1) == my_index).all(1))
(array([4]),)
>>> np.argwhere((np.stack(np.triu_indices(3), axis=1) == my_index).all(1))
array([[4]])

Explication:

np.stack(np.triu_indices(3), axis=1) vous donne les indices de votre triangle supérieur dans l'ordre:

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

Donc, tout ce que vous avez à faire est de trouver où il correspond [1,2] (ce que vous pouvez faire avec l'opérateur == et all)

1
sacuL