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

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)

Related

Matlab - Create a bigger matrix out of a smaller one with blank spaces

Let's say I have a 3 x 3 matrix (A), and I want to make it a 5 x 5 matrix (B), but the Matrix A has the following content:
1 2 3
4 5 6
7 8 9
And the resulting bigger matrix B, needs to have the following content:
1 0 2 0 3
0 0 0 0 0
4 0 5 0 6
0 0 0 0 0
7 0 8 0 9
I know this can be done with some "Fors" following a sequence like:
%% We get the dimensions of our matrix.
[xLength, yLength] = size(InMat);
%% We create a matrix of the double size.
NewInMat = zeros(xLength * 2, yLength * 2);
%% We prepare the counters to fill the new matrix.
XLenN = (xLength * 2) -1;
YLenN = (yLength * 2) - 1;
for i = 1 : XLenN
for j = 1 : YLenN
if mod(i, 2) ~= 0
if mod(j, 2) ~= 0
NewInMat(i, j) = InMat(i, j);
else
NewInMat(i,j) = mean([InMat(i, j - 1), InMat(i, j + 2)]);
end
end
end
end
But I would like to know if there is an easier way, or if Matlab has a tool for doing this task. Many thanks in advance!
You can use indexing:
InMat = [...
1 2 3
4 5 6
7 8 9];
s = size(InMat)*2-1;
NewInMat(1:2:s(1), 1:2:s(2)) = InMat;
Here NewInMat is allocated and filled at the same time.

Correspondence label and coordinates' points

How to obtain the coordinates of the first and the last appearances (under column-major ordering) of each label present in a matrix?
Example of a label matrix (where labels are 1 to 4):
L = [
1 1 1 1 0 0 0 0
0 0 0 0 2 2 0 0
0 0 0 0 0 0 2 0
0 0 0 0 0 0 0 0
0 0 0 0 0 3 0 0
0 0 0 0 0 0 3 3
0 0 0 4 0 0 0 0
4 4 4 0 0 0 0 0
];
For the above example L, I would like to obtain a matrix of coordinates like:
M = [
1 1 1
1 4 1
2 5 2
3 7 2
5 6 3
6 8 3
8 1 4
7 4 4 ];
Where the 1st column of M contains horizontal coordinates, the 2nd contains vertical coordinates, and the 3rd column contains the label. There should be 2 rows for each label.
With for-loop you can do it like that:
M=zeros(2*max(L(:)),3);
for k=1:max(L(:))
[r,c]=find(L==k);
s=sortrows([r c],2);
M(k*2-1:k*2,:)=[s(1,:) k; s(end,:) k];
end
M =
1 1 1
1 4 1
2 5 2
3 7 2
5 6 3
6 8 3
8 1 4
7 4 4
Maybe somehow with regionprops options you can do it without the loop...
I just had to try it with accumarray:
R = size(L, 1);
[rowIndex, colIndex, values] = find(L); % Find nonzero values
index = (colIndex-1).*R+rowIndex; % Create a linear index
labels = unique(values); % Find unique values
nLabels = numel(labels);
minmax = zeros(2, nLabels);
minmax(1, :) = accumarray(values, index, [nLabels 1], #min); % Collect minima
minmax(2, :) = accumarray(values, index, [nLabels 1], #max); % Collect maxima
temp = ceil(minmax(:)/R);
M = [minmax(:)-R.*(temp-1) temp repelem(labels, 2, 1)]; % Convert index to subscripts
M =
1 1 1
1 4 1
2 5 2
3 7 2
5 6 3
6 8 3
8 1 4
7 4 4
Here's what I got for timing with Dev-iL's script and Adiel's newest code (Note that the number of labels can't go above 127 due to how Adiel's code uses the uint8 values as indices):
| Adiel | Dev-iL | gnovice
-----------------------+---------+---------+---------
20 labels, 1000x1000 | 0.0753 | 0.0991 | 0.0889
20 labels, 10000x10000 | 12.0010 | 10.2207 | 8.7034
120 labels, 1000x1000 | 0.1924 | 0.3439 | 0.1387
So, for moderate numbers of labels and (relatively) smaller sizes, Adiel's looping solution looks like it does best, with my solution lying between his and Dev-iL's. For larger sizes or greater numbers of labels, my solution starts to take the lead.
If you're looking for a vectorized solution, you can do this:
nTags = max(L(:));
whois = bsxfun(#eq,L,reshape(1:nTags,1,1,[]));
% whois = L == reshape(1:nTags,1,1,[]); % >=R2016b syntax.
[X,Y,Z] = ind2sub(size(whois), find(whois));
tmp = find(diff([0; Z; nTags+1])); tmp = reshape([tmp(1:end-1) tmp(2:end)-1].',[],1);
M = [X(tmp), Y(tmp), repelem(1:nTags,2).'];
Or with extreme variable reuse:
nTags = max(L(:));
Z = bsxfun(#eq,L,reshape(1:nTags,1,1,[]));
[X,Y,Z] = ind2sub(size(Z), find(Z));
Z = find(diff([0; Z; nTags+1]));
Z = reshape([Z(1:end-1) Z(2:end)-1].',[],1);
M = [X(Z), Y(Z), repelem(1:nTags,2).'];
Here's my benchmarking code:
function varargout = b42973322(isGPU,nLabels,lMat)
if nargin < 3
lMat = 1000;
end
if nargin < 2
nLabels = 20; % if nLabels > intmax('uint8'), Change the type of L to some other uint.
end
if nargin < 1
isGPU = false;
end
%% Create L:
if isGPU
L = sort(gpuArray.randi(nLabels,lMat,lMat,'uint8'),2);
else
L = sort(randi(nLabels,lMat,lMat,'uint8'),2);
end
%% Equality test:
M{3} = DeviL2(L);
M{2} = DeviL1(L);
M{1} = Adiel(L);
assert(isequal(M{1},M{2},M{3}));
%% Timing:
% t(3) = timeit(#()DeviL2(L)); % This is always slower, so it's irrelevant.
t(2) = timeit(#()DeviL1(L));
t(1) = timeit(#()Adiel(L));
%% Output / Print
if nargout == 0
disp(t);
else
varargout{1} = t;
end
end
function M = Adiel(L)
M=[];
for k=1:max(L(:))
[r,c]=find(L==k);
s=sortrows([r c],2);
M=[M;s(1,:) k; s(end,:) k];
end
end
function M = DeviL1(L)
nTags = max(L(:));
whois = L == reshape(1:nTags,1,1,[]); % >=R2016b syntax.
[X,Y,Z] = ind2sub(size(whois), find(whois));
tmp = find(diff([0; Z; nTags+1])); tmp = reshape([tmp(1:end-1) tmp(2:end)-1].',[],1);
M = [X(tmp), Y(tmp), repelem(1:nTags,2).'];
end
function M = DeviL2(L)
nTags = max(L(:));
Z = L == reshape(1:nTags,1,1,[]);
[X,Y,Z] = ind2sub(size(Z), find(Z));
Z = find(diff([0; Z; nTags+1]));
Z = reshape([Z(1:end-1) Z(2:end)-1].',[],1);
M = [X(Z), Y(Z), repelem(1:nTags,2).'];
end
You can retrive the uniqe values (your labels) of the matrix with unique.
Having them retrived you can use find to get their indices.
Put together your matrix with it.

Fastest way of generating a logical matrix by given row indices of true values?

What is the most efficient way of generating
>> A
A =
0 1 1
1 1 0
1 0 1
0 0 0
with
>> B = [2 3; 1 2; 1 3]
B =
2 3
1 2
1 3
in MATLAB?
E.g., B(1, :), which is [2 3], means that A(2, 1) and A(3, 1) are true.
My attempt still requires one for loop, iterating through B's row. Is there a loop-free or more efficient way of doing this?
This is one way of many, though sub2ind is the dedicated function for that:
%// given row indices
B = [2 3; 1 2; 1 3]
%// size of row index matrix
[n,m] = size(B)
%// size of output matrix
[N,M] = deal( max(B(:)), n)
%// preallocation of output matrix
A = zeros(N,M)
%// get col indices to given row indices
cols = bsxfun(#times, ones(n,m),(1:n).')
%// set values
A( sub2ind([N,M],B,cols) ) = 1
A =
0 1 1
1 1 0
1 0 1
If you want a logical matrix, change the following to lines
A = false(N,M)
A( sub2ind([N,M],B,cols) ) = true
Alternative solution
%// given row indices
B = [2 3; 1 2; 1 3];
%// number if rows
r = 4; %// e.g. = max(B(:))
%// number if cols
c = 3; %// size(B,1)
%// preallocation of output matrix
A = zeros(r,c);
%// set values
A( bsxfun(#plus, B.', 0:r:(r*(c-1))) ) = 1;
Here's a way, using the sparse function:
A = full(sparse(cumsum(ones(size(B))), B, 1));
This gives
A =
0 1 1
1 1 0
1 0 1
If you need a predefined number of rows in the output, say r (in your example r = 4):
A = full(sparse(cumsum(ones(size(B))), B, 1, 4, size(B,1)));
which gives
A =
0 1 1
1 1 0
1 0 1
0 0 0
You can equivalently use the accumarrray function:
A = accumarray([repmat((1:size(B,1)).',size(B,2),1), B(:)], 1);
gives
A =
0 1 1
1 1 0
1 0 1
Or with a predefined number of rows, r = 4,
A = accumarray([repmat((1:size(B,1)).',size(B,2),1), B(:)], 1, [r size(B,1)]);
gives
A =
0 1 1
1 1 0
1 0 1
0 0 0

Using 'find' on rows of a matrix without looping

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

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;