matlab: addressing of one index without sub2ind - matlab

This is very closely related to this other question, but that question wanted to avoid sub2ind because of performance concerns. I am more concerned about the "unelegance" of using sub2ind.
Let's suppose I want to create another MxN matrix which is all zeros except for one entry in each column that I want to assign from the corresponding entry in a vector, and choice of row in each column is based on another vector. For example:
z = zeros(10,4);
rchoice = [3 1 8 7];
newvals = [123 456 789 10];
% ??? I would like to set z(3,1)=123, z(1,2)=456, z(8,3)=789, z(7,4)=10
I can use sub2ind to accomplish this (which I used in an answer to a closely related question):
z(sub2ind(size(z),rchoice,1:4)) = newvals
but is there another alternative? Seems like logical addressing could be used in some way but I'm stumped, because in order to set the elements of a logical matrix to 1, you're dealing with the same element positions as in the matrix you actually want to address.

There's a much simpler way of doing it.
nCols=size(z,2);
z(rchoice,1:nCols)=diag(newvals);

You can just add the number of rows in previous columns to rchoice to get the linear index directly.
nRows = size(z,1); %# in case you don't know this already
nCols2write = length(newvals);
z(rchoice+[0:nRows:(nRows*(nCols2write-1)]) = newvals;

Related

Matlab: Create a zero matrix with 1 in a particular place

For example, I can create a zeros(100). But I want the entry of row 58 and column 59 to be 1. But I need temporary variable and multiple lines to do this.
a. Let this matrix be M. How can I do this in one line? M = ....?
P.S.
b. Better still, sometimes I want two or more entries of the zero matrix be 1.
Again, how can I do this?
If I can do a. in one-line, of course I can add them up. But is there any special function to do fill zero matrix entries with 1?
First, remember that a one line expression isn't always the most effective. It could also be harder to read/understand.
One way to do this is by using a sparse matrix
The following example creates a 10x10 zero-matrix with ones at [5,2] (row 5, col 2) and [7 5]
full(sparse([5 7],[2 5],1,10,10))
Use full to convert it from a sparse matrix to a "full" one
Another (faster but maybe not as intuitive) alternative is to use accumarray
accumarray([5 2;7 5],1,[10,10])
Remember that the index values above is used directly in the expression to get on one line, the better option would be to create them separately
points = [5 2; 7 5]
or perhaps,
rowIdx = [5 7];
colIdx = [2 5];

find in multidimensional array - MATLAB

I have a 3 by 3 by 2 by 3 array X containing 1s or 0s. Picture this as a row of three 3 by 3 matrices along a row and another such 'layer' behind them (from the '2').
I want to find the positions in X of the 0s in the first layer and second layer separately. I'm not really sure how to do this with find, but heuristically something like:
A = find(X == 0 & 3rd index of X is 1)
B = find(X == 0 & 3rd index of X is 2)
EDIT
I just realised my attempt to simplify my actual question made it misleading. The array X actually has -1's, 1's and -2's and I want to find the -2's. They're not meant to be logical operators. Also I would prefer any operation proposed to be as fast as possible as this will be part of a recursive backtracking algorithm.
solution using logical indexing
I recommend to use logical indexing instead of find.
This gives you all indices where X is 1
value_you_want=-2
C=X==value_you_want;
Now you want only parts of these indices in A and B, first initialize A and B with false of the same size as C:
A=false(size(C));
B=A;
And finally copy the slice you want to each of these matrices:
A(:,:,1,:)=C(:,:,1,:);
B(:,:,2,:)=C(:,:,2,:);
If you really want your numeric indices, use find(A) and find(B)
Alternative solution using linear indices and find
%get all indices
C=find(X==value_you_want)
%convert linear indices to subscript indices, only use third dimension
[~,~,S,~]=ind2sub(size(X),find(X==0));
%Use S to split C
A=C(S==1);
B=C(S==2);
Generally use find(condition) to return linear indices in the array satisfying condition.
A = find(A(:,:,1,:)<1)
B = find(A(:,:,2,:)<1)

Maintaining original order and dimensions of a 3D matrix while using sort

I'm working with a fairly large 3D matrix (32x87x378), and I want to be able to extract every Nth element of a matrix, while keeping them in the same order. Similar to a previous question I asked: Matlab: Extracting Nth element of a matrix, while maintaining the original order of matrix
The method I was given was quite practical (and simple) and works well in most instances. For a random (1x20) matrix, where I wanted every 5th value, beginning with 4 and 5 (so that I am left with a 1x8 matrix (ab) of elements 4,5,9,10,14,15,19,20). It is done as follows:
r = rand(1,20);
n = 5;
ab = r(sort([4:n:numel(r) 5:n:numel(r)]))
My question is, how can this method be used for a 3D matrix r for it's 3rd dimension (or can it?), such as this:
r = rand(2,5,20);
It should be fairly simple, such as this:
n = 5;
ab = r(sort([4:n:numel(r) 5:n:numel(r)],3));
However, this will then give me a 1x80 matrix, as it does not preserve the original dimensions. Is there a way to correct this using the sort function? I'm also open to other suggestions, but I just want to be sure I am not missing anything.
Thanks in advance.
See if this is what you are after -
ab = r(:,:,sort([4:n:size(r,3) 5:n:size(r,3)]))

How to change elements in matrices using MATLAB

Starting wish a 7x4 binary matrix I need to change a random bit in each column to simulate error. Have been trying to no avail.
A very straightforward way to do this is to use a for loop. It might not be the most efficient approach in MATLAB, but it's probably good enough considering your data set is so small.
Iterate through each of the four columns. On each iteration, randomly chose a number from 1 to 7 to represent the row in that column that you have selected to change. Finally, flip the bit at that row/column. The following code does just this. Assume that "A" is a binary matrix with 7 rows and 4 columns
for col=1:4; %// Iterate through each column
row = ceil(7*rand()); %// Randomly chose a number from 1 to 7 to represent row
A(row,col) = ~A(row,col); %// Flip the bit at the specified row/col
end
Another possibility is to create 4 random numbers in one call, and assign in a vectorized fashion:
rowNumbers = randi(4,[1 4])
A(rowNumbers,:) = ~A(rowNumbers,:);

Compact MATLAB matrix indexing notation

I've got an n-by-k sized matrix, containing k numbers per row. I want to use these k numbers as indexes into a k-dimensional matrix. Is there any compact way of doing so in MATLAB or must I use a for loop?
This is what I want to do (in MATLAB pseudo code), but in a more MATLAB-ish way:
for row=1:1:n
finalTable(row) = kDimensionalMatrix(indexmatrix(row, 1),...
indexmatrix(row, 2),...,indexmatrix(row, k))
end
If you want to avoid having to use a for loop, this is probably the cleanest way to do it:
indexCell = num2cell(indexmatrix, 1);
linearIndexMatrix = sub2ind(size(kDimensionalMatrix), indexCell{:});
finalTable = kDimensionalMatrix(linearIndexMatrix);
The first line puts each column of indexmatrix into separate cells of a cell array using num2cell. This allows us to pass all k columns as a comma-separated list into sub2ind, a function that converts subscripted indices (row, column, etc.) into linear indices (each matrix element is numbered from 1 to N, N being the total number of elements in the matrix). The last line uses these linear indices to replace your for loop. A good discussion about matrix indexing (subscript, linear, and logical) can be found here.
Some more food for thought...
The tendency to shy away from for loops in favor of vectorized solutions is something many MATLAB users (myself included) have become accustomed to. However, newer versions of MATLAB handle looping much more efficiently. As discussed in this answer to another SO question, using for loops can sometimes result in faster-running code than you would get with a vectorized solution.
I'm certainly NOT saying you shouldn't try to vectorize your code anymore, only that every problem is unique. Vectorizing will often be more efficient, but not always. For your problem, the execution speed of for loops versus vectorized code will probably depend on how big the values n and k are.
To treat the elements of the vector indexmatrix(row, :) as separate subscripts, you need the elements as a cell array. So, you could do something like this
subsCell = num2cell( indexmatrix( row, : ) );
finalTable( row ) = kDimensionalMatrix( subsCell{:} );
To expand subsCell as a comma-separated-list, unfortunately you do need the two separate lines. However, this code is independent of k.
Convert your sub-indices into linear indices in a hacky way
ksz = size(kDimensionalMatrix);
cksz = cumprod([ 1 ksz(1:end-1)] );
lidx = ( indexmatrix - 1 ) * cksz' + 1; #'
% lindx is now (n)x1 linear indices into kDimensionalMatrix, one index per row of indexmatrix
% access all n values:
selectedValues = kDimensionalMatrix( lindx );
Cheers!