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.