views:

1064

answers:

6

I want to apply a function to all columns in a matrix with MATLAB. For example, I'd like to be able to call smooth on every column of a matrix, instead of having smooth treat the matrix as a vector (which is the default behaviour if you call smooth(matrix)).

I'm sure there must be a more idiomatic way to do this, but I can't find it, so I've defined a map_column function:

function result= map_column(m, func)
    result= m;
    for col= 1:size(m,2)
        result(:,col)= func(m(:,col));
    end
end

which I can call with:

smoothed= map_column(input, @(c) (smooth(c, 9)));

Is there anything wrong with this code? How could I improve it?

A: 

If this is a common use-case for your function, it would perhaps be a good idea to make the function iterate through the columns automatically if the input is not a vector.

This doesn't exactly solve your problem but it would simplify the functions' usage. In that case, the output should be a matrix, too.

You can also transform the matrix to one long column by using m(:,:) = m(:). However, it depends on your function if this would make sense.

BastiBechtold
+1  A: 

Maybe you could always transform the matrix with the ' operator and then transform the result back.

smoothed = smooth(input', 9)';

That at least works with the fft function.

Hallgrim
This does not work for the smooth function. If you pass smooth a matrix, it treats it as one large vector.This method would be convenient for some other functions though.
dmnd
+4  A: 

The MATLAB "for" statement actually loops over the columns of whatever's supplied - normally, this just results in a sequence of scalars since the vector passed into for (as in your example above) is a row vector. This means that you can rewrite the above code like this:

function result = map_column(m, func)
    result = [];
    for m_col = m
      result = horzcat(result, func(m_col));
    end

If func does not return a column vector, then you can add something like

f = func(m_col);
result = horzcat(result, f(:));

to force it into a column.

Tim Whitcomb
+2  A: 

Your solution is fine.

Note that horizcat exacts a substantial performance penalty for large matrices. It makes the code be O(N^2) instead of O(N). For a 100x10,000 matrix, your implementation takes 2.6s on my machine, the horizcat one takes 64.5s. For a 100x5000 matrix, the horizcat implementation takes 15.7s.

If you wanted, you could generalize your function a little and make it be able to iterate over the final dimension or even over arbitrary dimensions (not just columns).

Mr Fooz
+1  A: 

Don't forget to preallocate the result matrix if you are dealing with large matrices. Otherwise your CPU will spend lots of cycles repeatedly re-allocating the matrix every time it adds a new row/column.

Jason S
+1  A: 

A way to cause an implicit loop across the columns of a matrix is to use cellfun. That is, you must first convert the matrix to a cell array, each cell will hold one column. Then call cellfun. For example:

A = randn(10,5);

See that here I've computed the standard deviation for each column.

cellfun(@std,mat2cell(A,size(A,1),ones(1,size(A,2))))

ans = 0.78681 1.1473 0.89789 0.66635 1.3482

Of course, many functions in MATLAB are already set up to work on rows or columns of an array as the user indicates. This is true of std of course, but this is a convenient way to test that cellfun worked successfully.

std(A,[],1)

ans = 0.78681 1.1473 0.89789 0.66635 1.3482

woodchips

related questions