Matlab swap - matlab

I am trying to create a function that will swap a specific number in a matrix with a specific number in the same matrix. For examlpe, if I start with A = [1 2 3;1 3 2], I want to be able to create B = [2 1 3; 2 3 1], simply by telling matlab to swap the 1's with the 2's. Any advice would be appreciated. Thanks!

If you have the following matrix:
A = [1 2 3; 1 3 2];
and you want all the ones to become twos and the twos to become ones, the following would be the simplest way to do it:
B = A;
B(find(A == 1)) = 2;
B(find(A == 2)) = 1;
EDIT:
As Kenny suggested, this can even be further simplified as:
B = A;
B(A == 1) = 2;
B(A == 2) = 1;

Another way to deal with the original problem is to create a permutation vector indicating to which numbers should the original entries be mapped to. For the example, entries [1 2 3] should be mapped respectively to [2 1 3], so that we can write
A = [1 2 3; 1 3 2];
perm = [2 1 3];
B = perm(A)
(advantage here is that everything is done in one step, and that it also works for operations more complicated than swaps ; drawback is that all elements of A must be positive integers with a known maximum)

Not sure why you would to perform that particular swap (row/column interchanges are more common). Matlab often denotes ':' to represent all of something. Here's how to swap rows and columns:
To swap rows:
A = A([New order of rows,,...], :)
To Swap columns:
A = A(:, [New order of columns,,...])
To change the entire i-th column:
A(:, i) = [New; values; for; i-th; column]
For example, to swap the 2nd and 3rd columns of A = [1 2 3;1 3 2]
A = A(:, [1, 3, 2])

A = [1 2 3; 1 3 2]
alpha = 1;
beta = 2;
indAlpha = (A == alpha);
indBeta = (A == beta);
A(indAlpha) = beta;
A(indBeta ) = alpha
I like this solution, it makes it clearer what is going on. Less magic numbers, could easily be made into a function. Recycles the same matrix if that is important.

I don't have a copy of MatLab installed, but I think you can do some thing like this;
for i=1:length(A)
if (A(i)=1), B(i) = 2, B(i)=A(i)
end
Note, that's only convert 1's to 2's and it looks like you also want to convert 2's to 1's, so you'll need to do a little more work.
There also probably a much more elegant way of doing it given you can do this sort of thing in Matlab
>> A = 1:1:3
A = [1,2,3]
>> B = A * 2
B = [2,4,6]
There might be a swapif primitive you can use, but I haven't used Matlab in a long time, so I'm not sure the best way to do it.

In reference to tarn's more elegant way of swapping values you could use a permutation matrix as follows:
>> a =[1 2 3];
>> T = [1 0 0;
0 0 1;
0 1 0];
>> b = a*T
ans =
1 3 2
but this will swap column 2 and column 3 of the vector (matrix) a; whereas the question asked about swapping the 1's and 2's.
Update
To swap elements of two different values look into the find function
ind = find(a==1);
returns the indices of all the elements with value, 1. Then you can use Mitch's suggestion to change the value of the elements using index arrays. Remeber that find returns the linear index into the matrix; the first element has index 1 and the last element of an nxm matrix has linear index n*m. The linear index is counted down the columns. For example
>> b = [1 3 5;2 4 6];
>> b(3) % same as b(1,2)
ans = 3
>> b(5) % same as b(1,3)
ans = 5
>> b(6) % same as b(2,3)
ans = 6

Related

Row-wise "ismember" without for-loop

I am trying to perform a row-wise "ismember" in MATLAB in order to find out where, in Set, each element of Input is.
Here is my work so far.
function lia = ismemberrow(Input, Set)
lia = false(size(Input)); % Pre-assign the output lia
for J = 1 : size(Input,1)
% The J-th row of "lia" is the list of locations of the
% elements of Input(J,:) in Set
lia(J,:) = find(ismember(Set, Input(J,:)));
end
end
For example, if the variables Input and Set are defined as follows
Input = [1 4;
4 2;
4 3;
2 4;
1 2;
3 2];
Set = [3 2 4 1];
The the output lia of lia = ismemberrow(Input,Set) will be:
lia = [4 3;
3 2;
3 1;
2 3;
4 2;
1 2];
My function works accurately so far, but this function is called many times in my project so I am thinking of that if I can reduce the for-loop so that it spends less time. May I have some opinions on it?
A single call to ismember (no loop necessary) will give you what you want in the second output argument:
>> [~, lia] = ismember(Input, Set)
lia =
4 3
3 2
3 1
2 3
4 2
1 2
I'd go with ismember as in #gnovice's answer. But here are some alternatives, just for the fun of it.
If the values in Input are guaranteed to be in Set:
[ind, ~] = find(bsxfun(#eq, Set(:), Input(:).'));
result = reshape(ind, size(Input));
If they are not guaranteed to be:
[ind, val] = max(bsxfun(#eq, Set(:), permute(Input, [3 1 2])));
result = permute(ind.*val, [2 3 1]);
If your inputs are positive integers you simply can use indexing
m(Set)=1:numel(Set);
result = m(Input)
If the range of input is large you can use sparse matrix:
s = sparse(Set,1,1:numel(Set));
result = s(Input)
Result:
4 3
3 2
3 1
2 3
4 2
1 2

how to omit for loop when there is constrains

I have the following two arrays:
A = [1 2;3 4] and B = [1 5 4]
I want to do the following operation:
for each element of A(call it A(i))
for each element of B~=b do
( (A(i) - 1)/(b-1) ) * ( (A(i) - 5)/(b-5) ) * ( (A(i)- 4)/(b-4) )
end
end
It means that, sometimes the numerator equals to zero, so the product should be zeros. And I want to do the operation for the elements of B which are not equal to the b in denominator to not make it Inf.
How can I do this for the whole matrix A instead of using for loop?
Code
A = [1 2;3 4];
B = [1 5 4];
m1 = bsxfun(#minus,A,permute([1 5 4],[3 1 2]));
m2 = bsxfun(#minus,B,permute([1 5 4],[3 1 2]));
for k1=1:size(A,1)
for k2=1:size(A,2)
t2 = squeeze(bsxfun(#rdivide,m1(k1,k2,:),m2));
t2(1:size(t2,1)+1:end)=1;
A1(k1,k2) = prod(t2(:)); %%// Output
end
end
Output
A1 =
0 -0.2500
-0.1111 0
You can remove the nested loops, but at least two issues there -
You would be going to 4th and 5th dimension with it, using bsxfun. So, debugging would be tough.
bsxfun with higher dimensions to my knowledge seems to get slower.
You could just do the operation, and correct later:
C = (A-1)./(B-1) .* (A-5)./(B-5) .* (A-4)./(B-4)
C(isinf(C)) = 0;
or
C(B==b) = 0;
Possibly you'd need bsxfun, I'm not clear on the size of the output you want...

Find part of vector in another vector matlab

I would like to know if there is an easy way to find the indices of a vector in another vector in matlab:
a = [1 2 3 5 7 10 2 3 6 8 7 5 2 4 7 2 3]
b = [2 3]
So how to get the indices of a when comparing it with b (index of first element is needed)
In this case:
ans = [2 7 16]
Thanks in advance
find(a(1:end-1) == b(1) & a(2:end) == b(2) == 1)
You can re-purpose strfind by converting the elements of both vectors to byte arrays (uint8) with typecast:
bytesPerEl = numel(typecast(a(1),'uint8'));
byteLocs = strfind(char(typecast(a,'uint8')),char(typecast(b,'uint8')));
locsb = (byteLocs-1)/bytesPerEl + 1
locsb =
2 7 16
Just make sure a and b are of the same type. Also note that this works for 1D vectors, not matrixes or higher dimensional arrays.
General approach with length of b arbitrary (not necessarily 2 as in the example), and avoiding the use of strings:
match1 = bsxfun(#eq, a(:), b(:).'); %'// now we just need to make the diagonals
%// horizontal (in order to apply "all" row-wise). For that we'll use indices
%// ind, ind1, ind2
ind = reshape(1:numel(match1), numel(a), numel(b));
ind1 = nonzeros(tril(ind)); %// source indices
ind2 = sort(nonzeros(tril(flipud(ind)))); %// destination indices
match2 = zeros(size(match1));
match2(ind2) = match1(ind1); %// diagonals have become horizontal
result = find(all(match2.'));

Vector of the occurence number

I have a vector a=[1 2 3 1 4 2 5]'
I am trying to create a new vector that would give for each row, the occurence number of the element in a. For instance, with this matrix, the result would be [1 1 1 2 1 2 1]': The fourth element is 2 because this is the first time that 1 is repeated.
The only way I can see to achieve that is by creating a zero vector whose number of rows would be the number of unique elements (here: c = [0 0 0 0 0] because I have 5 elements).
I also create a zero vector d of the same length as a. Then, going through the vector a, adding one to the row of c whose element we read and the corresponding number of c to the current row of d.
Can anyone think about something better?
This is a nice way of doing it
C=sum(triu(bsxfun(#eq,a,a.')))
My first suggestion was this, a not very nice for loop
for i=1:length(a)
F(i)=sum(a(1:i)==a(i));
end
This does what you want, without loops:
m = max(a);
aux = cumsum([ ones(1,m); bsxfun(#eq, a(:), 1:m) ]);
aux = (aux-1).*diff([ ones(1,m); aux ]);
result = sum(aux(2:end,:).');
My first thought:
M = cumsum(bsxfun(#eq,a,1:numel(a)));
v = M(sub2ind(size(M),1:numel(a),a'))
on a completely different level, you can look into tabulate to get info about the frequency of the values. For example:
tabulate([1 2 4 4 3 4])
Value Count Percent
1 1 16.67%
2 1 16.67%
3 1 16.67%
4 3 50.00%
Please note that the solutions proposed by David, chappjc and Luis Mendo are beautiful but cannot be used if the vector is big. In this case a couple of naïve approaches are:
% Big vector
a = randi(1e4, [1e5, 1]);
a1 = a;
a2 = a;
% Super-naive solution
tic
x = sort(a);
x = x([find(diff(x)); end]);
for hh = 1:size(x, 1)
inds = (a == x(hh));
a1(inds) = 1:sum(inds);
end
toc
% Other naive solution
tic
x = sort(a);
y(:, 1) = x([find(diff(x)); end]);
y(:, 2) = histc(x, y(:, 1));
for hh = 1:size(y, 1)
a2(a == y(hh, 1)) = 1:y(hh, 2);
end
toc
% The two solutions are of course equivalent:
all(a1(:) == a2(:))
Actually, now the question is: can we avoid the last loop? Maybe using arrayfun?

Selecting an element from a matrix in a cell

Good day,
I have got something similar as:
A = [1 2; 3 4];
B = [2 3; 4 5; 6 7];
C{1} = A;
C{2} = B;
clear A;
clear B;
Now I would like to select element (2,1) from matrix B, that is, element (2,1) from C{2}. However, matrix B itself does not exist any more.
One possibility is:
B = C{2};
B(2,1)
However, is there a more direct way to access elements from matrices which are stored in a cell?
Cells allow you to chain subscripts in the following manner
>> C{2}(2, 1)
ans =
4