tags:

views:

76

answers:

3

I have a variable dimension matrix, X. I want a function that will get the first half of X in one dimension. I.E., I want something like this:

function x = variableSubmatrix(x, d)
    if d == 1
        switch ndims(x)
            case 1
                x = x(1:end/2);
            case 2
                x = x(1:end/2, :);
            case 3
                x = x(1:end/2, :, :);
            (...)
        end
    elseif d == 2
        switch ndims(x)
            case 2
                x = x(:, 1:end/2);
            case 3
                x = x(:, 1:end/2, :);
            (...)
        end
    elseif (...)
    end
end

I'm not quite sure how to do this. I need it to be fast, as this will be used many times in computation.

A: 

You can use s = size(X) to get the dimensions of X, and then try X(1:floor(s(1)),:) to get half of the matrix.

Note that size returns a vector array containing the length in each dimension (e.g., a 2x3 matrix will return a size of [2 3]). As such, you may want to change s(1) to whichever dimension you require.

Example:

a = [1 2 3 4; 5 6 7 8;]

s = size(a);          % s = [2 4]

b = a(1:s(1)/2,:);    % b = [1 2 3 4];
c = a(:,1:s(2)/2);    % c = [1 2; 5 6];
eykanal
+6  A: 

This should do the trick:

function x = variableSubmatrix(x, d)
  index = repmat({':'},1,ndims(x));  %# Create a 1-by-ndims(x) cell array
                                     %#   containing ':' in each cell
  index{d} = 1:size(x,d)/2;          %# Create an index for dimension d
  x = x(index{:});                   %# Index with a comma separated list
end

The above first creates a 1-by-ndims(x) cell array with ':' in each cell. The cell corresponding to dimension d is then replaced with a vector containing the numbers 1 through half the size of dimension d. Then, the contents of the cell array are output as a comma-separated list (using the {:} syntax) and used as the indices into x. This works because ':' and : are treated the same way (i.e. "all elements of this dimension") when used in an indexing statement.

gnovice
Wow, that is great! I didn't know about `':'` either!
rlbond
A: 

For what it's worth, here is the solution in Python (with numpy):

To halve dimension i:

def halve(x,i):
    return x[(slice(None),)*i+(slice(x.shape[i]/2),)]

x = zeros([2,4,6,8])
y = halve(x,2) # dimension 2 was 6
y.shape # (2,4,3,8) # dimension 2 is now 3

If you just want to halve the first dimension, then x[:len(x)/2] is sufficient.

Edit

I got some sceptic comments, on my previous solution, x[:len(x)/2], so here is an example:

x=zeros([4,2,5,6,2,3]) # first dimension is 4
len(x) # 4
x.shape # 4,2,5,6,2,3
x[:len(x)/2].shape # 2,2,5,6,2,3 <- first dimension divided by two
Olivier
Will that really work? The OP wants to halve the matrix *along a specific dimension*. The `end` keyword doesn't represent the total number of matrix elements, it represents the maximum length of the dimension it is indexing.
gnovice
-1, what gnovice said.
mtrw
I insist that it does work. It works because `len(x)` is the length of the tensor on the first dimension, it's *not* the total number of elements.
Olivier
@Olivier - In your updated example, how would you halve the array in the second dimension (i.e., output shape 4,1,5,6,2,3)? What is desired is a way to parameterize the dimension which is to be halved. It's true that the python syntax for halving on the first dimension of a many-dimensioned array is nicer than the Matlab equivalent, but that's not the question.
mtrw
@mtrw my bad, I had missed that. I updated my solution.
Olivier
@Olivier - there's a small syntax error (return return) in your update but otherwise a nice solution.
mtrw
@mtrw hehe, thanks, typo is fixed now.
Olivier

related questions