MATLAB: How do I fix subscripted assignment dimension mismatch? - matlab

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

Related

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)

How to get the logical matrix corresponding to "scatter" plot?

If I have a two column matrix A like below, I can plot the scatter plot using scatter/plot command. I would like to get the matrix corresponding to such outputs as in hist command. hist command gives the vector output too.
A=[7 1;3 2; 4 3]
For example out=scatter(A(:,1),A(:,2)) must give something like below:
[0 0 0;
0 0 0;
0 1 0;
0 0 1;
0 0 0;
0 0 0;
1 0 0]
Only the indices (7,1), (3,2) and (4,3) are only ones. Or Can someone give me a snippet code to realize this without using loops?
You can use a combination of sparse and full where you can specify the non-zero row and column locations, and the rest of the matrix would be zero:
A = [7 1; 3 2; 4 3];
B = full(sparse(A(:,1), A(:,2), 1, max(A(:,1)), max(A(:,2)))) == 1;
The sparse command takes in the row and column locations of what is non-zero for the first two inputs, the third input is what the non-zero location would be for each row and column location. We can specify a constant to mean that every non-zero location gets the same coefficient, which is 1. We can also specify the size of the matrix, where in this case the rows and columns of the output correspond to the largest number in the first and second columns respectively. Because this is a sparse matrix, you will want to convert this to a full matrix and because you want it to be logical, you will want to compare all elements with the number 1.
We thus get for the output, which is B:
B =
7×3 logical array
0 0 0
0 0 0
0 1 0
0 0 1
0 0 0
0 0 0
1 0 0
Alternatively, we can use sub2ind to create linear indices to index into a pre-allocated matrix of logical false and set only those non-zero row locations to true:
A = [7 1; 3 2; 4 3];
B = false(max(A(:,1)), max(A(:,2)));
ind = sub2ind(size(B), A(:,1), A(:,2));
B(ind) = true;
We first allocate the matrix, then calculate the linear indices to index into the matrix, then finally set the right locations to true. The output here would be the same as the sparse approach.
Just to add: rayryeng's solution is fine if you really want your result to be logical in the sense that it is equal to one if there is anything at the coordinate and zero otherwise. Still, since you added a note on hist, I was wondering if you actually want to count the number of times a specific coordinate is hit. In this case, consider using
S = histcounts2(A(:,2),A(:,1));
if you have access to R2015b+. If not, there is a hist2 function on fileexchange you can use for the purpose.
Here is my solution. Matlab provides a command called accumarray.
S = logical(accumarray(A, 1) )
will give the result too.

assign new matrix values based on row and column index vectors

New to MatLab here (R2015a, Mac OS 10.10.5), and hoping to find a solution to this indexing problem.
I want to change the values of a large 2D matrix, based on one vector of row indices and one of column indices. For a very simple example, if I have a 3 x 2 matrix of zeros:
A = zeros(3, 2)
0 0
0 0
0 0
I want to change A(1, 1) = 1, and A(2, 2) = 1, and A(3, 1) = 1, such that A is now
1 0
0 1
1 0
And I want to do this using vectors to indicate the row and column indices:
rows = [1 2 3];
cols = [1 2 1];
Is there a way to do this without looping? Remember, this is a toy example that needs to work on a very large 2D matrix. For extra credit, can I also include a vector that indicates which value to insert, instead of fixing it at 1?
My looping approach is easy, but slow:
for i = 1:length(rows)
A(rows(i), cols(i)) = 1;
end
sub2ind can help here,
A = zeros(3,2)
rows = [1 2 3];
cols = [1 2 1];
A(sub2ind(size(A),rows,cols))=1
A =
1 0
0 1
1 0
with a vector to 'insert'
b = [1,2,3];
A(sub2ind(size(A),rows,cols))=b
A =
1 0
0 2
3 0
I found this answer online when checking on the speed of sub2ind.
idx = rows + (cols - 1) * size(A, 1);
therefore
A(idx) = 1 % or b
5 tests on a big matrix (~ 5 second operations) shows it's 20% faster than sub2ind.
There is code for an n-dimensional problem here too.
What you have is basically a sparse definition of a matrix. Thus, an alternative to sub2ind is sparse. It will create a sparse matrix, use full to convert it to a full matrix.
A=full(sparse(rows,cols,1,3,2))

Eliminating zeros in a matrix - Matlab

Hi I have the following matrix:
A= 1 2 3;
0 4 0;
1 0 9
I want matrix A to be:
A= 1 2 3;
1 4 9
PS - semicolon represents the end of each column and new column starts.
How can I do that in Matlab 2014a? Any help?
Thanks
The problem you run into with your problem statement is the fact that you don't know the shape of the "squeezed" matrix ahead of time - and in particular, you cannot know whether the number of nonzero elements is a multiple of either the rows or columns of the original matrix.
As was pointed out, there is a simple function, nonzeros, that returns the nonzero elements of the input, ordered by columns. In your case,
A = [1 2 3;
0 4 0;
1 0 9];
B = nonzeros(A)
produces
1
1
2
4
3
9
What you wanted was
1 2 3
1 4 9
which happens to be what you get when you "squeeze out" the zeros by column. This would be obtained (when the number of zeros in each column is the same) with
reshape(B, 2, 3);
I think it would be better to assume that the number of elements may not be the same in each column - then you need to create a sparse array. That is actually very easy:
S = sparse(A);
The resulting object S is a sparse array - that is, it contains only the non-zero elements. It is very efficient (both for storage and computation) when lots of elements are zero: once more than 1/3 of the elements are nonzero it quickly becomes slower / bigger. But it has the advantage of maintaining the shape of your matrix regardless of the distribution of zeros.
A more robust solution would have to check the number of nonzero elements in each column and decide what the shape of the final matrix will be:
cc = sum(A~=0);
will count the number of nonzero elements in each column of the matrix.
nmin = min(cc);
nmax = max(cc);
finds the smallest and largest number of nonzero elements in any column
[i j s] = find(A); % the i, j coordinates and value of nonzero elements of A
nc = size(A, 2); % number of columns
B = zeros(nmax, nc);
for k = 1:nc
B(1:cc(k), k) = s(j == k);
end
Now B has all the nonzero elements: for columns with fewer nonzero elements, there will be zero padding at the end. Finally you can decide if / how much you want to trim your matrix B - if you want to have no zeros at all, you will need to trim some values from the longer columns. For example:
B = B(1:nmin, :);
Simple solution:
A = [1 2 3;0 4 0;1 0 9]
A =
1 2 3
0 4 0
1 0 9
A(A==0) = [];
A =
1 1 2 4 3 9
reshape(A,2,3)
ans =
1 2 3
1 4 9
It's very simple though and might be slow. Do you need to perform this operation on very large/many matrices?
From your question it's not clear what you want (how to arrange the non-zero values, specially if the number of zeros in each column is not the same). Maybe this:
A = reshape(nonzeros(A),[],size(A,2));
Matlab's logical indexing is extremely powerful. The best way to do this is create a logical array:
>> lZeros = A==0
then use this logical array to index into A and delete these zeros
>> A(lZeros) = []
Finally, reshape the array to your desired size using the built in reshape command
>> A = reshape(A, 2, 3)

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);