Replace rows having at least one duplicate number or one zero by a user defined row - matlab

I have a matrix having rows with repeated numbers.
A= [ 2 3 6;
4 7 4;
8 7 2;
1 3 1;
7 8 2 ]
The codes below find those rows and replace them with a Dummy_row [1 2 3]
new_A=[ 2 3 6;
1 2 3;
8 7 2;
1 2 3;
7 8 2 ]
This are the codes:
CODE NUMBER 1 (#Bruno)
Dummy_row = [1 2 3];
b = any(~diff(sort(A,2),1,2),2);
A(b,:) = repmat(Dummy_row,sum(b),1)
CODE NUMBER 2 (#Kamtal)
Dummy_row = [1 2 3];
b = diff(sort(A,2),1,2);
b = sum(b == 0,2);
b = b > 0;
c = repmat(Dummy_row,sum(b),1);
b = b' .* (1:length(b));
b = b(b > 0);
newA = A;
newA(b,:) = c
Note: both codes Number 1 and 2 perform the task efficiently.
Question
How can this code(either code num 1 or num 2) be modified such that it also replaces any rows having at least one zero with the Dummy_row?

Code 1
b = any(~diff(sort(A,2),1,2),2) | any(A==0,2); % <-- Only change
A(b,:) = repmat(Dummy_row,sum(b),1);
Code 2
b = diff(sort(A,2),1,2);
b = sum(b == 0,2);
b = (b > 0) | any(A==0,2); % <-- Only change
c = repmat(Dummy_row,sum(b),1);
b = b' .* (1:length(b));
b = b(b > 0);
newA = A;
newA(b,:) = c;
By the way: Code1 basically does the same thing that Code2 does, just that it uses logical-indexing instead of doing the unnecessary conversion from logical indexes to index positions.

Related

How to generate matrix in MATLAB where values decrease with increasing coordinates

I'm trying to generate an n x n matrix like
5 4 3 2 1
4 4 3 2 1
3 3 3 2 1
2 2 2 2 1
1 1 1 1 1
where n = 5 or n 50. I'm at an impasse and can only generate a portion of the matrix. It is Problem 2.14 from Numerical Methods using MATLAB 3rd Edition by Penny and Lindfield. This is the best I have so far:
n = 5;
m = n;
A = zeros(m,n);
for i = 1:m
for j = 1:n
A(i,j) = m;
end
m = m - 1;
end
Any feedback is appreciated.
That was a nice brain-teaser, here’s my solution:
[x,y] = meshgrid(5:-1:1);
out = min(x,y)
Output:
ans =
5 4 3 2 1
4 4 3 2 1
3 3 3 2 1
2 2 2 2 1
1 1 1 1 1
Here's one loop-based approach:
n = 5;
m = n;
A = zeros(m, n);
for r = 1:m
for c = 1:n
A(r, c) = n+1-max(r, c);
end
end
And here's a vectorized approach (probably not faster, just for fun):
n = 5;
A = repmat(n:-1:1, n, 1);
A = min(A, A.');
That's one of the matrices in Matlab's gallery, except that it needs a 180-degree rotation, which you can achieve with rot90:
n = 5;
A = rot90(gallery('minij', n), 2);

Dynamic window forming in efficient way in MATLAB

Can someone help me to provide an efficient way or help me to perform the provide code to do make same results in minimal possible steps. I shall be grateful to you.
I have an Original Array:
A = [1 1 1 4.3 4.5 4 4.3 3 1 0 0 2 6.2 6.3 6 6.2 7.4 8 7.2 2 2 3 3 2];
Output Looks like:
A = [1 1 1 4 4 4 4 3 1 0 0 2 6 6 6 6 6 7 7 2 2 3 3 2];
I apply some restrictions and removed some values from array of local maxima’s after that I received some new arrays.
Yposlocfiltered = [6 15 23];
idx = [4 6 3];
Yneglocfiltered = [2 9 20];
idx_neg = [1 1 2];
Where I will find idx(local maxima value) I will check if values behind and ahead are greater make a window.
Example
If I will find 4 and 4.5, 4.3 is greater than 4 include it in backward window
4.3 is greater than 4 include it in forward window.
I need to find range of values behind local maxima and ahead local maxima.
I have tried to write a code that’s works fine but issue is that it’s too long.
Can someone please provide me an idea to perform this action in minimal steps and in faster ways?
I have only provided code for positive local maxima’s as for negative local maxima code Is just replica of this.
Code:only for positive local maximas
clc
clear all
A = [1 0 1 4.3 4.5 5 4.3 3 0 0 0 2 6.2 6.3 7 6.2 7.4 8 7.2 1 2 3 4 2];
Yposlocfiltered = [ 6 15 23];
idx = [4 6 3];
Yneglocfiltered = [2 9 20];
idx_neg = [1 1 2];
for b = 1: numel(idx)
for c = 1:numel(A)
f = Yposlocfiltered(1,b)-c ;
g = Yposlocfiltered(1,b)+c ;
% if (f > 0 && g <= numel(X))
if(f > 0)
if (A(Yposlocfiltered(1,b)-c))> idx(1,b)
else
d= f+1;
z(b)= d;
backward_window = z;
break
end
end
end
end
for r = 1: numel(idx)
for s = 1:numel(A)
u = Yposlocfiltered(1,r)-s ;
v = Yposlocfiltered(1,r)+s ;
% if (f > 0 && g <= numel(X))
if(v <=numel(A))
if (A(Yposlocfiltered(1,r)+s))> idx(1,r)
else
w= v-1;
y(r)= w;
forward_window = y;
break
end
end
end
end
n=4
for i=1:length(backward_window)
range = backward_window(i): forward_window(i);
p = range
if n <= numel(p)
p = range(1:n)
A( p) = idx(i);
else
% if (size(range)<= 3)
A( p) = idx(i);
end
end
From the first look at your code, I believe you can combine your first two for loops into one.
sA = numel(A);
sI = numel(idx);
for i = 1:sI
f = Yposlocfiltered(i) - [1:sA];
g = Yposlocfiltered(i) + [1:sA];
f(f<1) = [];
g(g>sA) = [];
backward_window(i) = f(find(A(f) <= idx(i), 1)) + 1;
forward_window(i) = g(find(A(g) <= idx(i), 1)) - 1;
end
Here, you can use find to locate the element of an array matching the specified condition, i.e. g <= numel(X) or A(f) <= idx(i).
Your last loop which modifies A can also be integrated into the same loop, so you can have:
sA = numel(A);
sI = numel(idx);
n=4;
for i = 1:sI
f = Yposlocfiltered(i) - [1:sA];
g = Yposlocfiltered(i) + [1:sA];
f(f<1) = [];
g(g>sA) = [];
backward_window(i) = f(find(A(f) <= idx(i), 1)) + 1;
forward_window(i) = g(find(A(g) <= idx(i), 1)) - 1;
range = backward_window(i) : forward_window(i);
if n <= numel(range)
A(range(1:n)) = idx(i);
else
A(range) = idx(i);
end
end

Find and replace the rows of an array having repeated number by a fixed given row

I have a matrix having rows with repeated numbers. I want to find those rows and replace them with a dummy row so as to keep the number of rows of the matrix constant.
Dummy_row = [1 2 3]
(5x3) Matrix A
A = [2 3 6;
4 7 4;
8 7 2;
1 3 1;
7 8 2]
(5x3) Matrix new_A
new_A = [2 3 6;
1 2 3;
8 7 2;
1 2 3;
7 8 2]
I tried the following which deleted the rows having repeated numbers.
y = [1 2 3]
w = sort(A,2)
v = all(diff(t,1,2)~=0|w(:,1:2)==0,2) % When v is zero, the row has repeated numbers
z = A(w,:)
Can you please help?
bsxfun based solution -
%// Create a row mask of the elements that are to be edited
mask = any(sum(bsxfun(#eq,A,permute(A,[1 3 2])),2)>1,3);
%// Setup output variable and set to-be-edited rows as copies of [1 2 3]
new_A = A;
new_A(mask,:) = repmat(Dummy_row,sum(mask),1)
Code run -
A =
2 3 6
4 7 4
8 7 2
1 3 1
7 8 2
new_A =
2 3 6
1 2 3
8 7 2
1 2 3
7 8 2
You could use the following:
hasRepeatingNums = any(diff(sort(A, 2), 1, 2)==0, 2);
A(hasRepeatingNums,:) = repmat(Dummy_row, nnz(hasRepeatingNums), 1);
See if this works for you,
A= [ 2 3 6;
4 7 4;
8 7 2;
5 5 5;
1 8 8;
1 3 1;
7 8 2 ];
Dummy_row = [1 2 3];
b = diff(sort(A,2),1,2);
b = sum(b == 0,2);
b = b > 0;
c = repmat(Dummy_row,sum(b),1);
b = b' .* (1:length(b));
b = b(b > 0);
newA = A;
newA(b,:) = c;
gives,
newA =
2 3 6
1 2 3
8 7 2
1 2 3
1 2 3
1 2 3
7 8 2
Edit
Not much change is needed, try this,
Dummy_row = [1 2 3];
b = sum(A == 0,2);
b = b > 0;
c = repmat(Dummy_row,sum(b),1);
b = b' .* (1:length(b));
b = b(b > 0);
newA = A;
newA(b,:) = c;

Making a match-and-append code more efficient without 'for' loop

I am trying to match 1st column of A with 1st to 3rd columns of B, and append corresponding 4th column of B to A.
For example,
A=
1 2
3 4
B=
1 2 4 5 4
1 2 3 5 3
1 1 1 1 2
3 4 5 6 5
I compare A(:,1) and B(:, 1:3)
1 and 3 are in A(:,1)
1 is in the 1st, 2nd, 3rd rows of B(:, 1:3), so append B([1 2 3], 4:end)' to A's 1st row.
3 is in the 2nd and 4th rows of B(:,1:3), so append B([2 4], 4:end)' to A's 2nd row.
So that it becomes:
1 2 5 4 5 3 1 2
3 4 5 3 6 5 0 0
I could code this using only for and if.
clearvars AA A B mem mem2 mem3
A = [1 2 ; 3 4]
B = [1 2 4 5 4; 1 2 3 5 3; 1 1 1 1 2; 3 4 5 6 5]
for n=1:1:size(A,1)
mem = ismember(B(:,[1:3]), A(n,1));
mem2 = mem(:,1) + mem(:,2) + mem(:,3);
mem3 = find(mem2>0);
AA{n,:} = horzcat( A(n,:), reshape(B(mem3,[4,5])',1,[]) ); %'
end
maxLength = max(cellfun(#(x)numel(x),AA));
out = cell2mat(cellfun(#(x)cat(2,x,zeros(1,maxLength-length(x))),AA,'UniformOutput',false))
I am trying to make this code efficient, by not using for and if, but couldn't find an answer.
Try this
a = A(:,1);
b = B(:,1:3);
z = size(b);
b = repmat(b,[1,1,numel(a)]);
ab = repmat(permute(a,[2,3,1]),z);
row2 = mat2cell(permute(sum(ab==b,2),[3,1,2]),ones(1,numel(a)));
AA = cellfun(#(x)(reshape(B(x>0,4:end)',1,numel(B(x>0,4:end)))),row2,'UniformOutput',0);
maxLength = max(cellfun(#(x)numel(x),AA));
out = cat(2,A,cell2mat(cellfun(#(x)cat(2,x,zeros(1,maxLength-length(x))),AA,'UniformOutput',false)))
UPDATE Below code runs in almost same time as the iterative code
a = A(:,1);
b = B(:,1:3);
z = size(b);
b = repmat(b,[1,1,numel(a)]);
ab = repmat(permute(a,[2,3,1]),z);
df = permute(sum(ab==b,2),[3,1,2])';
AA = arrayfun(#(x)(B(df(:,x)>0,4:end)),1:size(df,2),'UniformOutput',0);
AA = arrayfun(#(x)(reshape(AA{1,x}',1,numel(AA{1,x}))),1:size(AA,2),'UniformOutput',0);
maxLength = max(arrayfun(#(x)(numel(AA{1,x})),1:size(AA,2)));
out2 = cell2mat(arrayfun(#(x,i)((cat(2,A(i,:),AA{1,x},zeros(1,maxLength-length(AA{1,x}))))),1:numel(AA),1:size(A,1),'UniformOutput',0));
How about this:
%# example data
A = [1 2
3 4];
B = [1 2 4 5 4
1 2 3 5 3
1 1 1 1 2
3 4 5 6 5];
%# rename for clarity & reshape for algorithm's convenience
needle = permute(A(:,1), [2 3 1]);
haystack = B(:,1:3);
data = B(:,4:end).';
%# Get the relevant rows of 'haystack' for each entry in 'needle'
inds = any(bsxfun(#eq, haystack, needle), 2);
%# Create data that should be appended to A
%# All data and functionality in this loop is local and static, so speed
%# should be optimal.
append = zeros( size(A,1), numel(data) );
for ii = 1:size(inds,3)
newrow = data(:,inds(:,:,ii));
append(ii,1:numel(newrow)) = newrow(:);
end
%# Now append to A, stripping unneeded zeros
A = [A append(:, ~all(append==0,1))]

specific tuples generation and counting (matlab)

I need to generate (I prefere MATLAB) all "unique" integer tuples k = (k_1, k_2, ..., k_r) and
its corresponding multiplicities, satisfying two additional conditions:
1. sum(k) = n
2. 0<=k_i<=w_i, where vector w = (w_1,w_2, ..., w_r) contains predefined limits w_i.
"Unique" tuples means, that it contains unique unordered set of elements
(k_1,k_2, ..., k_r)
[t,m] = func(n,w)
t ... matrix of tuples, m .. vector of tuples multiplicities
Typical problem dimensions are about:
n ~ 30, n <= sum(w) <= n+10, 5 <= r <= n
(I hope that exist any polynomial time algorithm!!!)
Example:
n = 8, w = (2,2,2,2,2), r = length(w)
[t,m] = func(n,w)
t =
2 2 2 2 0
2 2 2 1 1
m =
5
10
in this case exist only two "unique" tuples:
(2,2,2,2,0) with multiplicity 5
there are 5 "identical" tuples with same set of elements
0 2 2 2 2
2 0 2 2 2
2 2 0 2 2
2 2 2 0 2
2 2 2 2 0
and
(2,2,2,1,1) with multiplicity 10
there are 10 "identical" tuples with same set of elements
1 1 2 2 2
1 2 1 2 2
1 2 2 1 2
1 2 2 2 1
2 1 1 2 2
2 1 2 1 2
2 1 2 2 1
2 2 1 1 2
2 2 1 2 1
2 2 2 1 1
Thanks in advance for any help.
Very rough (extremely ineffective) solution. FOR cycle over 2^nvec-1 (nvec = r*maxw) test samples and storage of variable res are really terrible things!!!
This solution is based on tho following question.
Is there any more effective way?
function [tup,mul] = tupmul(n,w)
r = length(w);
maxw = max(w);
w = repmat(w,1,maxw+1);
vec = 0:maxw;
vec = repmat(vec',1,r);
vec = reshape(vec',1,r*(maxw+1));
nvec = length(vec);
res = [];
for i = 1:(2^nvec - 1)
ndx = dec2bin(i,nvec) == '1';
if sum(vec(ndx)) == n && all(vec(ndx)<=w(ndx)) && length(vec(ndx))==r
res = [res; vec(ndx)];
end
end
tup = unique(res,'rows');
ntup = size(tup,1);
mul = zeros(ntup,1);
for i=1:ntup
mul(i) = size(unique(perms(tup(i,:)),'rows'),1);
end
end
Example:
> [tup mul] = tupmul(8,[2 2 2 2 2])
tup =
0 2 2 2 2
1 1 2 2 2
mul =
5
10
Or same case but with changed limits for first two positions:
>> [tup mul] = tupmul(8,[1 1 2 2 2])
tup =
1 1 2 2 2
mul =
10
This is far more better algorithm, created by Bruno Luong (phenomenal MATLAB programmer):
function [t, m, v] = tupmul(n, w)
v = tmr(length(w), n, w);
t = sort(v,2);
[t,~,J] = unique(t,'rows');
m = accumarray(J(:),1);
end % tupmul
function v = tmr(p, n, w, head)
if p==1
if n <= w(end)
v = n;
else
v = zeros(0,1);
end
else
jmax = min(n,w(end-p+1));
v = cell2mat(arrayfun(#(j) tmr(p-1, n-j, w, j), (0:jmax)', ...
'UniformOutput', false));
end
if nargin>=4 % add a head column
v = [head+zeros(size(v,1),1,class(head)) v];
end
end %tmr