4

Assuming that I have the following matrix/array:

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

and I want to apply the following permutation:

1 -> 5
2 -> 4

the result should be in the end:

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

Now, an incredibly naive (and memory costly) way of doing so might be:

a2 = deepcopy(a1)
a2[0,:] = a1[4,:]
a2[4,:] = a1[0,:]
a = deepcopy(a2)
a2[:,0] = a[:,4]
a2[:,4] = a[:,0]

a3 = deepcopy(a2)
a2[1,:] = a3[3,:]
a2[3,:] = a3[1,:]
a = deepcopy(a2)
a2[:,1] = a[:,3]
a2[:,3] = a[:,1]

But, I would like to know if there is something more efficient that does this. numpy.shuffle and numpy.permutation seem to permute only the rows of the matrix (not the columns at the same time). That doesn't work for me because the matrices are adjacency matrices (representing graphs), and I need to do the permutations which will give me a graph which is isomorphic with the original graph. Furthermore, I need to do an arbitrary number of permutations (more than one).

Thanks!

TheRevanchist
  • 331
  • 1
  • 4
  • 12

2 Answers2

9

You can perform the swap in a one-liner using integer array indexing:

a = np.array([[0, 0, 1, 1, 1],
              [0, 0, 1, 0, 1],
              [1, 1, 0, 1, 1],
              [1, 0, 1, 0, 0],
              [1, 1, 1, 0, 0]])
b = a.copy()

# map 0 -> 4 and 1 -> 3 (N.B. Python indexing starts at 0 rather than 1)
a[[4, 3, 0, 1]] = a[[0, 1, 4, 3]]

print(repr(a))
# array([[1, 1, 1, 0, 0],
#        [1, 0, 1, 0, 0],
#        [1, 1, 0, 1, 1],
#        [0, 0, 1, 0, 1],
#        [0, 0, 1, 1, 1]])

Note that array indexing always returns a copy rather than a view - there's no way to swap arbitrary rows/columns of an array without generating a copy.


In this particular case you could avoid the copy by using slice indexing, which returns a view rather than a copy:

b = b[::-1] # invert the row order

print(repr(b))
# array([[1, 1, 1, 0, 0],
#        [1, 0, 1, 0, 0],
#        [1, 1, 0, 1, 1],
#        [0, 0, 1, 0, 1],
#        [0, 0, 1, 1, 1]])

Update:

You can use the same indexing approach to swap columns.

c = np.arange(25).reshape(5, 5)
print(repr(c))
# array([[ 0,  1,  2,  3,  4],
#        [ 5,  6,  7,  8,  9],
#        [10, 11, 12, 13, 14],
#        [15, 16, 17, 18, 19],
#        [20, 21, 22, 23, 24]])

c[[0, 4], :] = c[[4, 0], :]     # swap row 0 with row 4...
c[:, [0, 4]] = c[:, [4, 0]]     # ...and column 0 with column 4

print(repr(c))

# array([[24, 21, 22, 23, 20],
#        [ 9,  6,  7,  8,  5],
#        [14, 11, 12, 13, 10],
#        [19, 16, 17, 18, 15],
#        [ 4,  1,  2,  3,  0]])

I've used a different example array in this case - your version will yield an identical output after performing the row/column swaps which makes it difficult to understand what's going on.

ali_m
  • 71,714
  • 23
  • 223
  • 298
  • Thanks for the reply. Hwever, you are changing just the order of rows. What I need is to change both the order of rows and columns (1st row to get swapped with 5th row, 1st column to get swapped with 5th column). I achieved that with the code I posted here - at the moment just below your post - but it looks to me that it is quite expensive (doing a deepcopy everytime I swap rows/columns). – TheRevanchist Dec 23 '15 at 22:35
  • See my update. Your question was rather difficult to understand because your example array yields an identical result after performing the row/column swaps. – ali_m Dec 23 '15 at 22:55
  • This is exactly what I was needing. It gives the same results as the piece of code I posted, but it doesn't need to deepcopy the matrix everytime I do swaps. Thanks! – TheRevanchist Dec 24 '15 at 15:32
  • Note that it will copy the rows/columns that are being swapped, since integer array indexing will always return a copy rather than a view (this is unavoidable, though). As a side note, you can use `a.copy()` rather than `deepcopy` to force a copy of the data in an array. – ali_m Dec 24 '15 at 15:38
0

I found a solution to do what I want (though it is expensive):

a2 = deepcopy(a1)
first = randint(0, 5, 10)
second = randint(0, 5, 10)
for i in range(len(first)):
    a = deepcopy(a2)
    a2[first[i],:] = a[second[i],:]
    a2[second[i],:] = a[first[i],:]
for i in range(len(first)):
    a = deepcopy(a2)
    a2[:,first[i]] = a[:,second[i]]
    a2[:,second[i]] = a[:,first[i]] 

Basically, I am doing 10 random switches. However, I need to copy the matrix many times. Anyway, a2 now represents a graph which is isomorphic with a1.

TheRevanchist
  • 331
  • 1
  • 4
  • 12