Search code examples
pythonarrayspython-3.xmatrixvector

Filter matrix columns containing a canonical vector with Python 3 without using libraries


In a matrix, a canonical column refers to a column that contains a canonical vector. A canonical vector is a vector that has a single element equal to 1 and all other elements equal to 0.

Given this list, leave only the columns that have a canonical vector.

matrix = [['X1', 'X2', 'X3', 'X4', 'X5', 'U1', 'U2', 'B'], [2.0, 1.0, -1.0, 0, 0, 1.0, 0, 8], [1.0, 1.0, 0, 1.0, 0, 0, 0, 2], [1.0, 2.0, 0, 0, -1.0, 0, 1.0, 8]]

That is, this should be the result in this exercise:

matrix = [['X4', 'U1', 'U2'], [0, 1.0, 0], [1.0, 0, 0], [0, 0, 1.0]]

Here is my code but it is failing, I don't know what I should modify anymore:

matrix = [['X1', 'X2', 'X3', 'X4', 'X5', 'U1', 'U2', 'B'], [2.0, 1.0, -1.0, 0, 0, 1.0, 0, 8], [1.0, 1.0, 0, 1.0, 0, 0, 0, 2], [1.0, 2.0, 0, 0, -1.0, 0, 1.0, 8]]

# Get the indices of the columns that contain a canonical vector (value 1.0 in a single position)
columnas_canonicas = [i for i, columna in enumerate(zip(*matrix[1:])) if sum(1 for elemento in columna if elemento == 1.0) == 1]

# We filter the columns that contain a canonical vector
matrix_aux = [[fila[0]] + [fila[i+1] for i in columnas_canonicas] for fila in matrix]

print(matrix_aux) #the output matrix

I've tried this code but it doesn't work, and it throws me this wrong array(matrix) that doesn't contain the columns with the canonical vectors.

[['X1', 'X5', 'U2', 'B'], [2.0, 0, 0, 8], [1.0, 0, 0, 2], [1.0, -1.0, 1.0, 8]]

None of the column vectors obtained with my code are canonical column vectors


Solution

  • def get_canonical_vectors(matrix):
        M = matrix[1:]
        m, n = len(M), len(M[0])
        indices = []
        matrix_aux = [[] for i in range(m+1)]
        for j in range(n):
            column = [M[i][j] for i in range(m)]
            if column.count(1) == 1 and column.count(0) == m - 1:
                indices.append(j)
                matrix_aux[0].append(matrix[0][j])
                for i in range(m):
                    matrix_aux[i+1].append(column[i])
        return indices, matrix_aux
    
    matrix = [['X1', 'X2', 'X3', 'X4', 'X5', 'U1', 'U2', 'B'], [2.0, 1.0, -1.0, 0, 0, 1.0, 0, 8], [1.0, 1.0, 0, 1.0, 0, 0, 0, 2], [1.0, 2.0, 0, 0, -1.0, 0, 1.0, 8]]
    indices, matrix_aux = get_canonical_vectors(matrix)
    print(indices)
    print(matrix_aux)
    

    prints

    [3, 5, 6]
    [['X4', 'U1', 'U2'], [0, 1.0, 0], [1.0, 0, 0], [0, 0, 1.0]]
    

    Edit: actually, you can write this much shorter, close to what you did, by replacing

    matrix_aux = [[fila[0]] + [fila[i+1] for i in columnas_canonicas] for fila in matrix]
    

    with

    matrix_aux = list(map(list, zip(*[c for i, c in enumerate(zip(*matrix)) if i in columnas_canonicas])))
    

    However: this would also require updating the way the indices are retrieved. Currently, with

    columnas_canonicas = [i for i, columna in enumerate(zip(*matrix[1:])) if sum(1 for elemento in columna if elemento == 1.0) == 1]
    

    you could get false positives, e.g. a column [-2, 1, 2] would incorrectly get identified as a canonical column. You could use this instead to avoid this issue:

    columnas_canonicas = [i for i, columna in enumerate(zip(*matrix[1:])) if columna.count(1) == 1 and columna.count(0) == len(columna) - 1]