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