Matlab: multiple Assignment for vectors without Loop - matlab

is there any possibility to assign multiple values for a matrix from an another vector without a loop?
For example:
I have a matrix filled with zeros:
matrix=zeros(2);
matrix =
0 0
0 0
Now i have an another vector where the first two columns are the positions and the third column are the values wich belongs to the corresponding positions.
values=[2 1 4;1 2 2]
values =
Posx PosY Value
2 1 4
1 2 2
The result should look like:
matrix =
0 2 <-- matrix(values(2,1),values(2,2))=values(2,3) ;
4 0 <-- matrix(values(1,1),values(1,2))=values(1,3);

This isn't pretty, but it is a one liner:
matrix(size(matrix,1) * (values(:,2) - 1) + values(:,1)) = values(:,3)
I can make it a bit clearer by splitting it into two lines. The idea is that you transform the first two columns of values into a one dimensional indexing vector which has as many elements as there are values to be assigned, and then assign values:
index = size(matrix,1) * (values(:,2) - 1) + values(:,1)
matrix(index) = values(:,3)
When you index into a matrix with a vector it counts down the columns first, and then across the rows. To make it even more clear, split the first statement up some more:
numRows = size(matrix,1)
rowIndex = values(:,1)
colIndex = values(:,2)
vals = values(:,3)
index = numRows * (colIndex - 1) + rowIndex
matrix(index) = vals
In fact, you don't need to go through all the trouble of building the index vector, as the function sub2ind exists to do that for you:
index = sub2ind(size(matrix), rowIndex, colIndex)
matrix(index) = vals
although I think it's good to see how to get the results with a call to sub2index, for your own education.

I made a function to do that, you can use it, if you want:
function B = ndassign( A , varargin )
%%% copy A to B, and assign values to A at specified nd indexes
%%% B=ndind(A,X,Y,Z,V)
%%% ---> B(X(i),Y(i),Z(i))=V(i)
%%% Example:
%%% ndassign(eye(3),[1 2 3],[3 2 1],[4 5 6])
%%% ans =
%%% 1 0 4
%%% 0 5 0
%%% 6 0 1
B=A;
inds=sub2ind(size(A),varargin{1:end-1});
B(inds)=varargin{end};
end

Related

How do I compare elements of one array to a column of a matrix, then shorten the matrix correspondingly?

I have a matrix 'Z' sized 100000x2 and imported as an Excel file using readmatrix. I have a created array 'Time' (Time = [-200:0.1:300]'). I would like to compare all values in column 1 of 'Z' to 'Time' and eliminate all values of column 1 of 'Z' that do not equal a value of 'Time', thus shortening my 'Z' matrix to match my desired time values. Column 2 are pressure traces, so this would give me my desired time values and the corresponding pressure trace.
This sort of thing can be done without loops:
x = [1,2,3,4,1,1,2,3,4];
x = [x', (x+1)'] % this is your 'Z' data from the excel file (toy example here)
x =
1 2
2 3
3 4
4 5
1 2
1 2
2 3
3 4
4 5
y = [1,2]; % this is your row of times you want eliminated
z = x(:,1)==y % create a matrix logical arrays indicating the matches in the first column
z =
9×2 logical array
1 0
0 1
0 0
0 0
1 0
1 0
0 1
0 0
0 0
z = z(:,1)+z(:,2); % there is probably another summing technique that is better for your case
b = [x(z~=1,1), x(z~=1,2)] % use matrix operations to extract the desired rows
b =
3 4
4 5
3 4
4 5
All the entries of x where the first column did not equal 1 or 2 are now gone.
x = ismember(Z(:,1),Time); % logical indexes of the rows you want to keep
Z(~x,:) = []; % get rid of the other rows
Or instead of shortening Z you could create a new array to use downstream in your code:
x = ismember(Z(:,1),Time); % logical indexes of the rows you want to keep
Znew = Z(x,:); % the subset you want
You have to loop over all rows, use a nested if statement to check the item, and delete the row if it doesn't match.
Syntax for loops:
for n = 1:100000:
//(operation)//
end
Syntax for if statements:
if x == y
//(operation)//
Syntax for deleting a row: Z(rownum,:) = [];

MATLAB: How to count number frequency columnwise, in continuous blocks

I have a matrix a in Matlab that looks like the following:
a = zeros(10,3);
a(3:6,1)=2; a(5:9,3)=1; a(5:7,2)=3; a(8:10,1)=2;
a =
0 0 0
0 0 0
2 0 0
2 0 0
2 3 1
2 3 1
0 3 1
2 0 1
2 0 1
2 0 0
I would like to obtain a cell array with the number of times that each number appears in a column. Also, it should be ordered depending on the element value, regardless of the column number. In the example above I would like to obtain the cell:
b = {[5],[4,3],[3]}
Because the number 1 appears once for 5 times, the number 2 twice in blocks of 4 and 3, and the number 3 once for 3 times. As you can see the recurrences are ordered according to the element value and not to the number of the column where the elements appear.
Since you're not concerned with the column, you can string all the columns into a single column vector, padding with zeroes on either end to prevent spans at the start and end of columns from running together:
v = reshape(padarray(a, [1 0]), [], 1);
% Or if you don't have the Image Processing Toolbox function padarray...
v = reshape([zeros(1, size(a, 2)); a; zeros(1, size(a, 2))], [], 1);
Now, assuming spans are always separated by 1 or more zeroes, you can find the length of each span as follows:
endPoints = find(diff(v) ~= 0); % Find where transitions to or from 0 occur
spans = endPoints(2:2:end)-endPoints(1:2:end); % Index of transitions to 0 minus
% index of transitions from 0
And finally you can accumulate the spans based on the value present in those spans:
b = accumarray(v(endPoints(1:2:end)+1), spans, [], #(v) {v(:).'}).';
And for your example:
b =
1×3 cell array
[5] [1×2 double] [3]
Note:
The ordering of values in the resulting cell array is not guaranteed to match the order in spans (i.e. b{2} above is [3 4] instead of [4 3]). If order matters, you'll need to sort your subscripts as per this section of the documentation. Here's how you would change the computation of b:
[vals, index] = sort(v(endPoints(1:2:end)+1));
b = accumarray(vals, spans(index), [], #(v) {v(:).'}).';
The hard part is finding and separating the blocks. diff will find the starting point of any run of numbers, which is the starting point for this solution:
b = [zeros(1,size(a,2)); a; zeros(1,size(a,2))];
idx = diff(b)~=0;
block_values = b(idx);
block_lengths = diff([0; find(idx)]);
Now we have two vectors of the values of each block, and how long they are, and they just need to be captured in the cell array, ignoring the zero blocks
c = accumarray(block_values(block_values~=0), block_lengths(block_values~=0), [], #(x) {x}).';
b = {}
for i = 1:ncolumns
for n = 1:nnumbers
b{i}(n) = sum(a(:,i) == n)
end
end
(Note that this places zeros for numbers at which the count is 0, but otherwise I don't see how else you would be able to recognize which value is being counted)

Matlab - insert/append rows into matrix iteratively

How in matlab I can interactively append matrix with rows?
For example lets say I have empty matrix:
m = [];
and when I run the for loop, I get rows that I need to insert into matrix.
For example:
for i=1:5
row = v - x; % for example getting 1 2 3
% m.append(row)?
end
so after inserting it should look something like:
m = [
1 2 3
3 2 1
1 2 3
4 3 2
1 1 1
]
In most programming languages you can simply append rows into array/matrix. But I find it hard to do it in matlab.
m = [m ; new_row]; in your loop. If you know the total row number already, define m=zeros(row_num,column_num);, then in your loop m(i,:) = new_row;
Just use
m = [m; row];
Take into account that extending a matrix is slow, as it involves memory reallocation. It's better to preallocate the matrix to its full size,
m = NaN(numRows,numCols);
and then fill the row values at each iteration:
m(ii,:) = row;
Also, it's better not to use i as a variable name, because by default it represents the imaginary unit (that's why I'm using ii here as iteration index).
To create and add a value into the matrix you can do this and can make a complete matrix like yours.
Here row = 5 and then column = 3 and for hence two for loop.
Put the value in M(i, j) location and it will insert the value in the matrix
for i=1:5
for j=1:3
M(i, j) = input('Enter a value = ')
end
fprintf('Row %d inserted successfully\n', i)
end
disp('Full Matrix is = ')
disp(M)
Provably if you enter the same values given, the output will be like yours,
Full Matrix is =
1 2 3
3 2 1
1 2 3
4 3 2
1 1 1

Matlab: Getting Random values from each column w/o zeros

I have a 2d matrix as follows:
possibleDirections =
1 1 1 1 0
0 0 2 2 0
3 3 0 0 0
0 4 0 4 4
5 5 5 5 5
I need from every column to get a random number from the values that are non-zero in to a vector. The value 5 will always exist so there won't be any columns with all zeros.
Any ideas how this can be achieved with the use of operations on the vectors (w/o treating each column separately)?
An example result would be [1 1 1 1 5]
Thanks
You can do this without looping directly or via arrayfun.
[rowCount,colCount] = size(possibleDirections);
nonZeroCount = sum(possibleDirections ~= 0);
index = round(rand(1,colCount) .* nonZeroCount +0.5);
[nonZeroIndices,~] = find(possibleDirections);
index(2:end) = index(2:end) + cumsum(nonZeroCount(1:end-1));
result = possibleDirections(nonZeroIndices(index)+(0:rowCount:(rowCount*colCount-1))');
Alternative solution:
[r,c] = size(possibleDirections);
[notUsed, idx] = max(rand(r, c).*(possibleDirections>0), [], 1);
val = possibleDirections(idx+(0:c-1)*r);
If the elements in the matrix possibleDirections are always either zero or equal to the respective row number like in the example given in the question, the last line is not necessary as the solution would already be idx.
And a (rather funny) one-liner:
result = imag(max(1e05+rand(size(possibleDirections)).*(possibleDirections>0) + 1i*possibleDirections, [], 1));
Note, however, that this one-liner only works if the values in possibleDirections are much smaller than 1e5.
Try this code with two arrayfun calls:
nc = size(possibleDirections,2); %# number of columns
idx = possibleDirections ~=0; %# non-zero values
%# indices of non-zero values for each column (cell array)
tmp = arrayfun(#(x)find(idx(:,x)),1:nc,'UniformOutput',0);
s = sum(idx); %# number of non-zeros in each column
%# for each column get random index and extract the value
result = arrayfun(#(x) tmp{x}(randi(s(x),1)), 1:nc);

MATLAB: How do I fix subscripted assignment dimension mismatch?

new_img is ==>
new_img = zeros(height, width, 3);
curMean is like this: [double, double, double]
new_img(rows,cols,:) = curMean;
so what is wrong here?
The line:
new_img(rows,cols,:) = curMean;
will only work if rows and cols are scalar values. If they are vectors, there are a few options you have to perform the assignment correctly depending on exactly what sort of assignment you are doing. As Jonas mentions in his answer, you can either assign values for every pairwise combination of indices in rows and cols, or you can assign values for each pair [rows(i),cols(i)]. For the case where you are assigning values for every pairwise combination, here are a couple of the ways you can do it:
Break up the assignment into 3 steps, one for each plane in the third dimension:
new_img(rows,cols,1) = curMean(1); %# Assignment for the first plane
new_img(rows,cols,2) = curMean(2); %# Assignment for the second plane
new_img(rows,cols,3) = curMean(3); %# Assignment for the third plane
You could also do this in a for loop as Jonas suggested, but for such a small number of iterations I kinda like to use an "unrolled" version like above.
Use the functions RESHAPE and REPMAT on curMean to reshape and replicate the vector so that it matches the dimensions of the sub-indexed section of new_img:
nRows = numel(rows); %# The number of indices in rows
nCols = numel(cols); %# The number of indices in cols
new_img(rows,cols,:) = repmat(reshape(curMean,[1 1 3]),[nRows nCols]);
For an example of how the above works, let's say I have the following:
new_img = zeros(3,3,3);
rows = [1 2];
cols = [1 2];
curMean = [1 2 3];
Either of the above solutions will give you this result:
>> new_img
new_img(:,:,1) =
1 1 0
1 1 0
0 0 0
new_img(:,:,2) =
2 2 0
2 2 0
0 0 0
new_img(:,:,3) =
3 3 0
3 3 0
0 0 0
Be careful with such assignments!
a=zeros(3);
a([1 3],[1 3]) = 1
a =
1 0 1
0 0 0
1 0 1
In other words, you assign all combinations of row and column indices. If that's what you want, writing
for z = 1:3
newImg(rows,cols,z) = curMean(z);
end
should get what you want (as #gnovice suggested).
However, if rows and cols are matched pairs (i.e. you'd only want to assign 1 to elements (1,1) and (3,3) in the above example), you may be better off writing
for i=1:length(rows)
newImg(rows(i),cols(i),:) = curMean;
end