Indexing equal/different elements in each row of a Matlab array - arrays

Suppose I have a IxJ matrix A in Matlab which contains some numbers (possibly, including Inf, -Inf).
For example, for I=3 and J=6, I could have
A= [0 0 Inf -Inf 0 1;
5 4 Inf -Inf 6 5;
Inf -Inf 0 Inf 0 2];
I want to construct a matrix B of size IxJ, such that each row i starts from 1, adds a +1 every time an element of A(i,:) changes, and assigns equal index to equal elements. Two Inf elements have to be treated as equal. Similarly, two -Inf elements have to be treated as equal.
In the example above
B= [1 1 2 3 1 4; %
1 2 3 4 5 1;
1 2 3 1 3 4];
Could you advise on how to proceed?

A simple approach is to use the third output of unique with the 'stable' option for each row:
B = NaN(size(A)); % preallocate
for k = 1:size(A,1)
[~, ~, B(k,:)] = unique(A(k,:), 'stable');
end

Related

How to reconstruct a 2D K-by-K square matrix from a 1D vector that contain its upper diagonal element given that the other element are zeros?

I got a vector of numbers with length 6 like this a = [1 2 3 4 5 6] and I want to reconstruct the corresponding 4-by-4 matrix A like this where all the element from the diagonal to the other lower diagonal are all zero.
A = [0 1 2 3
0 0 4 5
0 0 0 6
0 0 0 0]
The relationship between the vector a and the corresponding matrix A is that if the dimension of the matrix is K then the length of vector a is K(K-1)/2. In this case the length of a is 6 which mean K = 4.
Another example case would be a = [1 2 3] then
A = [0 1 2
0 0 3
0 0 0
How can I do this?
If you have the Statistics Toolbox, just use squareform and triu:
a = [1 2 3 4 5 6];
A = triu(squareform(a, 'tomatrix'));
Without the toolbox:
a = [1 2 3 4 5 6];
n = (1 + sqrt(1+8*numel(a)))/2; % size of matrix
A = zeros(n); % initiallize
A((1:n).'>(1:n)) = a; % build logical mask using implicit expansion, and fill the
% lower half of the matrix with the desired values in column-major order
A = A.'; % transpose to put the values into the upper half in row-major order

Sum up vector values till threshold, then start again

I have a vector a = [1 3 4 2 1 5 6 3 2]. Now I want to create a new vector 'b' with the cumsum of a, but after reaching a threshold, let's say 5, cumsum should reset and start again till it reaches the threshold again, so the new vector should look like this:
b = [1 4 4 2 3 5 6 3 5]
Any ideas?
You could build a sparse matrix that, when multiplied by the original vector, returns the cumulative sums. I haven't timed this solution versus others, but I strongly suspect this will be the fastest for large arrays of a.
% Original data
a = [1 3 4 2 1 5 6 3 2];
% Threshold
th = 5;
% Cumulative sum corrected by threshold
b = cumsum(a)/th;
% Group indices to be summed by checking for equality,
% rounded down, between each cumsum value and its next value. We add one to
% prevent NaNs from occuring in the next step.
c = cumsum(floor(b) ~= floor([0,b(1:end-1)]))+1;
% Build the sparse matrix, remove all values that are in the upper
% triangle.
S = tril(sparse(c.'./c == 1));
% In case you use matlab 2016a or older:
% S = tril(sparse(bsxfun(#rdivide,c.',c) == 1));
% Matrix multiplication to create o.
o = S*a.';
By normalizing the arguments of cumsum with the threshold and flooring you can get grouping indizes for accumarray, which then can do the cumsumming groupwise:
t = 5;
a = [1 3 4 2 1 5 6 3 2];
%// cumulative sum of normalized vector a
n = cumsum(a/t);
%// subs for accumarray
subs = floor( n ) + 1;
%// cumsum of every group
aout = accumarray( subs(:), (1:numel(subs)).', [], #(x) {cumsum(a(x))});
%// gather results;
b = [aout{:}]
One way is to use a loop. You create the first cumulative sum cs, and then as long as elements in cs are larger than your threshold th, you replace them with elements from the cumulative sum on the rest of the elements in a.
Because some elements in a might be larger than th, this loop will be infinite unless we also eliminate these elements too.
Here is a simple solution with a while loop:
a = [1 3 4 2 1 5 6 3 2];
th = 5;
cs = cumsum(a);
while any(cs>th & cs~=a) % if 'cs' has values larger that 'th',
% and there are any values smaller than th left in 'a'
% sum all the values in 'a' that are after 'cs' reached 'th',
% excluding values that are larger then 'th'
cs(cs>th & cs~=a) = cumsum(a(cs>th & cs~=a));
end
Calculate the cumulative sum and replace the indices value obeying your condition.
a = [1 3 4 2 1 5 6 3 2] ;
b = [1 4 4 2 3 5 6 3 5] ;
iwant = a ;
a_sum = cumsum(a) ;
iwant(a_sum<5) = a_sum(a_sum<5) ;

How to find adjacency matrix given a set of links and edges in matlab

I have vector of all edges for example
A = [1;2;3;4];
I also have the matrix of all the links connecting these edges represented by the edge numbers for example
B = [1 3;3 1;1 2;1 2;2 3;4 3];
I would like to construct the adjacency matrix with this data. The matrix should not consider the ordering of the edges in the links For example the second link has edges 1 2 but the matrix should have entries in both 1,2 and 2,1.
So therefore i need an output like this
C = [0 1 1 0;1 0 1 0;1 1 0 1;0 0 1 0];
I cannot think of any other way other than using a for loop for the size of B and then finding the egdes for each link in B and then adding 1's to a pre-initialized 4x4 matrix at i,j where i,j is the link edges.
Is this an efficient way because my real size is many magnitudes greater than 4? Could someone help with a better way to construct the matrix?
You can use sparse to build the matrix, and then optionally convert to full:
result = full(sparse(B(:,1), B(:,2), 1)); % accumulate values
result = result | result.'; % make symmetric with 0/1 values
Equivalently, you can use accumarray:
result = accumarray(B, 1); % accumulate values
result = result | result.'; % make symmetric with 0/1 values
For A = [1;2;3;4]; B = [1 3;3 1;1 2;1 2;2 3;4 3], either of the above gives
result =
4×4 logical array
0 1 1 0
1 0 1 0
1 1 0 1
0 0 1 0

How to do a matrix manipulation on Matlab?

I have a matrix A of size m x n and another matrix b of size 1 x n (in Matlab).
The matrix b is such that it consists of sequences of 1s, then sequences of 2s, then sequences of 3s, etc. up to some value k.
(For example b = [1 1 1 2 2 2 3 4 4], n = 9)
I want to take A, and for each row in A, choose the max in each segment, zeroing everything else in that subsequence.
So, for example, for a row A = [0 -1 2 3 4 1 3 4 5]) I would get
[0 0 2 0 4 0 3 0 5]
If there are multiple rows in A (m > 1), this should happen for each row.
I can do it easily using for loops, but it works very slowly, because I loop both over m and n.
Is there a "oneliner" to do it in Matlab, or something simple that works fast?
If A is a single row, accumarray can do the job using an ad hoc function:
result = accumarray(b(:), A(:) ,[] , #(x) {x==max(x)});
result = vertcat(result{:}).' .* A;
Not sure how fast this will be, since it uses cells.
If A has several rows, you can use a loop over the rows.

MATLAB Vector of elements x(i) or y(i) with max(abs(x),abs(y))

Sorry for the title, couldn't think of a concise way to phrase the problem. I need to write a MATLAB one-liner that gives you a vector of elements z(i) where z(i) is the element x(i) or y(i) given by max(abs(x(i)),abs(y(i))). I.e, z is the vector whose elements are the ith elements of x or y which has the maximum absolute value.
I have
max(abs(x),abs(y))
But this obviously gives you a vector of the greatest absolute values. This is close to what I want, but I need to get the sign back of the original vector. I'm not sure how to do this on a single line.
Under the assumption that x and y are arrays (not necessarily vectors) of identical dimensions, you can use logical indexing:
(abs(x)>=abs(y)).*x + (abs(x)<abs(y)).*y
For information, abs(x)>=abs(y) is a logical array for which, for all valid indices, the kth component is
1 if x(k) is greater than or equal to y(k),
0 otherwise.
Example:
>> x = [4;7;-1;9;6];
>> y = [5;2;-3;9;3];
>> (abs(x)>=abs(y)).*x + (abs(x)<abs(y)).*y
ans =
5
7
-3
9
6
If you are interested in a generic code that you could use when working with a number of 2D matrices, let's say x, y and p, you can try this -
x = [-2 4 1;
4 -3 2]
y = [8 -3 -5;
-9 1 5]
p = [6 8 6;
7 -1 -2]
mats = cat(3,x,y,p);%// concatenate all identically sized 2D matrices into 3D
[m,n] = size(x);%// get size
[maxval,dim3ind] = max(abs(mats),[],3);%// Max abs values and indices across dim3
mats_sign = sign(mats); %// signum values
out = mats_sign((dim3ind-1)*m*n + bsxfun(#plus,[1:m]',[0:n-1]*m)).*maxval %// output
Output -
x =
-2 4 1
4 -3 2
y =
8 -3 -5
-9 1 5
p =
6 8 6
7 -1 -2
out =
8 8 6
-9 -3 5
So, if you would like to include one more 2D matrix say q into the mix, just edit the first line of code -
mats = cat(3,x,y,p,q);

Resources