MATLAB: detect and remove mirror imaged pairs in 2 column matrix - arrays

I have a matrix
[1 2
3 6
7 1
2 1]
and would like to remove mirror imaged pairs..i.e. output would be either:
[1 2
3 6
7 1]
or
[3 6
7 1
2 1]
Is there a simple way to do this? I can imagine a complicated for loop, something like (or a version which wouldn't delete the original pair..only the duplicates):
for i=1:y
var1=(i,1);
var2=(i,2);
for i=1:y
if array(i,1)==var1 && array(i,2)==var2 | array(i,1)==var2 && array(i,2)==var1
array(i,1:2)=[];
end
end
end
thanks

How's this for simplicity -
A(~any(tril(squeeze(all(bsxfun(#eq,A,permute(fliplr(A),[3 2 1])),2))),2),:)
Playing code-golf? Well, here we go -
A(~any(tril(pdist2(A,fliplr(A))==0),2),:)
If dealing with two column matrices only, here's a simpler version of bsxfun -
M = bsxfun(#eq,A(:,1).',A(:,2)); %//'
out = A(~any(tril(M & M.'),2),:)
Sample run -
A =
1 2
3 6
7 1
6 5
6 3
2 1
3 4
>> A(~any(tril(squeeze(all(bsxfun(#eq,A,permute(fliplr(A),[3 2 1])),2))),2),:)
ans =
1 2
3 6
7 1
6 5
3 4
>> A(~any(tril(pdist2(A,fliplr(A))==0),2),:)
ans =
1 2
3 6
7 1
6 5
3 4

Here a not so fancy, but hopefully understandable and easy way.
% Example matrix
m = [1 2; 3 6 ; 7 1; 2 1; 0 3 ; 3 0];
Comparing m with its flipped version, the function ismember returns mirror_idx, a 1D-vector with each row containing the index of the mirror-row, or 0 if there's none.
[~, mirror_idx] = ismember(m,fliplr(m),'rows');
Go through the indices of the mirror-rows. If you find one "mirrored" row (mirror_idx > 0), set its counter-part to "not mirrored".
for ii = 1:length(mirror_idx)
if (mirror_idx(ii) > 0)
mirror_idx(mirror_idx(ii)) = 0;
end
end
Take only the rows that are marked as not having a mirror.
m_new = m(~mirror_idx,:);
Greetings

Related

BetaCV in matlab

I have a matrix indices with 2 columns and 20 rows.
indices =
[1 2;
2 3;
2 1;
... ]
there is a second matrix distMat with 4 rows and 4 columns and i want to find the sum of elements in distMat located in position given in each row of indices[]
distMat =
[1 3 1 5
2 2 4 2
3 8 3 7
3 8 3 7]
since indices rows are 1 2, 2 3, 3 1 so elements of that positions should be retrieved and added
so i wrote
result = sum(distMat[indices])
I am getting syntax error. so how to solve this problem
Another approach: use sparse to build a logical index that selects the values to be summed:
indices = [1 2; 2 3; 2 1];
distMat = [1 3 1 5; 2 2 4 2; 3 8 3 7; 3 8 3 7];
result = sum(distMat((sparse(indices(:,1), indices(:,2), true, size(distMat,1), size(distMat,2)))));
This works in Octave as well.
One way would be to get the linear indices and then simply index and sum, just like you had at the end -
idx = sub2ind(size(distMat), indices(:,1), indices(:,2));
out = sum(distMat(idx))
Sample run -
>> indices
indices =
1 2
2 3
2 1
>> distMat
distMat =
1 3 1 5
2 2 4 2
3 8 3 7
3 8 3 7
>> idx = sub2ind(size(distMat), indices(:,1), indices(:,2));
>> distMat(idx)
ans =
3
4
2
>> sum(distMat(idx))
ans =
9

Ordered random numbers in Matlab

I am trying to generate random numbers between 1 and 5 using Matlab's randperm and calling randperm = 5.
Each time this gives me a different array let's say for example:
x = randperm(5)
x = [3 2 4 1 5]
I need the vector to be arranged such that 4 and 5 are always next to each other and 2 is always between 1 and 3... so for e.g. [3 2 1 4 5] or [4 5 1 2 3].
So essentially I have two "blocks" of unequal length - 1 2 3 and 4 5. The order of the blocks is not so important, just that 4 & 5 end up together and 2 in between 1 and 3.
I can basically only have 4 possible combinations:
[1 2 3 4 5]
[3 2 1 4 5]
[4 5 1 2 3]
[4 5 3 2 1]
Does anyone know how I can do this?
Thanks
I'm not sure if you want a solution that would somehow generalize to a larger problem, but based on how you've described your problem above it looks like you are only going to have 8 possible combinations that satisfy your constraints:
possible = [1 2 3 4 5; ...
1 2 3 5 4; ...
3 2 1 4 5; ...
3 2 1 5 4; ...
4 5 1 2 3; ...
5 4 1 2 3; ...
4 5 3 2 1; ...
5 4 3 2 1];
You can now randomly select one or more of these rows using randi, and can even create an anonymous function to do it for you:
randPattern = #(n) possible(randi(size(possible, 1), [1 n]), :)
This allows you to select, for example, 5 patterns at random (one per row):
>> patternMat = randPattern(5)
patternMat =
4 5 3 2 1
3 2 1 4 5
4 5 3 2 1
1 2 3 5 4
5 4 3 2 1
You can generate each block and shuffle each one then and set them as members of a cell array and shuffle the cell array and finally convert the cell array to a vector.
b45=[4 5]; % block 1
b13=[1 3]; % block 2
r45 = randperm(2); % indices for shuffling block 1
r13 = randperm(2); % indices for shuffling block 2
r15 = randperm(2); % indices for shuffling the cell
blocks = {b45(r45) [b13(r13(1)) 2 b13(r13(2))]}; % shuffle each block and set them a members of a cell array
result = [blocks{r15}] % shuffle the cell and convert to a vector

Efficient "window-select" array blocks?

Suppose I have the following array:
x = [a b
c d
e f
g h
i j];
I want to "swipe a window of two rows" progressively (one row at a time) along the array to generate the following array:
y = [a b c d e f g h
c d e f g h i j];
What is the most efficient way to do this? I don't want to use cellfun or arrayfun or for loops.
im2col is going to be your best bet here if you have the Image Processing Toolbox.
x = [1 2
3 4
5 6
7 8];
im2col(x.', [1 2])
% 1 2 3 4 5 6
% 3 4 5 6 7 8
If you don't have the Image Processing Toolbox, you can also easily do this with built-ins.
reshape(permute(cat(3, x(1:end-1,:), x(2:end,:)), [3 2 1]), 2, [])
% 1 2 3 4 5 6
% 3 4 5 6 7 8
This combines the all rows with the next row by concatenating a row-shifted version along the third dimension. Then we use permute to shift the dimensions around and then we reshape it to be the desired size.
If you don't have the Image Processing Toolbox, you can do this using simple indexing:
x =
1 2
3 4
5 6
7 8
9 10
y = x.'; %% Transpose it, for simplicity
z = [y(1:end-2); y(3:end)] %% Take elements 1:end-2 and 3:end and concatenate them
z =
1 2 3 4 5 6 7 8
3 4 5 6 7 8 9 10
You can do the transposing and reshaping in a simple step (see Suever's edit), but the above might be easier to read, understand and debug for beginners.
Here's an approach to solve it for a generic case of selecting L rows per window -
[m,n] = size(x) % Store size
% Extend rows by indexing into them with a progressive array of indices
x_ext = x(bsxfun(#plus,(1:L)',0:m-L),:);
% Split the first dim at L into two dims, out of which "push" back the
% second dim thus created as the last dim. This would bring in the columns
% as the second dimension. Then, using linear indexing reshape into the
% desired shape of L rows for output.
out = reshape(permute(reshape(x_ext,L,[],n),[1,3,2]),L,[])
Sample run -
x = % Input array
9 1
3 1
7 5
7 8
4 9
6 2
L = % Window length
3
out =
9 1 3 1 7 5 7 8
3 1 7 5 7 8 4 9
7 5 7 8 4 9 6 2

Removing zeros and then vertically collapse the matrix

In MATLAB, say I have a set of square matrices, say A, with trace(A)=0 as follows:
For example,
A = [0 1 2; 3 0 4; 5 6 0]
How can I remove the zeros and then vertically collapse the matrix to become as follow:
A_reduced = [1 2; 3 4; 5 6]
More generally, what if the zeroes can appear anywhere in the column (i.e., not necessarily at the long diagonal)? Assuming, of course, that the total number of zeros for all columns are the same.
The matrix can be quite big (hundreds x hundreds in dimension). So, an efficient way will be appreciated.
To compress the matrix vertically (assuming every column has the same number of zeros):
A_reduced_v = reshape(nonzeros(A), nnz(A(:,1)), []);
To compress the matrix horizontally (assuming every row has the same number of zeros):
A_reduced_h = reshape(nonzeros(A.'), nnz(A(1,:)), []).';
Case #1
Assuming that A has equal number of zeros across all rows, you can compress it horizontally (i.e. per row) with this -
At = A' %//'# transpose input array
out = reshape(At(At~=0),size(A,2)-sum(A(1,:)==0),[]).' %//'# final output
Sample code run -
>> A
A =
0 3 0 2
3 0 0 1
7 0 6 0
1 0 6 0
0 16 0 9
>> out
out =
3 2
3 1
7 6
1 6
16 9
Case #2
If A has equal number of zeros across all columns, you can compress it vertically (i.e. per column) with something like this -
out = reshape(A(A~=0),size(A,1)-sum(A(:,1)==0),[]) %//'# final output
Sample code run -
>> A
A =
0 3 7 1 0
3 0 0 0 16
0 0 6 6 0
2 1 0 0 9
>> out
out =
3 3 7 1 16
2 1 6 6 9
This seems to work, quite fiddly to get the behaviour right with transposing:
>> B = A';
>> C = B(:);
>> reshape(C(~C==0), size(A) - [1, 0])'
ans =
1 2
3 4
5 6
As your zeros are always in the main diagonal you can do the following:
l = tril(A, -1);
u = triu(A, 1);
out = l(:, 1:end-1) + u(:, 2:end)
A correct and very simple way to do what you want is:
A = [0 1 2; 3 0 4; 5 6 0]
A =
0 1 2
3 0 4
5 6 0
A = sort((A(find(A))))
A =
1
2
3
4
5
6
A = reshape(A, 2, 3)
A =
1 3 5
2 4 6
I came up with almost the same solution as Mr E's though with another reshape command. This solution is more universal, as it uses the number of rows in A to create the final matrix, instead of counting the number of zeros or assuming a fixed number of zeros..
B = A.';
B = B(:);
C = reshape(B(B~=0),[],size(A,1)).'

Matlab- moving numbers to new row if condition is met

I have a variable like this that is all one row:
1 2 3 4 5 6 7 8 9 2 4 5 6 5
I want to write a for loop that will find where a number is less than the previous one and put the rest of the numbers in a new row, like this
1 2 3 4 5 6 7 8 9
2 4 5 6
5
I have tried this:
test = [1 2 3 4 5 6 7 8 9 2 4 5 6 5];
m = zeros(size(test));
for i=1:numel(test)-1;
for rows=1:size(m,1)
if test(i) > test(i+1);
m(i+1, rows+1) = test(i+1:end)
end % for rows
end % for
But it's clearly not right and just hangs.
Let x be your data vector. What you want can be done quite simply as follows:
ind = [find(diff(x)<0) numel(x)]; %// find ends of increasing subsequences
ind(2:end) = diff(ind); %// compute lengths of those subsequences
y = mat2cell(x, 1, ind); %// split data vector according to those lenghts
This produces the desired result in cell array y. A cell array is used so that each "row" can have a different number of columns.
Example:
x = [1 2 3 4 5 6 7 8 9 2 4 5 6 5];
gives
y{1} =
1 2 3 4 5 6 7 8 9
y{2} =
2 4 5 6
y{3} =
5
If you are looking for a numeric array output, you would need to fill the "gaps" with something and filling with zeros seem like a good option as you seem to be doing in your code as well.
So, here's a bsxfun based approach to achieve the same -
test = [1 2 3 4 5 6 7 8 9 2 4 5 6 5] %// Input
idx = [find(diff(test)<0) numel(test)] %// positions of row shifts
lens = [idx(1) diff(idx)] %// lengths of each row in the proposed output
m = zeros(max(lens),numel(lens)) %// setup output matrix
m(bsxfun(#le,[1:max(lens)]',lens)) = test; %//'# put values from input array
m = m.' %//'# Output that is a transposed version after putting the values
Output -
m =
1 2 3 4 5 6 7 8 9
2 4 5 6 0 0 0 0 0
5 0 0 0 0 0 0 0 0

Resources