Replace specific entries of a multidimensional arrays avoiding loops - arrays

I would like to replace the entry corresponding to the column number of an array that is part of a 3D matrix by zero. My matrix is of size IxJxJ. In each column j I can find a matrix of size IxJof which I would like to replace the jth column by zero.
You can find below an example of what I would like using a simple 3D matrix A. This example uses a loop, which is what I am trying to avoid.
A(:,:,1) = randi([1,2],5,3);
A(:,:,2) = randi([3,4],5,3);
A(:,:,3) = randi([5,6],5,3);
for i = 1:3
B = A(:,i,:);
B = squeeze(B);
B(:,i) = 0;
A(:,i,:) = B;
end

Firstly, you can replace the 4 lines of code in your for loop with just A(:,i,i) = 0;. I don't see any real need to avoid the for loop.
Using linear indexing, you can do
A((1:size(A,1)).'+size(A,1).*(size(A,2)+1).*(0:size(A,2)-1)) = 0
or for older version of Matlab without implicit expansion (pre-R2016b)
A(bsxfun(#plus,(1:size(A,1)).',size(A,1).*(size(A,2)+1).*(0:size(A,2)-1))) = 0
After some very quick testing, it actually looks like the bsxfun solution is fastest, but the differences aren't huge, your results may differ.

Use eye to create a logical mask and mutiply it by A.
A = A .* reshape(~eye(3), 1, 3, 3) ;

Related

Array of sets in Matlab

Is there a way to create an array of sets in Matlab.
Eg: I have:
a = ones(10,1);
b = zeros(10,1);
I need c such that c = [(1,0); (1,0); ...], i.e. each set in c has first element from a and 2nd element from b with the corresponding index.
Also is there some way I can check if an unknown set (x,y) is in c.
Can you all please help me out? I am a Matlab noob. Thanks!
There are not sets in your understanding in MATLAB (I assume that you are thinking of tuples on Python...) But there are cells in MATLAB. That is a data type that can store pretty much anything (you may think of pointers if you are familiar with the concept). It is indicated by using { }.
Knowing this, you could come up with a cell of arrays and check them using cellfun
% create a cell of numeric arrays
C = {[1,0],[0,2],[99,-1]}
% check which input is equal to the array [1,0]
lg = cellfun(#(x)isequal(x,[1,0]),C)
Note that you access the address of a cell with () and the content of a cell with {}. [] always indicate arrays of something. We come to this in a moment.
OK, this was the part that you asked for; now there is a bonus:
That you use the term set makes me feel that they always have the same size. So why not create an array of arrays (or better an array of vectors, which is a matrix) and check this matrix column-wise?
% array of vectors (there is a way with less brackets but this way is clearer):
M = [[1;0],[0;2],[99;-1]]
% check at which column (direction ",1") all rows are equal to the proposed vector:
lg = all(M == [0;2],1)
This way is a bit clearer, better in terms of memory and faster.
Note that both variables lg are arrays of logicals. You can use them directly to index the original variable, i.e. M(:,lg) and C{lg} returns the set that you are looking for.
If you would like to get logical value regarding if p is in C, maybe you can try the code below
any(sum((C-p).^2,2)==0)
or
any(all(C-p==0,2))
Example
C = [1,2;
3,-1;
1,1;
-2,5];
p1 = [1,2];
p2 = [1,-2];
>> any(sum((C-p1).^2,2)==0) # indicating p1 is in C
ans = 1
>> any(sum((C-p2).^2,2)==0) # indicating p2 is not in C
ans = 0
>> any(all(C-p1==0,2))
ans = 1
>> any(all(C-p2==0,2))
ans = 0

In MATLAB: How should nested fields of a struct be converted to a cell array?

In MATLAB, I would like to extract a nested field for each index of a 1 x n struct (a nonscalar struct) and receive the output as a 1 x n cell array. As a simple example, suppose I start with the following struct s:
s(1).f1.fa = 'foo';
s(2).f1.fa = 'yedd';
s(1).f1.fb = 'raf';
s(2).f1.fb = 'da';
s(1).f2 = 'bok';
s(2).f2 = 'kemb';
I can produce my desired 1 x 2 cell array c using a for-loop:
n = length(s);
c = cell(1,n);
for k = 1:n
c{k} = s(k).f1.fa;
end
If I wanted to do analogously for a non-nested field, for example f2, then I could "vectorize" the operation (see this question), writing simply:
c = {s.f2};
However the same approach does not appear to work for nested fields. What then are possible ways to vectorize the above for-loop?
You cannot really vectorize it. The problem is that Matlab does not allow most forms of nested indexing, including []..
The most concise / readable option would be to concatenate s.f1 results in a structure array using [...], and then index into the new structure array with a separate call:
x = [s.f1]; c = {x.fa};
If you have a Mapping Toolbox, you could use extractfield to perform the second indexing in one expression:
c = extractfield([s.f1], 'fa');
Alternatively you could write a one-liner using arrayfun - here's a couple of options:
c = arrayfun(#(x) x.f1.fa, s, 'uni', false);
c = arrayfun(#(x) x.fa, [s.f1], 'uni', false);
Note that arrayfun and similar functions are generally slower than explicit for loops. So if the performance is critical, time / profile your code, before making a decision to get rid of the loop.

Multiply elements in second column according to labels in the first

I'm working in Matlab.
I have a two-dimensional matrix with two columns. Lets consider elements in the first column as labels. Labels may be repeated.
How to multiply all elements in the second column for every label?
Example:
matrix = [1,3,3,1,5; 2,3,7,8,3]'
I need to get:
a = [1,3,5; 16,21,3]'
Can you help me with doing it without for-while cycles?
I would use accumarray. The preprocessing with unique assigns integer indices 1:n to the values in the first row, which allow accumarray to work without creating unnecessary bins for 2 and 4. It also enables the support for negative numbers and floats.
[ulable,~,uindex]=unique(matrix(:,1))
r=accumarray(uindex,matrix(:,2),[],#prod)
r=[ulable,r]
/You can also use splitapply:
[ulable,~,uindex]=unique(matrix(:,1))
r=splitapply(#prod,matrix(:,2),uindex)
r=[ulable,r]
You can do it without loops using accumarray and the prod function:
clear
clc
matrix = [1,3,3,1,5; 2,3,7,8,3]';
A = unique(matrix,'rows');
group = A(:,1);
data = A(:,2);
indices = [group ones(size(group))];
prods = accumarray(indices, data,[],#prod); %// As mentionned by #Daniel. My previous answer had a function handle but there is no need for that here since prod is already defined in Matlab.
a = nonzeros(prods)
Out = [unique(group) a]
Out =
1 16
3 21
5 3
Check Lauren blog's post here, accumarray is quite interesting and powerful!
Try something like this, I'm sure it can be improved...
unValues = unique(matrix(:,1));
bb = ones(size(unValues));
for ii = 1:length(unValues)
bb(ii) = bb(ii)*prod(matrix(matrix(:, 1) == unValues(ii), 2));
end
a = [unValues bb];

Efficient concatenation of multiple, varying in size matrices in a loop

I would like to efficiently concatenate multiple matrices into a vector. However, the number of such matrices and their sizes vary. Say, I have two stacks A and B, each consisting of m matrices.
Naive approach would be the following:
merged = [];
for i = 1 : m
merged = [merged ; A{i}(:) ; B{i}(:)];
end
The challenging part is to optimise the above code to avoid copying the older array contents to the new array as it makes each assignment. For instance, one could compute the number of elements in each matrix and then preallocate a vector capable of storing all the elements. Still, I am not entirely sure how to efficiently place the matrices inside the vector.
Any suggestions would be appreciated.
One possible approach:
merged = cellfun(#(x) x(:), [A(:) B(:)].', 'uni', false);
merged = vertcat(merged{:});
Depending on the size of A and B the follow could be faster
C = {A{:} ; B{:}};
merged = vertcat(C{:});
(starting to show above numel(A) > 500)
I used the following as test data
m = 1000;
A = cell(m,1);
B = cell(m,1);
for i=1:m
A{i} = round(10*rand(max(round(11*rand),1),1));
B{i} = round(10*rand(max(round(5*rand),1),1));
end

vectorize a filter to a subsequence of an array in Matlab

I have a vector,"a", and a filter,"b".Both of those vectors contain only 0 or 1.
I would like to transform "a" such that any sequence of 1 only starts when b is at 1.
I have illustrated this using a loop but, as my vectors are huge, it is extremely inefficient.
The result I would like is stored in "r".
a=[0;0;1;1;1;1;1;1;0;0;1;1;0;0;1;1;1;1;1];
b=[0;0;0;0;1;0;1;0;0;1;0;1;0;1;1;0;0;0;0];
r=[0;0;0;0;1;1;1;1;0;0;0;1;0;0;1;1;1;1;1];
for i=2:length(a)
if a(i)==1 &&a(i-1)==0 && b(i)==0
a(i)=a(i-1);
end
end
assert(sum(a==r)==length(a))
Here's a two-liner:
r = a;
r([false; diff(a)>0 & b(2:end)==0]) = 0;
Please note that you need to adapt the code for row vectors (this works for column vectors).

Resources