web-dev-qa-db-fra.com

Génération de matrices symétriques dans Numpy

J'essaie de générer des matrices symétriques en numpy. Plus précisément, ces matrices doivent avoir des entrées d'emplacements aléatoires, et dans chaque entrée, le contenu peut être aléatoire. Le long de la diagonale principale, nous ne nous soucions pas des entrées, alors je les ai également randomisées.

L'approche que j'ai adoptée consiste à générer d'abord une matrice nxn entièrement nulle et à boucler simplement sur les indices des matrices. Cependant, étant donné que le bouclage est relativement cher en python, je me demande si je peux obtenir la même chose sans utiliser les boucles for de python.

Y a-t-il des éléments intégrés à numpy qui me permettent d'atteindre mon objectif plus efficacement?

Voici mon code actuel:

import numpy as np
import random

def empty(x, y):
    return x*0

b = np.fromfunction(empty, (n, n), dtype = int)

for i in range(0, n):
    for j in range(0, n):
        if i == j:
            b[i][j] = random.randrange(-2000, 2000)
        else:
            switch = random.random()
            random.seed()
            if switch > random.random():
                a = random.randrange(-2000, 2000)
                b[i][j] = a
                b[j][i] = a
            else:
                b[i][j] = 0
                b[j][i] = 0
29
Ryan

Vous pouvez simplement faire quelque chose comme:

import numpy as np

N = 100
b = np.random.random_integers(-2000,2000,size=(N,N))
b_symm = (b + b.T)/2

Où vous pouvez choisir la distribution de votre choix dans le np.random ou module scipy équivalent.

Mise à jour: Si vous essayez de construire des structures de type graphique, consultez certainement le package networkx:

http://networkx.lanl.gov

qui a un certain nombre de routines intégrées pour construire des graphiques:

http://networkx.lanl.gov/reference/generators.html

De plus, si vous souhaitez ajouter un certain nombre de zéros placés au hasard, vous pouvez toujours générer un ensemble d'indices aléatoires et remplacer les valeurs par zéro.

37
JoshAdel

Je ferais mieux de:

a = np.random.Rand(N, N)
m = np.tril(a) + np.tril(a, -1).T

car dans ce cas tous les éléments d'une matrice sont de même distribution (uniforme dans ce cas).

24
Ben Usman

J'utilise la fonction suivante pour créer une matrice symétrique verticalement et horizontalement:

def make_sym(a):
    w, h = a.shape
    a[w - w // 2 :, :] = np.flipud(a[:w // 2, :])
    a[:, h - h // 2:] = np.fliplr(a[:, :h // 2])

Voyons comment cela fonctionne:

>>> m = (np.random.Rand(10, 10) * 10).astype(np.int)
>>> make_sym(m)
>>> m
array([[2, 7, 5, 7, 7, 7, 7, 5, 7, 2],
       [6, 3, 9, 3, 6, 6, 3, 9, 3, 6],
       [1, 4, 6, 7, 2, 2, 7, 6, 4, 1],
       [9, 2, 7, 0, 8, 8, 0, 7, 2, 9],
       [5, 5, 6, 1, 9, 9, 1, 6, 5, 5],
       [5, 5, 6, 1, 9, 9, 1, 6, 5, 5],
       [9, 2, 7, 0, 8, 8, 0, 7, 2, 9],
       [1, 4, 6, 7, 2, 2, 7, 6, 4, 1],
       [6, 3, 9, 3, 6, 6, 3, 9, 3, 6],
       [2, 7, 5, 7, 7, 7, 7, 5, 7, 2]])
0
a5kin

Il y a ici une réponse élégante qui produit une matrice où toutes les entrées suivent la même distribution. Cependant, cette réponse rejette (n-1)*n/2 nombres aléatoires sans les utiliser.

Si vous souhaitez que toutes les valeurs suivent la même distribution, les générer toutes en même temps et générer uniquement celles que vous allez utiliser, vous pouvez exécuter ce qui suit:

>>> import numpy as np
>>> n = 5
>>> r = np.random.Rand(n*(n+1)//2)
>>> sym = np.zeros((n,n))
>>> for i in range(n):
...     t = i*(i+1)//2
...     sym[i,0:i+1] = r[t:t+i+1]
...     sym[0:i,i] = r[t:t+i]
... 
>>> print(sym)
[[0.03019945 0.30679756 0.85722724 0.78498237 0.56146757]
 [0.30679756 0.46276869 0.45104513 0.28677046 0.10779794]
 [0.85722724 0.45104513 0.62193894 0.86898652 0.11543257]
 [0.78498237 0.28677046 0.86898652 0.13929717 0.45309959]
 [0.56146757 0.10779794 0.11543257 0.45309959 0.5671571 ]]

L'idée ici est de suivre les nombres triangulaires pour savoir combien d'éléments du vecteur aléatoire ont déjà été utilisés précédemment. Compte tenu de cette valeur t, remplissez la ligne actuelle jusqu'à et y compris la diagonale et la colonne actuelle jusqu'à (mais n'inclut pas) la diagonale.

0
Fábio Reale

Si cela ne vous dérange pas d'avoir des zéros sur la diagonale, vous pouvez utiliser l'extrait de code suivant:

def random_symmetric_matrix(n):
    _R = np.random.uniform(-1,1,n*(n-1)/2)
    P = np.zeros((n,n))
    P[np.triu_indices(n, 1)] = _R
    P[np.tril_indices(n, -1)] = P.T[np.tril_indices(n, -1)]
    return P

Notez que vous n'avez besoin de générer que n * (n-1)/2 variables aléatoires en raison de la symétrie.

0
Aetienne Sardon