user3089880
user3089880

Reputation: 53

How can I apply a matrix transform to each row of a NumPy array efficiently?

Let's say I have a 2d NumPy ndarray, like so:

[[ 0, 1, 2, 3 ],
 [ 4, 5, 6, 7 ],
 [ 8, 9, 10, 11 ]]

Conceptually speaking, what I want to do is this:

For each row:
    Transpose the row
    Multiply the transposed row by a transformation matrix
    Transpose the result
    Store the result in the original ndarray, overwriting the original row data

I have an extremely slow, brute-force method which functionally achieves this:

import numpy as np
transform_matrix = np.matrix( /* 4x4 matrix setup clipped for brevity */ )
for i, row in enumerate( data ):
    tr = row.reshape( ( 4, 1 ) )
    new_row = np.dot( transform_matrix, tr )
    data[i] = new_row.reshape( ( 1, 4 ) )

However, this seems like the sort of operation that NumPy should do well with. I assume that - as someone new to NumPy - I'm just missing something fundamental in the documentation. Any pointers?

Note that if it's faster to create a new ndarray rather than edit it in-place, that can work for what I'm doing, too; speed of the operation is the primary concern.

Upvotes: 5

Views: 20326

Answers (2)

user2357112
user2357112

Reputation: 280291

The lengthy series of operations you want to perform is equivalent to the following:

data[:] = data.dot(transform_matrix.T)

or using a new array instead of modifying the original, which should be a bit faster:

data.dot(transform_matrix.T)

Here's the explanation:

For each row:
    Transpose the row

Equivalent to transposing the matrix and then going over the columns.

    Multiply the transposed row by a transformation matrix

Left-multiplying each column of a matrix by a second matrix is equivalent to left-multiplying the whole thing by the second matrix. At this point, what you have is transform_matrix.dot(data.T)

    Transpose the result

One of the basic properties of matrix transposes is that transform_matrix.dot(data.T).T is equivalent to data.dot(transform_matrix.T).

    Store the result in the original ndarray, overwriting the original row data

The slice assignment does this.

Upvotes: 11

alko
alko

Reputation: 48307

It seems you need transpose operator:

>>> np.random.seed(11)
>>> transform_matrix = np.random.randint(1, 10, (4,4))
>>> np.dot(transform_matrix, data.T).T
matrix([[ 24,  24,  17,  37],
        [ 76, 108,  61, 137],
        [128, 192, 105, 237]])

or equivalently, as (A*B).T = (B.T * A.T):

>>> np.dot(data, transform_matrix.T)

Upvotes: 4

Related Questions