Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sub matrix of a list of lists (without numpy)

Suppose I have a matrix composed of a list of lists like so:

>>> LoL=[list(range(10)) for i in range(10)]
>>> LoL
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]

Assume, also, that I have a numpy matrix of the same structure called LoLa:

>>> LoLa=np.array(LoL)

Using numpy, I could get a submatrix of this matrix like this:

>>> LoLa[1:4,2:5]
array([[2, 3, 4],
       [2, 3, 4],
       [2, 3, 4]])

I can replicate the numpy matrix slice in pure Python like so:

>>> r=(1,4)
>>> s=(2,5)
>>> [LoL[i][s[0]:s[1]] for i in range(len(LoL))][r[0]:r[1]]
[[2, 3, 4], [2, 3, 4], [2, 3, 4]] 

Which is not the easiest thing in the world to read nor the most efficient :-)

Question: Is there an easier way (in pure Python) to slice an arbitrary matrix as a sub matrix?

like image 998
the wolf Avatar asked Mar 27 '13 01:03

the wolf


3 Answers

In [74]: [row[2:5] for row in LoL[1:4]]
Out[74]: [[2, 3, 4], [2, 3, 4], [2, 3, 4]]

You could also mimic NumPy's syntax by defining a subclass of list:

class LoL(list):
    def __init__(self, *args):
        list.__init__(self, *args)
    def __getitem__(self, item):
        try:
            return list.__getitem__(self, item)
        except TypeError:
            rows, cols = item
            return [row[cols] for row in self[rows]]

lol = LoL([list(range(10)) for i in range(10)])
print(lol[1:4, 2:5])

also yields

[[2, 3, 4], [2, 3, 4], [2, 3, 4]]

Using the LoL subclass won't win any speed tests:

In [85]: %timeit [row[2:5] for row in x[1:4]]
1000000 loops, best of 3: 538 ns per loop
In [82]: %timeit lol[1:4, 2:5]
100000 loops, best of 3: 3.07 us per loop

but speed isn't everything -- sometimes readability is more important.

like image 66
unutbu Avatar answered Nov 04 '22 13:11

unutbu


Do this,

submat = [ [ mat[ i ][ j ] for j in range( index1, index2 ) ] for i in range( index3, index4 ) ]

the submat will be the rectangular (square if index3 == index1 and index2 == index4) chunk of your original big matrix.

like image 43
Edmund Avatar answered Nov 04 '22 13:11

Edmund


For one, you can use slice objects directly, which helps a bit with both the readability and performance:

r = slice(1,4)
s = slice(2,5)
[LoL[i][s] for i in range(len(LoL))[r]]

And if you just iterate over the list-of-lists directly, you can write that as:

[row[s] for row in LoL[r]]
like image 22
Ray Avatar answered Nov 04 '22 14:11

Ray