Using 'find' on rows of a matrix without looping - matlab

If A is a nx by ny matrix of zeros and ones and I want to find the index of the the first and last zeros in each row. Currently I'm doing the following:
for ix = 1:nx
lhs_i = find(A(ix,:) < 1,1,'first');
rhs_i = find(A(ix,:) < 1,1,'last');
if ~isempty(lhs_i)
lhs(ix,k) = lhs_i;
rhs(ix,k) = rhs_i;
else
lhs(ix,k) = NaN;
rhs(ix,k) = NaN;
end
end
I'm sure there is a better way that doesn't involve a loop. Any suggestions?

You can use accumarray -
[R,C] = find(A==0);
out = zeros(size(A,1),2)
out(1:max(R),:) = [accumarray(R,C,[],#min) accumarray(R,C,[],#max)]
Finally, if need be, replace the zeros with NaNs, but zeros themselves look like good specifiers of invalid rows (rows without zeros).
Sample run -
>> A
A =
3 1 3 3 4
0 3 0 2 0
0 0 4 4 0
1 4 1 4 2
>> [R,C] = find(A==0);
>> out = zeros(size(A,1),2);
>> out(1:max(R),:) = [accumarray(R,C,[],#min) accumarray(R,C,[],#max)]
out =
0 0
1 5
1 5
0 0
Here's another using bsxfun and minmax -
P = bsxfun(#times,A==0,1:size(A,2));
P(P==0) = nan;
out = minmax(P)
With this solution, Inf/-Inf would act as the invalid specifier.
Sample run -
>> A
A =
0 4 0 2 2
3 4 3 1 1
0 4 3 1 2
1 0 3 4 0
>> P = bsxfun(#times,A==0,1:size(A,2));
>> P(P==0) = nan;
>> minmax(P)
ans =
1 3
-Inf Inf
1 1
2 5

Related

Replace specific matrix position with array value without using for loop in MATLAB

can I know how can I replace values in specific matrix position without using for loop in MATLAB? I initialize matrix a that I would like to replace its value on specified row and column for each no. This has to be done a few time within num for loop. The num for loop is important here because I would want the update the value in the original code.
The real code is more complicated, I am simplifying the code for this question.
I have the code as follow:
a = zeros(2,10,15);
for num = 1:10
b = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
c = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
d = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
for no = 1:15
a(b(no),d(no),no) = c(1,no,:)
end
end
A sample output for, say no 13 is as follows:
a(:,:,13) =
Columns 1 through 8
0 0 0 0 0 7.3160 0 0
0 0 0 0 0 0 0 0
Columns 9 through 10
0 0
0 0
Thank you so much for any help I could get.
It can be done using sub2ind, which casts the subs to a linear index.
Following your vague variable names, it would look like this (omitting the useless loop over num):
a = zeros(2,10,15);
b = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
d = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
c = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
% // we vectorize the loop over no:
no = 1:15;
a(sub2ind(size(a), b, d, no)) = c;
Apart from the sub2ind based approach as suggested in Nras's solution, you can use a "raw version" of sub2ind to reduce a function call if performance is very critical. The related benchmarks comparing sub2ind and it's raw version is listed in another solution. Here's the implementation to solve your case -
no = 1:15
a = zeros(2,10,15);
[m,n,r] = size(a)
a((no-1)*m*n + (d-1)*m + b) = c
Also for pre-allocation, you can use a much faster approach as listed in Undocumented MATLAB blog post on Preallocation performance with -
a(2,10,15) = 0;
The function sub2ind is your friend here:
a = zeros(2,10,15);
x = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
y = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
z = 1:15;
dat = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
inds = sub2ind(size(a), x, y, z);
a(inds) = dat;
Matlab provides a function 'sub2ind' may do what you expected.
with variable as the same you posted:
idx = sub2ind(size(a),b,d,[1:15]); % return the index of row a column b and page [1:15]
a(idx) = c;

Find position of vector in matrix [duplicate]

This question already has answers here:
General method to find submatrix in matlab matrix
(3 answers)
Closed 8 years ago.
I have the vector:
1 2 3
and the matrix:
4 1 2 3 5 5
9 8 7 6 3 1
1 4 7 8 2 3
I am trying to find a simple way of locating the vector [1 2 3] in my matrix.
A function returning either coordinates (Ie: (1,2:4)) or a matrix of 1s where there is a match a 0s where there isn't, Ie:
0 1 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
So far, the only function I've found is is 'ismember', which however only tells me if the individual components of the vector appear in the matrix. Suggestions?
Use strfind with a linearized version of the matrix, and then convert linear indices to subindices. Care should be taken to remove matches of the vector spanning different rows.
mat = [1 2 3 1 2 3 1 2;
3 0 1 2 3 5 4 4]; %// data
vec = [1 2 3]; %// data
ind = strfind(reshape(mat.',[],1).', vec);
[col row] = ind2sub(fliplr(size(mat)), ind);
keep = col<=size(mat,2)-length(vec)+1; %// remove result split across rows
row = row(keep);
col = col(keep);
Result for this example:
>> row, col
row =
1 1 2
col =
1 4 3
meaning the vector appears three times: row 1, col 1; row 1, col 4; row 2, col 3.
The result can be expressed in zero-one form as follows:
result = zeros(fliplr(size(mat)));
ind_ones = bsxfun(#plus, ind(keep).', 0:numel(vec)-1);
result(ind_ones) = 1;
result = result.';
which gives
>> result
result =
1 1 1 1 1 1 0 0
0 0 1 1 1 0 0 0
One way to get the starting location of the vector in the matrix is using colfilt:
>> A = [1 2 3 1 2 3 1 2; ...
3 0 1 2 3 5 4 4]; % matrix from Luis Mendo
>> T = [1 2 3];
>> colFun = #(x,t) all(x==repmat(t,1,size(x,2)),1);
>> B = colfilt(A,size(T),'sliding',colFun,T(:))
B =
0 1 0 0 1 0 0 0
0 0 0 1 0 0 0 0
That gives a mask of the center points, which translate to (row,col) coordinates:
>> [ii,jj]=find(B);
>> locs = bsxfun(#minus,[ii jj],floor((size(T)-1)/2))
locs =
1 1
2 3
1 4

How to find one value from a matrix

0I have on matrix-
A=[1 2 2 3 5 5;
1 5 5 8 8 7;
2 9 9 3 3 5];
From matrix i need to count now many nonzero elements ,how any 1,how many 2 and how many 3 in each row of given matrix"A".For these i have written one code like:
[Ar Ac]=size(A);
for j=1:Ar
for k=1:Ac
count(:,j)=nnz(A(j,:));
d(:,j)=sum(A(j,:)== 1);
e(:,j)=sum(A(j,:)==2);
f(:,j)=sum(A(j,:)==3);
end
end
but i need to write these using on loop i.e. here i manually use sum(A(j,:)== 1),sum(A(j,:)== 2) and sum(A(j,:)== 3) but is there any option where i can only write sum(A(j,:)== 1:3) and store all the values in the different row i.e, the result will be like-
b=[1 2 1;
1 0 0;
0 1 2];
Matlab experts need your valuable suggestions
Sounds like you're looking for a histogram count:
U = unique(A);
counts = histc(A', U)';
b = counts(:, ismember(U, [1 2 3]));
Example
%// Input matrix and vector of values to count
A = [1 2 2 3 5 5; 1 5 5 8 8 7; 2 9 9 3 3 5];
vals = [1 2 3];
%// Count values
U = unique(A);
counts = histc(A', U)';
b = counts(:, ismember(U, vals));
The result is:
b =
1 2 1
1 0 0
0 1 2
Generalizing the sought values, as required by asker:
values = [ 1 2 3 ]; % or whichever values are sought
B = squeeze(sum(bsxfun(#(x,y) sum(x==y,2), A, shiftdim(values,-1)),2));
Here is a simple and general way. Just change n to however high you want to count. n=max(A(:)) is probably a good general value.
result = [];
n = 3;
for col= 1:n
result = [result, sum(A==col, 2)];
end
result
e.g. for n = 10
result =
1 2 1 0 2 0 0 0 0 0
1 0 0 0 2 0 1 2 0 0
0 1 2 0 1 0 0 0 2 0
Why not use this?
B=[];
for x=1:size(A,1)
B=[B;sum(A(x,:)==1),sum(A(x,:)==2),sum(A(x,:)==3)];
end
I'd do this way:
B = [arrayfun(#(i) find(A(i,:) == 1) , 1:3 , 'UniformOutput', false)',arrayfun(#(i) find(A(i,:) == 2) , 1:3 , 'UniformOutput', false)',arrayfun(#(i) find(A(i,:) == 3) , 1:3 , 'UniformOutput', false)'];
res = cellfun(#numel, B);
Here is a compact one:
sum(bsxfun(#eq, permute(A, [1 3 2]), 1:3),3)
You can replace 1:3 with any array.
you can make an anonymous function for it
rowcnt = #(M, R) sum(bsxfun(#eq, permute(M, [1 3 2]), R),3);
then running it on your data returns
>> rowcnt(A,1:3)
ans =
1 2 1
1 0 0
0 1 2
and for more generalized case
>> rowcnt(A,[1 2 5 8])
ans =
1 2 2 0
1 0 2 2
0 1 1 0

MATLAB - Add Value at the first column of a matrix

I want to add one value at the beginning of a matrix for example, my matrix is
0,0,0,0,0,0
0,1,1,1,0,0
1,0,0,1,0,0
1,1,1,0,0,0
then I want to add '1' or '0' at the first column therefore it will become like this
1,0,0,0,0,0,0
1,0,1,1,1,0,0
1,1,0,0,1,0,0
1,1,1,1,0,0,0
0,0,0,0,0,0,0
0,0,1,1,1,0,0
0,1,0,0,1,0,0
0,1,1,1,0,0,0
how can I do that in MATLAB?
You can concatenate matrices without creating another one from scratch like this:
% your matrix
A = [ 0,0,0,0,0,0;
0,1,1,1,0,0;
1,0,0,1,0,0;
1,1,1,0,0,0 ];
A_with_zeros = [zeros(size(A,1),1) A]
A_with_ones = [ones(size(A,1),1) A]
% Output:
% A_with_zeros = [ 0 0 0 0 0 0 0
% 0 0 1 1 1 0 0
% 0 1 0 0 1 0 0
% 0 1 1 1 0 0 0 ]
%
% A_with_ones = [ 1 0 0 0 0 0 0 0
% 1 0 0 1 1 1 0 0
% 1 0 1 0 0 1 0 0
% 1 0 1 1 1 0 0 0 ]
Documentation about concatenating matrices:
horzcat
vertcat
These two documentation pages describe in details the existing methods that let you concatenate arrays horizontally (what I did in the example above) and vertically (if you wanted to add lines to your matrix).
Use the array concatenation syntax - [a b] to glue matrices together horizontally, [a; b] to glue them together vertically.
Like so:
>> a = ones(3,1)
a =
1
1
1
>> b = magic(3)
b =
8 1 6
3 5 7
4 9 2
>> c = [a b]
c =
1 8 1 6
1 3 5 7
1 4 9 2
Vertically:
>> d = ones(1,3)
d =
1 1 1
>> e = [d; b]
e =
1 1 1
8 1 6
3 5 7
4 9 2
If you mismatch the dimensions, MATLAB will give you an error. Don't do this:
>> f = ones(1,10)
f =
1 1 1 1 1 1 1 1 1 1
>> g = magic(3)
g =
8 1 6
3 5 7
4 9 2
>> [f; g]
??? Error using ==> vertcat
CAT arguments dimensions are not consistent.
Create a new matrix with an extra column, copy the old matrix in, then put the data for the new column in at (:, 1).
output = zeros(size(input, 1), size(input, 2) + 1);
output(:, 2:end) = input;
output(:, 1) = new_column;
or if you mean that you want to get two matricies, one with a column of 1s and one with a column of 0s:
output0 = zeros(size(input, 1), size(input, 2) + 1);
output0(:, 2:end) = input;
output1 = ones(size(input, 1), size(input, 2) + 1);
output1(:, 2:end) = input;

How to initialize a vector in MATLAB as per a pattern?

I am completely new to MATLAB.This may be a rather basic question.
Given numerical values for size, extras and max, I need to initialize a 1 X N vector such that the first size elements are 1, the next size are 2, the next size are 3 and so on till the last size elements are set to max. So I need to initialize size number of elements successively to x such that x increments from 1 to max. The extras are the number of leftover cells which are initialized to 0. To illustrate:
size = 3; %# (is same as the quotient of N/max)
extras = 1; %# (is same as remainder of N/max)
max = 3;
N = 10;
original_vector = [0 0 0 0 0 0 0 0 0 0];
The desired output is
Required_vector = [1 1 1 2 2 2 3 3 3 0]
Maybe something using the Kronecker product:
N = 10;
max = 3;
extras = rem(N, max);
size = floor(N/max);
v = [kron([1 : max], ones(1,size)) zeros(1, extras)];
I took a guess about how extras and size are calculated. You said size is N % max and extras is N rem max, but those are the same thing(?).
Some reshaping acrobatics should do it:
>> size = 3;
>> max = 3;
>> N = 10;
>> v = zeros(1, N);
>> v(1:size*max) = reshape(cumsum(ones(max, size))', size*max, 1)
v =
1 1 1 2 2 2 3 3 3 0
Another example:
>> size = 4;
>> max = 5;
>> N = 23;
>> v(1:size*max) = reshape(cumsum(ones(max, size))', size*max, 1)
v =
Columns 1 through 18
1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 5 5
Columns 19 through 23
5 5 0 0 0
This is a quite dirty implementation, but as you say you are very new to MATLAB, it might be better for you to see how you can more or less brute force a solution out. The trick here is the index reference done on Vec to place the numbers in. I have ignored the parameter extras and instead fill the vector up as best can be with the elements
N = 23;
max = 3;
size = 4;
Vec = zeros(N,1);
for i=1:max
for j=1:size
Vec((i-1)*size +1 + (j-1)) = i;
end
end
Vec'
extra = sum(Vec==0)
Output:
ans =
1 1 1 1 2 2 2 2 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0
extra =
11
A slight modification of #b3's solution:
N = 10;
mx = 3;
sz = floor(N/mx);
v = zeros(1,N);
v(1:mx*sz) = repmat(1:mx,sz,1)