matrix staggering in python

3

I have problems when trying to stagger a 5x5 matrix. First I made a null line go to the last row of the matrix (it worked), then I tried to make a row with the highest index fall below the one with the lowest index, but on the line:

if pivos_indices[i] > pivos_indices[linha_aux] and linha_aux < 5 and i < 5:

of the code, the compiler warns that the index of the list is out of range, but I do not know why (that's the problem). Then follow the code:

import numpy as np
def buscar_pivo(L):
    if (np.nonzero(L)[0]).size == 0:
        return -1
    else:
        return np.nonzero(L)[1][0]

def encontrar_pivos_indices(mat):
    pivos = []
    for i in range(5):
        pivos.append(buscar_pivo(np.array(mat[i])))
    return pivos

mat = np.matrix([[0,5,2,7,8],[8,10,4,14,16],[0,0,0,0,0],[2,6,10,16,22],[3,5,8,9,15]]).astype(float)
print("Matriz original:\n",mat,"\n")

pivos_indices = encontrar_pivos_indices(mat)

linha_aux = 0
for i in range(5):
    linha_aux = linha_aux + 1
    if pivos_indices[i] == -1 and linha_aux < 5 and i < 5:
        m = mat.tolist()
        (m[i],m[linha_aux]) = (m[linha_aux],m[i])
        mat = np.matrix(m)
        pivos_indices = encontrar_pivos_indices(mat)

print(mat,"\n")

linha_aux = 0
for i in range(5): 
    linha_aux = linha_aux + 1
    if pivos_indices[i] > pivos_indices[linha_aux] and linha_aux < 5 and i < 5:
        m = mat.tolist()
        (m[i],m[linha_aux]) = (m[linha_aux],m[i])
        mat = np.matrix(m)
        pivos_indices = encontrar_pivos_indices(mat)

print(mat)
    
asked by Andrew Jhonatan 14.10.2017 в 20:05
source

1 answer

2

The problem is in:

for i in range(5): 
    linha_aux = linha_aux + 1
    if pivos_indices[i] > pivos_indices[linha_aux] and linha_aux < 5 and i < 5:

You have an array of 5 elements, the maximum value that reaches i in your for is 4, but for i = 4 linha_aux is 5 and pivos_indices[5] is not valid because that index is out of range for an array of 5 elements (where the maximum index is 4) . and linha_aux < 5 does not serve as protection because it is not evaluated, since indexing occurs before it is evaluated.

The logical thing to do is to make the for iterate up to len(array) - 1 , in this case for i in range(4) .

There is a simpler way to sort the array according to the number of initial consecutive zeros. Using your idea of using np.nonzero we can use the resulting array with the pivot index. Then we order it and get the indexes of the ordered elements ( numpy.argsort ) and finally we indexed mat with it:

import numpy as np

def buscar_pivo(L):
    p = np.nonzero(L)[1]
    if p.size:
        return p[0]
    return L.size

mat = np.matrix([[0,  5,  2,  7,  8],
                 [0,  0,  0, 14, 16],
                 [0,  0,  0,  0,  0],
                 [0,  0, 10, 16, 22],
                 [0,  0,  0,  0,  8]]).astype(float)

res = mat[np.argsort(np.apply_along_axis(buscar_pivo, axis=1, arr=mat))]
print(res)

Exit:

[[  0.   5.   2.   7.   8.]
 [  0.   0.  10.  16.  22.]
 [  0.   0.   0.  14.  16.]
 [  0.   0.   0.   0.   8.]
 [  0.   0.   0.   0.   0.]]

For your original example we get:

>>> mat = np.matrix([[0,  5,  2,  7,  8],
                     [8, 10,  4, 14, 16],
                     [0,  0,  0,  0,  0],
                     [2,  6, 10, 16, 22],
                     [3,  5,  8,  9, 15]]).astype(float)
>>> res = mat[np.argsort(np.apply_along_axis(buscar_pivo, axis=1, arr=mat))]
>>> res  
[[  8.  10.   4.  14.  16.]
 [  2.   6.  10.  16.  22.]
 [  3.   5.   8.   9.  15.]
 [  0.   5.   2.   7.   8.]
 [  0.   0.   0.   0.   0.]]

Edit:

  • The function buscar_pivo has been modified to adapt to our needs. Previously -1 was returned if the row is null (all elements are 0). The problem is that when ordering the indexes of the pivots these rows would be the first, when they should be the last. For this reason, instead of returning -1 the size of the row is returned. For a 5 x 5 matrix, return 5, since the maximum index is 4, these lines will always be ordered at the end.

  • np.apply_along_axis(buscar_pivo, axis=1, arr=mat) iterates over the rows of the matrix mat creating an array with the outputs resulting from applying the function buscar_pivo to each row. That is, call buscar_pivo for each row, passing that row as an argument to the function and create a new array with the results. The argument axis indicates on which axis of the matrix we are going to apply the function. In a 2d matrix the 0 axis is the x axis (columns) and the 1 axis is the y axis (rows). In this case we use the axis 1 since what we want to pass to buscar_pivo are the rows. If we have a 3d array we can iterate on the z axis with axis = 3 , and so on. Let's see it with an example:

    >>> import numpy as np
    >>> array = np.array[[1,  2],
                         [5,  9],
                         [-4, 3]]
    
    # Funcción estúpida que retorna la suma de los elementos de un array:
    
    >>> def suma(arr):
            return np.sum(arr)
    
    # Suma de los elementos de cada columna:
    >>> s = np.apply_along_axis(suma, axis=0, arr=array)
    >>> s
    array([ 2, 14])
    
    # Suma de los elementods de cada fila:
    >>> s = np.apply_along_axis(suma, axis=1, arr=array)
    >>> s
    array([ 3, 14, -1])
    
  • np.argsort instead of returning the ordered array returns the indexes of those ordered elements.

    >>> import numpy as np
    >>> array = np.array([4,  2,  1, 5])
    >>> s = array.argsort() 
    >>> s
    array([2, 1, 0, 3]) #Se corresponden con los indices de 1, 2, 4 y 5
    
  • Finally we can index an array by passing it an index array:

    >>> import numpy as np
    >>> array = np.array([4,  2,  1, 5])
    >>> s = array[[3, 0, 1, 2]]
    >>> s
    array([5, 4, 2, 1])
    
answered by 15.10.2017 / 03:43
source