How to change elements of a matrix with reference to a vector of column indices without using for-loop? - arrays

I have a matrix
a =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
and b vector
b =
1 2 3 4 5 5
I want to replace value of each row in a matrix with reference value of b matrix value and finally generate a matrix as follows without using for loop.
a_new =
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
0 0 0 0 1
if first element of b, b(1) = 1 so change take first row of a vector and make first element as 1 because b(1) = 1.
How can I implement this without using for loop?

Sure. You only need to build a linear index from b and use it to fill the values in a:
a = zeros(6,5); % original matrix
b = [1 2 3 4 5 5]; % row or column vector with column indices into a
ind = (1:size(a,1)) + (b(:).'-1)*size(a,1); % build linear index
a(ind) = 1; % fill value at those positions

Same as Luis Mendo's answer, but using the dedicated function sub2ind:
a( sub2ind(size(a),(1:numel(b)).',b(:)) ) = 1

Also via the subscript to indices conversion way,
a = zeros(6,5);
b = [1 2 3 4 5 5];
idx = sub2ind(size(a), [1:6], b); % 1:6 just to create the row index per b entry
a(idx) = 1

any of these methods works in Octave:
bsxfun(#eq, [1:5 5]',(1:5))
[1:5 5].' == (1:5)

Related

MATLAB : Obtain a matrix by adding its last lines to the first lines of the basic matrix

I have a matrix B and I would like to obtain a new matrix C from B by adding its last w*a rows to the first w*a rows (w and a will be defined afterwards).
My matrix B is generally defined by :
I would like to obtain matrix C defined in a general way by:
The characteristics of matrices B and C are:
L and w are defined real values;
B0,B1,...,Bw are of dimension: a by b;
B is of dimension: [(L+w)×a] by (L×b);
C is of dimension: (L×a) by (L×b).
Example: For L = 4 and w = 2 I obtain the following matrix B:
The w*a = 2*1 = 2 last rows of B are:
The w*a = 2*1 = 2 first rows of B are:
By adding the two matrices we have:
The matrix C thus obtained is then:
For B0 = [1 0], B1 = [0 1] and B2 = [1 1]. We obtain :
B0, B1 and B2 are of dimension a by b i.e. 1 by 2;
B is of dimension: [(L+w )×(a)] by (L×b) i.e. [(4+2)×1] by (4×2) i.e. 6 by 8;
C is of dimension: (L×a) by (L×b) i.e. (4×1) by (4×2) i.e. 4 by 8.
The matrices B and C that I get are as follows:
B =
1 0 0 0 0 0 0 0
0 1 1 0 0 0 0 0
1 1 0 1 1 0 0 0
0 0 1 1 0 1 1 0
0 0 0 0 1 1 0 1
0 0 0 0 0 0 1 1
C =
1 0 0 0 1 1 0 1
0 1 1 0 0 0 1 1
1 1 0 1 1 0 0 0
0 0 1 1 0 1 1 0
I would like to have some suggestions on how to program this construction so that from a given matrix B I can deduce the matrix C.
Matlab's range indexing should help you do this in a few steps. The key things to remember are that ranges are inclusive, i.e. A[1:3] is a three 3x1 matrix, and that you can use the keyword end to automatically index the end of the matrix row or column.
%% Variables from OP example
w = 2;
L = 4;
B0 = [1 0];
B1 = [0 1];
B2 = [1 1];
[a, b] = size(B0);
% Construct B
BX = [B0;B1;B2]
B = zeros((L+w)*a, L*b);
for ii = 0:L-1
B(ii+1:ii+w+1, ii*b+1:ii*b+b) = BX;
end
%% Construct C <- THIS PART IS THE ANSWER TO THE QUESTION
% Grab first rows of B
B_first = B(1:end-w*a, :) % Indexing starts at first row, continues to w*a rows before the end, and gets all columns
% Grab last rows of B
B_last = B(end-w*a+1:end, :); % Indexing starts at w*a rows before the end, continues to end. Plus one is needed to avoid off by one error.
% Initialize C to be the same as B_first
C = B_first;
% Add B_last to the first rows of C
C(1:w*a, :) = C(1:w*a, :) + B_last;
I get the output
C =
1 0 0 0 0 0 1 1 0 1
0 1 1 0 0 0 0 0 1 1
1 1 0 1 1 0 0 0 0 0
0 0 1 1 0 1 1 0 0 0
0 0 0 0 1 1 0 1 1 0

How to balance unique values in an array Matlab

I have a vector
Y = [1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0]
1 occurs 17 times
0 occurs 21 times
How can I randomly remove 0s so that both values have equal amounts, such as 1 (17 times) and 0 (17 times)?
This should also work on much bigger matrix.
Starting with your example
Y = [1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0]
You can do the following:
% Get the indices of the value which is more common (`0` here)
zeroIdx = find(~Y); % equivalent to find(Y==0)
% Get random indices to remove
remIdx = randperm(nnz(~Y), nnz(~Y) - nnz(Y));
% Remove elements
Y(zeroIdx(remIdx)) = [];
You could combine the last two lines, but I think it would be less clear.
The randperm line is choosing the correct number of elements to remove from random indices between 1 and the number of zeros.
If the data can only have two values
Values are assumed to be 0 and 1. The most common value is randomly removed to equalize their counts:
Y = [1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0]; % data
ind0 = find(Y==0); % indices of zeros
ind1 = find(Y==1); % indices of ones
t(1,1:numel(ind0)) = ind0(randperm(numel(ind0))); % random permutation of indices of zeros
t(2,1:numel(ind1)) = ind1(randperm(numel(ind1))); % same for ones. Pads shorter row with 0
t = t(:, all(t,1)); % keep only columns that don't have padding
result = Y(sort(t(:))); % linearize, sort and use those indices into the data
Generalization for more than two values
Values are arbitrary. All values except the least common one are randomly removed to equalize their counts:
Y = [0 1 2 0 2 1 1 2 0 2 1 2 2 0 0]; % data
vals = [0 1 2]; % or use vals = unique(Y), but absent values will not be detected
t = [];
for k = 1:numel(vals) % loop over values
ind_k = find(Y==vals(k));
t(k, 1:numel(ind_k)) = ind_k(randperm(numel(ind_k)));
end
t = t(:, all(t,1));
result = Y(sort(t(:)));

Vectorisation of code for insertion of n x n matrices in a 3D array along the diagonal of a large matrix

As in my earlier question I am trying to insert small square matrices along the diagonal of a large matrix. However, these matrices are now contained in a 3D array, and have different values. As before, overlapping values are to be added, and the small matrices are only inserted where they can fit fully inside the large matrix. The step dimension will always be equal to 1.
I have achieved an answer through the use of for-loops, but am attempting to vectorise this code for efficiency. How would I do this? The current, unvectorised code is shown below.
function M = TestDiagonal2()
N = 10;
n = 2;
maxRand = 3;
deepMiniM = randi(maxRand,n,n,N+1-n);
M = zeros(N);
for i = 1:N+1-n
M(i:i+n-1,i:i+n-1) = M(i:i+n-1,i:i+n-1) + deepMiniM(:,:,i);
end
end
The desired result is an NxN matrix with n+1 diagonals populated:
3 1 0 0 0 0 0 0 0 0
4 5 3 0 0 0 0 0 0 0
0 3 3 3 0 0 0 0 0 0
0 0 1 6 3 0 0 0 0 0
0 0 0 4 4 4 0 0 0 0
0 0 0 0 2 3 2 0 0 0
0 0 0 0 0 2 6 2 0 0
0 0 0 0 0 0 4 2 2 0
0 0 0 0 0 0 0 3 3 1
0 0 0 0 0 0 0 0 3 3
This makes use of implicit expansion, as well as sparse to add values at coincident indices, and (:) indexing to linearize a matrix in the usual column-major order.
ind1 = repmat((1:n).', n, 1) + (0:N-n); % column indices for the sum
ind2 = repelem((1:n).', n) + (0:N-n); % row indices for the sum
M = full(sparse(ind1(:), ind2(:), deepMiniM(:), N, N)); % sum over those indices

Octave compare two arrays

Given two column vectors,I need to compare each element of the vector a with the first element of vector b in the first iteration and return a logical array. Then the second element of vector b with each element of vector a and return a logical array so forth. The number of logical arrays is equal to the number of elements in vector b.
a=1:10;
b=[5 6 7];
for j=1:length(b),
for i=1:10,
c=b(j)==a(i);
end;
end;
ex:
after the first iteration of inner loop need to return [0 0 0 0 1 0 0 0 0 0]
try this:
a = 1:10
b = [5 6 7]
output = zeros(3,10);
for i = 1:length(b)
output(i,:) = (a == b(:,i)) % b(:, i) meas using index get the value
end
output =
0 0 0 0 1 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 1 0 0 0

Select n elements in matrix left-wise based on certain value

I have a logical matrix A, and I would like to select all the elements to the left of each of my 1s values given a fixed distant. Let's say my distance is 4, I would like to (for instance) replace with a fixed value (saying 2) all the 4 cells at the left of each 1 in A.
A= [0 0 0 0 0 1 0
0 1 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 1 0 1]
B= [0 2 2 2 2 1 0
2 1 0 0 0 0 0
0 0 0 0 0 0 0
2 2 2 2 2 2 1]
In B is what I would like to have, considering also overwrting (last row in B), and cases where there is only 1 value at the left of my 1 and not 4 as the fixed searching distance (second row).
How about this lovely one-liner?
n = 3;
const = 5;
A = [0 0 0 0 0 1 0;
0 1 0 0 0 0 0;
0 0 0 0 0 0 0;
0 0 0 0 1 0 1]
A(bsxfun(#ne,fliplr(filter(ones(1,1+n),1,fliplr(A),[],2)),A)) = const
results in:
A =
0 0 5 5 5 1 0
5 1 0 0 0 0 0
0 0 0 0 0 0 0
0 5 5 5 5 5 1
here some explanations:
Am = fliplr(A); %// mirrored input required
Bm = filter(ones(1,1+n),1,Am,[],2); %// moving average filter for 2nd dimension
B = fliplr(Bm); %// back mirrored
mask = bsxfun(#ne,B,A) %// mask for constants
A(mask) = const
Here is a simple solution you could have come up with:
w=4; % Window size
v=2; % Desired value
B = A;
for r=1:size(A,1) % Go over all rows
for c=2:size(A,2) % Go over all columns
if A(r,c)==1 % If we encounter a 1
B(r,max(1,c-w):c-1)=v; % Set the four spots before this point to your value (if possible)
end
end
end
d = 4; %// distance
v = 2; %// value
A = fliplr(A).'; %'// flip matrix, and transpose to work along rows.
ind = logical( cumsum(A) ...
- [ zeros(size(A,1)-d+2,size(A,2)); cumsum(A(1:end-d-1,:)) ] - A );
A(ind) = v;
A = fliplr(A.');
Result:
A =
0 2 2 2 2 1 0
2 1 0 0 0 0 0
0 0 0 0 0 0 0
2 2 2 2 2 2 1
Approach #1 One-liner using imdilate available with Image Processing Toolbox -
A(imdilate(A,[ones(1,4) zeros(1,4+1)])==1)=2
Explanation
Step #1: Create a morphological structuring element to be used with imdilate -
morph_strel = [ones(1,4) zeros(1,4+1)]
This basically represents a window extending n places to the left with ones and n places to the right including the origin with zeros.
Step #2: Use imdilate that will modify A such that we would have 1 at all four places to the left of each 1 in A -
imdilate_result = imdilate(A,morph_strel)
Step #3: Select all four indices for each 1 of A and set them to 2 -
A(imdilate_result==1)=2
Thus, one can write a general form for this approach as -
A(imdilate(A,[ones(1,window_length) zeros(1,window_length+1)])==1)=new_value
where window_length would be 4 and new_value would be 2 for the given data.
Approach #2 Using bsxfun-
%// Paramters
window_length = 4;
new_value = 2;
B = A' %//'
[r,c] = find(B)
extents = bsxfun(#plus,r,-window_length:-1)
valid_ind1 = extents>0
jump_factor = (c-1)*size(B,1)
extents_valid = extents.*valid_ind1
B(nonzeros(bsxfun(#plus,extents_valid,jump_factor).*valid_ind1))=new_value
B = B' %// B is the desired output

Resources