4

I have a matrix a with an unknown number of dimensions.

I want to access it like a(:,:,:,:,:, ... ,:,1). In other words, setting only the last dimension.

How to do that without doing a lot of math and considering a as a 1 dimensional array. I know this solution works but it is a mess and very hard to implement when each dimension has a different size (and you don't even know the number of dimensions)

hsgubert
  • 2,266
  • 1
  • 22
  • 23
  • When you say "access it", do you want to extract that part of the array, like `b = a(:,:, ..., 1)`, or do you want to set it like `a(:,:, ..., 1) = b`, or something else? – Andrew Janke Apr 20 '15 at 00:43
  • Is there a difference between reading and setting? In this case I want to be able to do both. – hsgubert Apr 21 '15 at 04:01
  • 1
    Yeah. Basically, to assign, you need an "lvalue", which means a) whatever technique you use has to index directly in to the original array (or compensate with reshaping and reassignment), and b) if you wrap it in a function, the assignment has to happen inside the function, instead of it just returning indexes. I've updated my answer on the other question with a variant of the `slice` function that does assignment. – Andrew Janke Apr 21 '15 at 04:30

3 Answers3

4
% get number of dimensions of a
d = ndims(a)

% create cell array with indexes for each dimension
indexes = repmat({':'}, 1, d-1)
indexes{end+1} = 1

% access matrix
a(indexes{:})
hsgubert
  • 2,266
  • 1
  • 22
  • 23
  • 1
    +1. This is a common enough operation that I like to package this up in a `slice()` function. There's a longer discussion of this over here, on what's essentially the same question. http://stackoverflow.com/questions/22537326/on-shape-agnostic-slicing-of-ndarrays – Andrew Janke Apr 20 '15 at 00:35
  • Although the question is the same I just think the other question is not well formulated. I searched for this for half an hour before I posted my question and could't find this link you posted. – hsgubert Apr 21 '15 at 04:06
  • 1
    Good point; it would be hard to locate it if you didn't already know that specific, non-obvious terminology. It's worth having both live and searchable. I'm removing my "duplicate" vote from this question. – Andrew Janke Apr 21 '15 at 04:14
0

I have a different view than yours. You said:

I know this (considering a as 1-D array) solution works but it is a mess and very hard to implement when each dimension has a different size (and you don't even know the number of dimensions)

I think otherwise. Let me show you.

MATLAB is column-major i.e. if you tell MATLAB to collapse a n-D matrix into a 1-D vector, it would do the following steps:

  1. Take the first dimension.
  2. Take the first column, append second column to it, then third column etc. This way, it collapses the first dimension.
  3. Then take the next dimension, repeat the second step and append the obtained vector to the current vector. (Till you finish up all the dimensions).

Take this for example:

a=randi(12,2,2,2,2)

a(:,:,1,1) =

 7     8
10     5


a(:,:,2,1) =

 4     6
 6     5


a(:,:,1,2) =

 7     6
 9     6


a(:,:,2,2) =

 2     4
 1     4

Collpase the matrix a.

b=a(:)

b =

 7
10
 8
 5
 4
 6
 6
 5
 7
 9
 6
 6
 2
 1
 4
 4

Now once you understand the collapsing procedure, it is straightforward to build a general formula. Lets say you want to access the outermost dimension 1 (exactly as in the question).

dimToAccess=1;
sz=size(a);
c=b(prod(sz(1:end-1))*(dimToAccess-1)+1:prod(sz(1:end-1))*(dimToAccess));

Lets test it.

a=randi(12,2,2,2,2)

a(:,:,1,1) =

 7     8
10     5


a(:,:,2,1) =

 4     6
 6     5


a(:,:,1,2) =

 7     6
 9     6


a(:,:,2,2) =

 2     4
 1     4

b=a(:);
dimToAccess=1;
sz=size(a);
c=b(prod(sz(1:end-1))*(dimToAccess-1)+1:prod(sz(1:end-1))*(dimToAccess));

%Test - It should produce 1 as output.
isequal(reshape(c,[size(a,1) size(a,2) size(a,3)]),a(:,:,:,dimToAccess))

Answer seems long but its really of 4 lines.

Autonomous
  • 8,935
  • 1
  • 38
  • 77
  • 1
    It's good to know these indexing transformations, but OP's solution using `{':'}` might be better in practice. It generalizes easily to other dimensions and forms of indexes, and is probably more readable. I found this a little confusing because `dimToAccess` isn't the access of the dimension you're working on (that's always `ndims(a)`); it's the index along that dimension. – Andrew Janke Apr 20 '15 at 00:53
  • 1
    Also, the `b=a(:)` step is unnecessary. You can just index directly in to `a` using a single "linear" index, and you get the same results. – Andrew Janke Apr 20 '15 at 00:56
  • 1
    @AndrewJanke Yes. Even I prefer hsgubert's solution (which you suggested in your eariler post). I just wanted to tell the OP that matrix indexing is not so confusing and messy. – Autonomous Apr 20 '15 at 00:58
  • You code works, but it assumes the matrix has a certain number of dimensions. Perhaps if the code is well packaged in a function it could be usable. – hsgubert Apr 21 '15 at 04:04
0

Just shift the dimensions moving the last to the first.

x = shiftdim(x,ndims(x)-1)

You can then just work on the first elements. As Matlab organises the data column major, you don't need to worry about all of the trailing :. Just index the first column by indexing the first elements.

x(1:size(x,1)) = whatever

and shift back at the end if you like with shiftdim.

learnvst
  • 15,455
  • 16
  • 74
  • 121
  • How would you generalize this formula for the case where the last dimension's index value was something other than `1`, like `3` or `[2 4 6]`? – Andrew Janke Apr 20 '15 at 00:46