Generate square matrix for vector with diagonals in MatLab - arrays

I have a vector, where each value corresponds to a diagonal. I want to create a matrix from this vector. I have a code:
x = [1:5];
N = numel(x);
diagM = diag(repmat(x(1),N,1),0);
for iD = 2:N
d = repmat(x(iD),N-iD+1,1);
d_pos = diag(d,iD-1);
d_neg = diag(d,-iD+1);
d_join = d_pos+d_neg;
diagM = diagM+d_join;
end
It gives me what i want:
diagM =
1 2 3 4 5
2 1 2 3 4
3 2 1 2 3
4 3 2 1 2
5 4 3 2 1
But it becames really slow, for example for x=[1:10^4].
Could You help me with another FASTER way to generate such a sequence?

Use toeplitz:
x = 1:5;
diagM = toeplitz(x);
Or do it manually, vectorized:
x = 1:5;
t = 1:numel(x);
diagM = x(abs(t-t.')+1); % x(abs(bsxfun(#minus, t, t.'))+1) in old Matlab versions

Related

Sort a matrix according to ordering in another matrix

I am trying to sort an array based on another array. I tried the sort method with index return, but it is somehow behaving strangely.
y = [1 2 3; 2 3 4]
x = [1 3 4; 2 2 3]
[yy, ii] = sort(y,'descend');
yy =
2 3 4
1 2 3
ii =
2 2 2
1 1 1
But my x(ii) is not the matrix sorted based on y.
x(ii) =
2 2 2
1 1 1
I am expecting the matrix to be:
x(ii) =
2 2 3
1 3 4
How can I sort the matrix x according to another matrix y?
ii are row subscripts but are being inputted by you as linear indices.
You need to convert them to relevant linear indices before proceeding i.e.
>> szx = size(x);
>> x(sub2ind(szx, ii, repmat(1:szx(2),szx(1),1)))
ans =
2 2 3
1 3 4

Extract pattern and subsequent n elements from array and count number of occurences

I have an array of doubles like this:
C = [1 2 3 4 0 3 2 5 6 7 1 2 3 4 150 30]
i want to find the pattern [1 2 3 4] within the array and then store the 2 values after that pattern with it like:
A = [1 2 3 4 0 3]
B = [1 2 3 4 150 30]
i can find the pattern like this but i don't know how to get and store 2 values after that with the previous one.
And after finding A, B if i want to find the number of occurrences of each arrays within array C how can i do that?
indices = cellfun(#(c) strfind(c,pattern), C, 'UniformOutput', false);
Thanks!
Assuming you're fine with a cell array output, this works fine:
C = [1 2 3 4 0 3 2 5 6 7 1 2 3 4 150 30 42 1 2 3 4 0 3]
p = [1 2 3 4]
n = 2
% full patttern length - 1
dn = numel(p) + n - 1
%// find indices
ind = strfind(C,p)
%// pre check if pattern at end of array
if ind(end)+ dn > numel(C), k = -1; else k = 0; end
%// extracting
temp = arrayfun(#(x) C(x:x+dn), ind(1:end+k) , 'uni', 0)
%// post processing
[out, ~, idx] = unique(vertcat(temp{:}),'rows','stable')
occ = histcounts(idx).'
If the array C ends with at least n elements after the last occurrence of the pattern p, you can use the short form:
out = arrayfun(#(x) C(x:x+n+numel(p)-1), strfind(C,p) , 'uni', 0)
out =
1 2 3 4 0 3
1 2 3 4 150 30
occ =
2
1
A simple solution can be:
C = [1 2 3 4 0 3 2 5 6 7 1 2 3 4 150 30];
pattern = [1 2 3 4];
numberOfAddition = 2;
outputs = zeros(length(A),length(pattern)+ numberOfAddition); % preallocation
numberOfFoundPattern = 1;
lengthOfConsider = length(C) - length(pattern) - numberOfAddition;
for i = 1:lengthOfConsider
if(sum(C(i:i+length(pattern)) - pattern) == 0) % find pattern
outputs(numberOfFoundPattern,:) = C(i:i+length(pattern)+numberOfAddition);
numberOfFoundPattern = numberOfFoundPattern + 1;
end
end
outputs = outputs(1:numberOfFoundPattern - 1,:);

Matlab: Assemble submatrices whose #cols and #rows are stored in a vector

I have two vectors, R and C, which have the number of rows and columns, respectively, of submatrices that I need to assemble in a ones matrix I (40x20). There's 12 submatrices total.
R = [4 2 4 4 2 4];
C = [4 16 16 4];
Moreover, all the elements of each submatrix have its value stored in vector k:
k = [3 2 3 3 2 3 2 1 2 2 1 2 2 1 2 2 1 2 3 2 3 3 2 3 ]; % 24 elements
Thus for instance, submatrix M(1:4,1:4) has 4 rows, and 4 columns and value equal to k(1) = 1.
QUESTION: How can I assemble matrix M with all submatrices?
Any ideas?
Thanks!
EDIT:
The matrix M should look like this:
and the submatrices:
and the values of k:
Here is a vectorized solution:
R1 = repelem(1:numel(R), R);
C1 = repelem(1:numel(C), C);
[CC RR] = meshgrid(C1, R1);
idx = sub2ind([numel(R), numel(C)], RR, CC);
result = k(idx);
Instead you can use cell array, fill it with sub matrices and then convert the cell array to a matrix.
carr = cell(numel(R), numel(C));
k1 = reshape(k,numel(R),numel(C));
for ii = 1:numel(R)
for jj = 1:numel(C)
carr(ii,jj)=repmat(K1(ii,jj), R(ii), C(jj));
end
end
result = cell2mat(carr)

Find consecutive values in 3D array

Say I have an array the size 100x150x30, a geographical grid 100x150 with 30 values for each grid point, and want to find consecutive elements along the third dimension with a congruous length of minimum 3.
I would like to find the maximum length of consecutive elements blocks, as well as the number of occurrences.
I have tried this on a simple vector:
var=[20 21 50 70 90 91 92 93];
a=diff(var);
q = diff([0 a 0] == 1);
v = find(q == -1) - find(q == 1);
v = v+1;
v2 = v(v>3);
v3 = max(v2); % maximum length: 4
z = numel(v2); % number: 1
Now I'd like to apply this to the 3rd dimension of my array.
With A being my 100x150x30 array, I've come this far:
aa = diff(A, 1, 3);
b1 = diff((aa == 1),1,3);
b2 = zeros(100,150,1);
qq = cat(3,b2,b1,b2);
But I'm stuck on the next step, which would be: find(qq == -1) - find(qq == 1);. I can't make it work.
Is there a way to put it in a loop, or do I have to find the consecutive values another way?
Thanks for any help!
A = randi(25,100,150,30); %// generate random array
tmpsize = size(A); %// get its size
B = diff(A,1,3); %// difference
v3 = zeros(tmpsize([1 2])); %//initialise
z = zeros(tmpsize([1 2]));
for ii = 1:100 %// double loop over all entries
for jj = 1:150
q = diff([0 squeeze(B(ii,jj,:)).' 0] == 1);%'//
v = find(q == -1) - find(q == 1);
v=v+1;
v2=v(v>3);
try %// if v2 is empty, set to nan
v3(ii,jj)=max(v2);
catch
v3(ii,jj)=nan;
end
z(ii,jj)=numel(v2);
end
end
The above seems to work. It just doubly loops over both dimensions you want to get the difference over.
The part where I think you were stuck was using squeeze to get the vector to put in your variable q.
The try/catch is there solely to prevent empty consecutive arrays in v2 throwing an error in the assignment to v3, since that would remove its entry. Now it simply sets it to nan, though you can switch that to 0 of course.
Here's one vectorized approach -
%// Parameters
[m,n,r] = size(var);
max_occ_thresh = 2 %// Threshold for consecutive occurrences
% Get indices of start and stop of consecutive number islands
df = diff(var,[],3)==1;
A = reshape(df,[],size(df,3));
dfA = diff([zeros(size(A,1),1) A zeros(size(A,1),1)],[],2).'; %//'
[R1,C1] = find(dfA==1);
[R2,C2] = find(dfA==-1);
%// Get interval lengths
interval_lens = R2 - R1+1;
%// Get max consecutive occurrences across dim-3
max_len = zeros(m,n);
maxIDs = accumarray(C1,interval_lens,[],#max);
max_len(1:numel(maxIDs)) = maxIDs
%// Get number of consecutive occurrences that are a bove max_occ_thresh
num_occ = zeros(m,n);
counts = accumarray(C1,interval_lens>max_occ_thresh);
num_occ(1:numel(counts)) = counts
Sample run -
var(:,:,1) =
2 3 1 4 1
1 4 1 5 2
var(:,:,2) =
2 2 3 1 2
1 3 5 1 4
var(:,:,3) =
5 2 4 1 2
1 5 1 5 1
var(:,:,4) =
3 5 5 1 5
5 1 3 4 3
var(:,:,5) =
5 5 4 4 4
3 4 5 2 2
var(:,:,6) =
3 4 4 5 3
2 5 4 2 2
max_occ_thresh =
2
max_len =
0 0 3 2 2
0 2 0 0 0
num_occ =
0 0 1 0 0
0 0 0 0 0

Efficient technique to interleave data sets by classes in MATLAB

The data set is in the following format: Input sample matrix X and output class vector Y such that each row in X is a sample and each of its column corresponds to a feature. Each index in Y corresponds to the respective output class for the corresponding sample in X. X can contain real numbers while Y contains positive integers.
My aim is to order the data set in terms of its class. For example
X = Y =
1 8 3 2
4 2 6 1
7 8 9 2
2 3 4 3
1 4 6 1
should be ordered and interleaved as
X = Y =
4 2 6 1
1 8 3 2
2 3 4 3
1 4 6 1
7 8 9 2
The code I've attempted seems to take a long time to run as it is based on serial execution. It is the following.
X = csvread('X.csv');
Y = csvread('Y.csv');
n = size(unique(Y),1);
m = size(X,1);
for i = 1:n
Dataset(i).X = X(Y==i,:);
Dataset(i).Y = Y(Y==i);
end
[num, ~] = hist(Y,n);
maxfreq = max(num);
NewX = [];
NewY = [];
for j = 1:maxfreq
for i = 1:n
if(j <= size(Dataset(i).X,1))
NewX = [NewX; Dataset(i).X(j,:)];
NewY = [NewY; i];
end
end
end
X = NewX;
Y = NewY;
clear NewX;
clear NewY;
csvwrite('OrderedX.csv', X);
csvwrite('OrderedY.csv', Y);
Is is possible to parallelize the above code?
You're resizing matrices all the time which is expensive. A quick speedup for your algorithm would be to set NewX and NewY to the proper size and just copy data in:
NewX = zeros(size(X));
NewY = zeros(size(Y));
k = 1;
for j = 1:maxfreq
for i = 1:n
if(j <= size(Dataset(i).X,1))
NewX(k,:) = Dataset(i).X(j,:);
NewY(k) = i;
k=k+1;
end
end
end
Approach #1 Using cumsum and diff following the same philosophy as the one listed in this solution -
function [outX,outY] = interleave_cumsum_diff(X,Y)
Y = Y(:);
[R,C] = find(bsxfun(#eq,Y,unique(Y).'));
lens = accumarray(C,1);
out = ones(1,numel(R));
shifts = cumsum(lens(1:end-1));
out(shifts+1) = 1- diff([0 ; shifts]);
[~,idx] = sort(cumsum(out));
sort_idx = R(idx)';
outX = X(sort_idx,:);
outY = Y(sort_idx,:);
Approach #1 Using bsxfun -
function [outX,outY] = interleave_bsxfuns(X,Y)
Y = Y(:);
[R,C] = find(bsxfun(#eq,Y,unique(Y).'));
lens = accumarray(C,1);
mask = bsxfun(#le,[1:max(lens)]',lens.');
V = zeros(size(mask));
V(mask) = R;
Vt = V.';
sort_idx = Vt(mask.');
outX = X(sort_idx,:);
outY = Y(sort_idx,:);
Sample run -
1) Inputs :
>> X
X =
1 8 3
4 2 6
7 8 9
2 3 4
1 4 6
>> Y
Y =
2
1
2
3
1
2) Outputs from the two approaches :
>> [NewX,NewY] = interleave_cumsum_diff(X,Y)
NewX =
4 2 6
1 8 3
2 3 4
1 4 6
7 8 9
NewY =
1
2
3
1
2
>> [NewX,NewY] = interleave_bsxfuns(X,Y)
NewX =
4 2 6
1 8 3
2 3 4
1 4 6
7 8 9
NewY =
1
2
3
1
2

Resources