user5515662
user5515662

Reputation:

Python: how to transpose part of matrix

If I have a matrix like this:

matrix = [[1, 2, 3, 4], 
          [5, 6, 7, 8], 
          [9, 10, 11, 12],
          [13, 14, 15, 16]]

how can I get this:

matrix = [[1, 2, 3, 4], 
          [5, 6, 10, 14], 
          [9, 7, 11, 15],
          [13, 8, 12, 16]]

That is, how can I exclude the first row and the first column and transpose the rest?

I tried this, it leaves the matrix unchanged:

for i in range(1, 4):
    for j in range(1, 4):
        temp = copy.deepcopy(matrix[i][j])
        matrix[i][j] = matrix[j][i]
        matrix[j][i] = temp

And when I try:

new_matrix = list(matrix)
for i in range(1, 4):
    for j in range(1, 4):
        matrix[i][j] = new_matrix[j][i]
        matrix[j][i] = new_matrix[i][j]

I get this:

[[1, 2, 3, 4], 
[5, 6, 10, 14], 
[9, 10, 11, 15], 
[13, 14, 15, 16]]

I want to transpose it over both the main and the secondary diagonal.

Upvotes: 1

Views: 2277

Answers (5)

You can use

np.vstack([modified_submatrix, not_modified_sumbmatrix])

These can help for your task

Upvotes: 0

Trey Hunner
Trey Hunner

Reputation: 11804

This isn't the most idiomatic way to solve this problem, but I wanted to address why the attempts in the question didn't work and how to transpose in-place as you were originally trying.

There are a couple problems with this:

new_matrix = list(matrix)
for i in range(1, 4):
    for j in range(1, 4):
        matrix[i][j] = new_matrix[j][i]
        matrix[j][i] = new_matrix[i][j]

First you're assigning matrix[i][j] to matrix[j][i] and then assigning matrix[j][i] back to matrix[i][j], which means both will have the same number at the end.

If you did this, you'd swap them instead:

for i in range(1, 4):
    for j in range(1, 4):
        matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

This doesn't work either though. It doesn't work because we're swapping each cell twice (except for the diagonals which don't matter anyway).

We could make sure we only swap once by only going to the diagonals. One way to do that would be to only swap if i is less than j (or the other way around):

for i in range(1, 4):
    for j in range(1, 4):
        if i < j:
            matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

This should also give you the answer you're looking for (if swapping the indexes in-place is acceptable).

Upvotes: 0

9000
9000

Reputation: 40884

When in doubt, write a function.

Ever wondered why Python lacks a built-in function for transposing matrices? It's because zip does it already. But zip returns a sequence of tuples, and you need lists, so let's wrap it up.

def transpose(matrix):
    return [list(row) for row in zip(*matrix)]

We need to transpose a sub-matrix. How do we extract it?

def submatrix(matrix, skip=1):
    return [row[skip:] for row in matrix[skip:]]

It turns out we'll need a function to paste contents of one list over the contents of another, bigger list. The function below assumes that the smaller overlay list never gets past the bounds of the bigger base list, for the sake of simplicity.

def pasteOver(base, overlay, offset):
    """Paste overlay list over base list, starting at offset.

    Works even when overlay is not touching either end of base."""
    return base[:offset] + overlay + base[(len(overlay)) + offset:]

This picture hopefully helps understand why the indexes in the above code are what they are. The blue part is the overlay, the longer colored part is base; the part which will be overlaid is grayed out.

pic

The red part does not occur in your problem, but it's added to make the code a more general solution; it's the base[(len(overlay)) + offset:] part.

I hope you know how Python's list slicing works.

Now we can paste an entire matrix over another, bigger matrix, by pasting changed rows over it, and pasting a submatrix row over any changed row. Note how the code below is almost a literal translation of the previous sentence.

def graft(matrix, submatrix, offset):
    """Overlays submatrix on matrix at given offset from top/left."""
    changing_rows = matrix[offset : offset + len(submatrix)]
    changed_rows = [pasteOver(row, sub_row, offset)
                    for (row, sub_row) in zip(changing_rows, submatrix)]
    return pasteOver(matrix, changed_rows, offset)

Now it's easy to combine things to paste a transposed submatrix:

def subTranspose(matrix, skip=1):
    sub_transposed = transpose(submatrix(matrix, skip))
    return graft(matrix, sub_transposed, skip)

Note how every function is almost comically short and simple, so that "its correctness is painfully obvious" (q). Also note how every function is pure, it never changes the data passed to it or has any other side effects. This code, were it in a real code base, would be easier to read, test, reuse, and maintain.

(But if your matrices are really big, use numpy anyway.)

Upvotes: 2

karakfa
karakfa

Reputation: 67467

Learn numpy, even for this simple task it's worth.

import numpy as np

m=np.array(range(1,17)).reshape(4,4)    
m[1:,1:]=m[1:,1:].transpose()                                                                                     

array([[ 1,  2,  3,  4],
       [ 5,  6, 10, 14],
       [ 9,  7, 11, 15],
       [13,  8, 12, 16]])

Upvotes: 5

gamda
gamda

Reputation: 580

from math import ceil
# get rid of first row and first element in each row
m2 = [x[1:] for x in matrix[1:]]
for i in range(ceil(len(m2)/2)): # just top half of the rows
    for j in range(i, len(m2[i])): # just above the diagonal
        m2[i][j], m2[j][i] = m2[j][i], m2[i][j]
# create list with first element in matrix and rest from m2
m3 = []
for i in range(len(m1)):
    row = [matrix[i][0]]
    row.extend(m2[i]) 
    m3.append(line)
# now all m3 is missing is the first row 
m3.insert(0, matrix[0])

Since you hardcoded the length in the loops you posted, I assume the size will always be 4x4. This code will only work with square matrices.

Upvotes: 0

Related Questions