Monday, August 4, 2008

Matrix Transposition

As part of my heat map project, I needed to transpose a matrix. I know I could use SciPy or hand-code a method, but I remembered an IAQ (infrequently asked question) from Peter Norvig where he gives this example:

m = [(1,2,3), (4,5,6)]
zip(*m)
# [(1, 4), (2, 5), (3, 6)]


How does this work? Well, Norvig says that "f(*m) is like apply(f,m)," and I know that apply(f,m) feeds the elements of m to the function f, where in this case f is zip. If we do zip on two lists:

L = list('abcde')
R = range(len(L))
zip(L,R)
# [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4)]


However, feeding the nested list directly to zip does not do what we want:

nestedL = [L,R]
zip(nestedL)
# [(['a', 'b', 'c', 'd', 'e'],), ([0, 1, 2, 3, 4],)]

But this does:

apply(zip, nestedL)
# [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4)]


So the next question is, how does f(*args) turn into apply(f,args)?
def f(*T):
print type(T),T
f(nestedL)
# < type 'tuple'> ([['a', 'b', 'c', 'd', 'e'], [0, 1, 2, 3, 4]],)

T is a tuple containing nestedL as its first element. Here is where I lose the trail. It makes my head spin. But zip(*m) really is a neat trick to remember.

1 comment:

diffusing thoughts said...

hope this helps you further:

this function accepts any number of arguments:
def rint(*args):
print args

>> rint((1,2,3))
((1, 2, 3),)
it got one argument a tuple (1,2,3)

>> rint(*(1,2,3))
(1, 2, 3)
it got three arguments 1, 2, and 3 the tuple has been unpacked before feeding into rint. An astericks means "unpack".