Sort 3D matrix depending on specific column - matlab

I have 3D matrix (10*10*4) and I am trying to short each slice depending on the 2 column after that and depending on column 2 I want to delete all rows that have a value on column 2 less than 1 and greater than 17.
I used this code to sort but it is not working
clc;
clear;
A = rand(10,10,4)
column = 2;
[values,indices] = sort(A(:,column,:))
B = A(indices,:,:)
Thanks

Here is the code for sorting.
clc;
clear all;
A = rand(10,10,4);
column = 2;
z=A(:,column,:);
[values,indices] = sort(z,1) ;
B=zeros(size(A,1),size(A,2),size(A,3));
for i=1:size(A,3)
B(:,:,i) =A(indices(:,:,i),:,i);
end
In here all values in A and B are between 0 and 1. So no point in checking less than 1 and greater than 17. Another thing is that when you delete a row, what are you going to do with the third dimension? If you do the deletion for each third dimension separately B(:,:,1), B(:,:,2), B(:,:,3), B(:,:,4). Each dimension B(:,:,1), B(:,:,2), B(:,:,3), B(:,:,4) will have different number of rows. B cant have different number of rows like that.
Updated: code with sorting and delete rows when all values in 2nd column of that row is less than 0.05 or all values in 2nd column of that row is greater than 0.15
clc;
clear all;
A = rand(10,10,4);
%% sorting
column = 2; % sort base on column
z=A(:,column,:);
[values,indices] = sort(z,1) ;
B=zeros(size(A,1),size(A,2),size(A,3));
for i=1:size(A,3)
B(:,:,i) =A(indices(:,:,i),:,i);
end
%% deleting row
C=B;
column=2; % delete base on column
rowSize=size(C,1);
i=1;
while true
count=0;
count1=0;
for j=1:size(C,3)
if(C(i,column,j)< 0.05)
count=count+1;
end
if(C(i,column,j)> 0.15)
count1=count1+1;
end
end
if (count==size(C,3) || count1==size(C,3))
C(i,:,:)=[];
rowSize=rowSize-1;
else
i=i+1;
end
if (i>rowSize)
break;
end
end
Updated: code with sorting and delete rows when any values in 2nd column of that row is less than 0.05 or any values in 2nd column of that row is greater than 0.15
%% deleting row
C=B;
column=2; % delete base on column
rowSize=size(C,1);
i=1;
while true
flag=0;
for j=1:size(C,3)
if(C(i,column,j)< 0.05 || C(i,column,j)> 0.15)
flag=1;
break;
end
end
if (flag==1)
C(i,:,:)=[];
rowSize=rowSize-1;
else
i=i+1;
end
if (i>rowSize)
break;
end
end

Related

delete elements from a matrix and calculate mean

I have an N-by-M-Matrix as input called GR wich consists of the following numbers: -3,0,2,4,7,10,12
And I have to return a vector. If M=1, then it should just return the input.
If M>1 It should remove the lowest number from the matrix and then calculate the mean of the remaining numbers.
However, if one of the numbers in the row is -3, it should return the value -3 in the output.
My thoughts of the problem:
Is it possible to make a for loop?
for i=1:length(GR(:,1))
If length(GR(1,:))==1
GR=GR
end
If length(GR(1,:))>1
x=min(GR(i,:))=[] % for removing the lowest number in the row
GR=sum(x)/length(x(i,:))
I just don't have any Idea of how to detect if any of the numbers in the row is -3 and then return that value instead of calculating the mean and when I tried to delete the lowest number in the matrix using x=min(GR(i,:)) matlab gave me this error massage 'Deletion requires an existing variable.'
I put in a break function. As soon as it detects a -3 value it breaks from the loop. Same goes for the other function.
Note that it is an i,j (M*N) matrix. So you might need to change your loop.
for i=1:length(GR(:,1))
if GR(i,1)==-3
GR=-3
break
end
If length(GR(1,:))==1
GR=GR
break
end
If length(GR(1,:))>1
x=min(GR(i,:))=[] % for removing the lowest number in the row
GR=sum(x)/length(x(i,:))
end
end
you can use Nan's, nanmean, any, and dim argument in these functions:
% generate random matrix
M = randi(3);
N = randi(3);
nums = [-3,0,2,4,7,10,12];
GR = reshape(randsample(nums,N*M,true),[N M]);
% computation:
% find if GR has only one column
if size(GR,2) == 1
res = GR;
else
% find indexes of rows with -3 in them
idxs3 = any(GR == -3,2);
% the (column) index of the min. value in each row
[~,minCol] = min(GR,[],2);
% convert [row,col] index pair into linear index
minInd = sub2ind(size(GR),1:size(GR,1),minCol');
% set minimum value in each row to nan - to ignore it on averaging
GR(minInd) = nan;
% averaging each rows (except for the Nans)
res = nanmean(GR,2);
% set each row with (-3) in it to (-3)
res(idxs3) = -3;
end
disp(res)

arrange resulting loop row values into one array in matlab

Considering the fact that i have gotten some clarity on this current question, I have edited the code to conform with my current problem with the code. So now the problem i have with my code is that it does not loop any more. if i place a for loop and some conditions, the value of groups which is supposed to change with every loop does not therefore causing an error at k is 2 in the loop. The initial value of groups is gotten from the first part of the code which gets its value form a different array, but the for loop part of the code is supposed to use that initial groupsvalue and then continue changing from there. that is where the problem comes, as the value of groups refuses to change.
A = connections;
% Engine
tic
[m, n] = size(A);
groups = [];
ng = 0;
k=1;
w= 1:2:3;
u = unique(A(k,w)); % representation of kth row
[in, J] = ismember(A(k:end,:),u);
l = m-k+1;
r = repmat((1:l).', n, 1);
c = accumarray([r(in) J(in)],1,[l n]); % count
c = bsxfun(#min,c,c(1,:)); % clip
rows = sum(c,2)==2; % check if 2 elements are common
if any(rows)
ng = +1;
groups = (k-1) + [1; find(rows)];
end
gr=groups(end);
nwrry= [A(k,:);A(gr,:)];
for k=1:5
h=[];
h(k)= groups(end);
dff= setdiff(nwrry(end,:),nwrry(end-1,:));
[rw,cl]= find(nwrry==dff);
if cl==3
w=1:2:cl;
else
if cl==2
w=1:cl;
else
w=cl:2;
end
end
u = unique(A(h(k),w)); % representation of kth row
[in, J] = ismember(A(k:end,:),u);
l = m-k+1;
r = repmat((1:l).', n, 1);
c = accumarray([r(in) J(in)],1,[l n]); % count
c = bsxfun(#min,c,c(1,:)); % clip
rows = sum(c,2)==2; % check if 2 elements are common
if any(rows)
ng = ng+1;
groups = (k-1) + [1; find(rows)];
end
nwrry = [nwrry;A(groups(end),:)]
k=k+1
end
connections is the 800 by 3 array. if you want to test it and see what it gives as a result please make sure the array has at most 2 values in a row common with another row.
I have been able to answer this question. Thank you all who have commented with your advices and help because i did look into what you all said and been able to come up with something worthwhile.
The complete code is below;
%% call connections
A= connections;
[m, n] = size(A);%% calculate number of rows (m) and columns(n) in A
%% find rows with similar values as row 1
[a, b]=find(A(1:end,:) == A(1,1));%% Find rows with similar value as the first value of row 1
[a1, b1]=find(A(1:end,:) == A(1,2));%% Find rows with similar value as the second value of row 1
[a2, b2]=find(A(1:end,:) == A(1,3));%% Find rows with similar value as the third value of row 1
%% find the rows with atleast 2 values similar to row 1
int1= intersect(a,a1);%% Find same row numbers between a and a1
int2= intersect(a,a2);%% Find same row numbers between a and a2
int3= intersect(a1,a2);%% Find same row numbers between a1 and a2
ints=[int1;int2;int3];%% Place all intersections in one array(ints)
%% delete row number that is same with row number 1
ints= unique(ints);%% delete repeated row numbers
[Lia,Locb] = ismember(ints(1:end,:),1);%% Find same row numbers same with row number 1
[z, x]=find(Lia(1:end,:) == 1);%% Find row numbers equal to 1 in Lia
ints(z(1:end),:)=[];%% delete row numbers that is same with row number 1
%% place the searched row number and the similar row number in an array(gr)
gr=1;%% Place initial row number(1) in one array(gr)
tg=[a;a1;a2];%% Place all row numbers with similar value as row 1 in one array(tg)
if isempty(ints)%% conditions when ints is empty
f= setdiff(tg,gr);%% Find row numbers in tg not present in gr
gr=[gr;f(1,:)];%% add the similar row number in array gr
else %% conditions when ints is NOT empty
gr=[gr;ints(end)]; %% add the similar row number in array gr
end
%% place the searched row and the similar row in an array(nwrry)
nwrry= [A(1,:);A(gr(end),:)];
%% Create loop for all rows in A and repeat all processes above
for i=1:m-2
hrry=[];
hrry(i)= gr(end);
[a, b]=find(A(1:end,:) == A(hrry(end),1));
[a1, b1]=find(A(1:end,:) == A(hrry(end),2));
[a2, b2]=find(A(1:end,:) == A(hrry(end),3));
int1= intersect(a,a1);
int2= intersect(a,a2);
int3= intersect(a1,a2);
ints=[int1;int2;int3];
ints= unique(ints);
[Lia,Locb] = ismember(ints(1:end,:),gr(1:end,:));
[z, x]=find(Lia(1:end,:) == 1);
ints(z(1:end),:)=[];
tg=[a;a1;a2];
if isempty(ints)
f= setdiff(tg,gr);
if isempty(f)
Anb=1:m;
Anbc=Anb';
nwf= setdiff(Anbc,gr);
gr=[gr;nwf(1,:)];
else
gr=[gr;f(1,:)];
end
else
gr=[gr;ints(end)];
end
nwrry= [nwrry;A(gr(end),:)];
end

Matlab: nested loops with if conditional

I am trying to build a submatrix by iterating on the rows of the last column in the main matrix which is of size (50000, 21), and if the value inside is greater than 1000 then the whole corresponding y(i,:) row will be added to the new submatrix and add to it more 33 consecutive rows from the main matrix.
I wrote this code below but it is returning only a 33x21 size and I think i am iterating through the j index but not the i index:
for i=1:50000
if y(i,21)>1000
for j=1:33
n(j,:)=y(j+i,:)
j=j+1
end
i=i+1
end
end
What am I doing wrong?
try this:
k=1;
for i=1:50000
if y(i,21)>1000
for j=0:33 %watch this out i am not sure how many rows you want to keep and if these involve the one with the last element greater than 1000 or not..%
n(k,:)=y(j+i,:);
j=j+1;
k=k+1;
end
i=i+1;
end
end
Problem is that every new set of 33 rows overwrites the previous one.The above code i think will do the trick.
this is a vectorized verison that needs no loop:
%find elements that are greater than 1000
lo = find(y(:,21) > 1000);
%indeices of 33th row after the lo,
%since it may take a value that is greater than the size of matrix we limit this value by min
up = min(size(y,1), lo + 33);
% create indices based on ranges
%see http://stackoverflow.com/a/39434045/6579744
index=cumsum(accumarray(cumsum([1;up(:)-lo(:)+1]),[lo(:);0]-[0;up(:)]-1)+1);
index= index(1:end-1);
n = y(index, :)

Vectorize MATLAB code

Let's say we have three m-by-n matrices of equal size: A, B, C.
Every column in C represents a time series.
A is the running maximum (over a fixed window length) of each time series in C.
B is the running minimum (over a fixed window length) of each time series in C.
Is there a way to determine T in a vectorized way?
[nrows, ncols] = size(A);
T = zeros(nrows, ncols);
for row = 2:nrows %loop over the rows (except row #1).
for col = 1:ncols %loop over the columns.
if C(row, col) > A(row-1, col)
T(row, col) = 1;
elseif C(row, col) < B(row-1, col)
T(row, col) = -1;
else
T(row, col) = T(row-1, col);
end
end
end
This is what I've come up with so far:
T = zeros(m, n);
T(C > circshift(A,1)) = 1;
T(C < circshift(B,1)) = -1;
Well, the trouble was the dependency with the ELSE part of the conditional statement. So, after a long mental work-out, here's a way I summed up to vectorize the hell-outta everything.
Now, this approach is based on mapping. We get column-wise runs or islands of 1s corresponding to the 2D mask for the ELSE part and assign them the same tags. Then, we go to the start-1 along each column of each such run and store that value. Finally, indexing into each such start-1 with those tagged numbers, which would work as mapping indices would give us all the elements that are to be set in the new output.
Here's the implementation to fulfill all those aspirations -
%// Store sizes
[m1,n1] = size(A);
%// Masks corresponding to three conditions
mask1 = C(2:nrows,:) > A(1:nrows-1,:);
mask2 = C(2:nrows,:) < B(1:nrows-1,:);
mask3 = ~(mask1 | mask2);
%// All but mask3 set values as output
out = [zeros(1,n1) ; mask1 + (-1*(~mask1 & mask2))];
%// Proceed if any element in mask3 is set
if any(mask3(:))
%// Row vectors for appending onto matrices for matching up sizes
mask_appd = false(1,n1);
row_appd = zeros(1,n1);
%// Get 2D mapped indices
df = diff([mask_appd ; mask3],[],1)==1;
cdf = cumsum(df,1);
offset = cumsum([0 max(cdf(:,1:end-1),[],1)]);
map_idx = bsxfun(#plus,cdf,offset);
map_idx(map_idx==0) = 1;
%// Extract the values to be used for setting into new places
A1 = out([df ; false(1,n1)]);
%// Map with the indices obtained earlier and set at places from mask3
newval = [row_appd ; A1(map_idx)];
mask3_appd = [mask_appd ; mask3];
out(mask3_appd) = newval(mask3_appd);
end
Doing this vectorized is rather difficult because the current row's output depends on the previous row's output. Doing vectorized operations usually means that each element should stand out on its own using some relationship that is independent of the other elements that surround it.
I don't have any input on how you would achieve this without a for loop but I can help you reduce your operations down to one instead of two. You can do the assignment vectorized per row, but I can't see how you'd do it all in one shot.
As such, try something like this instead:
[nrows, ncols] = size(A);
T = zeros(nrows, ncols);
for row = 2:nrows
out = T(row-1,:); %// Change - Make a copy of the previous row
out(C(row,:) > A(row-1,:)) = 1; %// Set those elements of C
%// in the current row that are larger
%// than the previous row of A to 1
out(C(row,:) < B(row-1,:)) = -1; %// Same logic but for B now and it's
%// less than and the value is -1 instead
T(row,:) = out; %// Assign to the output
end
I'm currently figuring out how to do this with any loops whatsoever. I'll keep you posted.

break the for loops in matlab

I have written a matlab code and want to terminate the code after finding the first one in the matrix. BUT, it continues until the last one and then break works. What is the problem?
% The code is as follows:
for i=1:n
for j=1:m
if (bw(i,j)==1) % element with value one
p1(1)=i;p1(2)=j;
% fprintf('value of a: %d\n', i ,j)
sprintf('Found first one in the loop!!')
break; % terminate
end
end
end
It looks like you want to find the first 1 value in a matrix, searching in row-major order (increase column index first, then row index). You can do that without loops using the find function:
M = [0 1; 1 1]; %// example matrix
v = 1; %// value you want to find
[col, row] = find(M.'==v, 1);
Note that the matrix is transposed and the order of the two outputs of find is reversed. This is because Matlab finds elements in column-major order (increase row index first, then column index).
In the above example, with
M =
0 1
1 1
the result is
row =
1
col =
2
Since break exits only the inner loop (the one that runs with j) but not the outer loop (the one that runs with i), you can tackle the problem by using a simple boolean which you initially set as false.
When you break the inner loop, change it to true and then check (in the outer loop) if such boolean is true. If it is, also break the outer loop.
IWantToBreak=false;
for i=1:n
for j=1:m
if (bw(i,j)==1) % element with value one
p1(1)=i;p1(2)=j;
% fprintf('value of a: %d\n', i ,j)
sprintf('Found first one in the loop!!')
IWantToBreak=true;
break; % terminate
end
end
if IWantToBreak==true
break;
end
end