Logical vs Numerical array in MATLAB - arrays

I am comparing two binary arrays. I have an array where values can either be one or zero, one if the values are the same and zero if they are not. Please note I am doing other stuff beyond checking, so we don't need to get into vectorization or the nature of the code.
What is more efficient, using a numerical array or a logical array in MATLAB?

Logical values take up fewer bytes than most numeric values, which is a plus if you're dealing with very large arrays. You can also use logical arrays to do logical indexing. For example:
>> valArray = 1:5; %# Array of values
>> numIndex = [0 1 1 0 1]; %# Numeric array of ones and zeroes
>> binIndex = logical([0 1 1 0 1]); %# Logical array of ones and zeroes
>> whos
Name Size Bytes Class Attributes
binIndex 1x5 5 logical %# 1/8 the number of bytes
numIndex 1x5 40 double %# as a double array
valArray 1x5 40 double
>> b = valArray(binIndex) %# Logical indexing
b =
2 3 5
>> b = valArray(find(numIndex)) %# You have to use the FIND function to
%# find the indices of the non-zero
b = %# values in numIndex
2 3 5
One note: If you will be dealing with arrays of zeroes and ones that are very sparse (i.e. very few ones), it may be best to use an array of numeric indices such as you would get from the FIND function.Take the following example:
>> binIndex = false(1,10000); %# A 1-by-10000 logical array
>> binIndex([2 100 1003]) = true; %# Set 3 values to true
>> numIndex = find(binIndex) %# Find the indices of the non-zero values
numIndex =
2 100 1003
>> whos
Name Size Bytes Class Attributes
binIndex 1x10000 10000 logical %# 10000 bytes versus
numIndex 1x3 24 double %# many fewer bytes
%# for a shorter array

Logical of course! Matlab has the option of squeezing 8 items into 1 byte. (Whether it does or not is another matter).
a=ones(1000); b=(a==1);
tic;for(k=1:100)for(i=1:1000);for(j=1:1000);a(i,j)=a(i,j);end;end;end;toc
tic;for(k=1:100)for(i=1:1000);for(j=1:1000);b(i,j)=b(i,j);end;end;end;toc
result
4.561173 seconds
3.454697 seconds
but the benefit will be much greater if you're doing more logical operations rather than just looping!

Related

Extracting positions of elements from two Matlab vectors satisfying some criteria

Consider three row vectors in Matlab, A, B, C, each with size 1xJ. I want to construct a matrix D of size Kx3 listing every triplets (a,b,c) such that:
a is the position in A of A(a).
b is the position in B of B(b).
A(a)-B(b) is an element of C.
c is the position in C of A(a)-B(b).
A(a) and B(b) are different from Inf, -Inf.
For example,
A=[-3 3 0 Inf -Inf];
B=[-2 2 0 Inf -Inf];
C=[Inf -Inf -1 1 0];
D=[1 1 3; %-3-(-2)=-1
2 2 4; % 3-2=1
3 3 5]; % 0-0=0
I would like this code to be efficient, because in my real example I have to repeat it many times.
This question relates to my previous question here, but now I'm looking for the positions of the elements.
You can use combvec (or any number of alternatives) to get all pairings of indices a and b for the corresponding arrays A and B. Then it's simply a case of following your criteria
Find the differences
Check which differences are in C
Remove elements you don't care about
Like so:
% Generate all index pairings
D = combvec( 1:numel(A), 1:numel(B) ).';
% Calculate deltas
delta = A(D(:,1)) - B(D(:,2));
delta = delta(:); % make it a column
% Get delta index in C (0 if not present)
[~,D(:,3)] = ismember(delta,C);
% If A or B are inf then the delta is Inf or NaN, remove these
idxRemove = isinf(delta) | isnan(delta) | D(:,3) == 0;
D(idxRemove,:) = [];
For your example, this yields the expected results from the question.
You said that A and B are at most 7 elements long, so you have up to 49 pairings to check. This isn't too bad, but readers should be careful that the pairings can grow quickly for larger inputs.

Inserting selected number of zeros between fixed number of non-zero elements in a vector in MATLAB

I have a vector like
A=[1,2,3,4,5,6,7,8,9,10];
I would like to insert 2 zero every 5 number.
The result would be A=[1,2,3,4,5,0,0,6,7,8,9,10,0,0].
I know I could preallocate the space and then use a for cycle to assign the variable, but I was wandering if there was some more elegant way.
This works even if A doesn't contain an integer number of blocks:
A = [1,2,3,4,5,6,7,8,9,10,11,12]; % input vector
m = 5; % block size
n = 2; % number of zeros to be added after each block
B = zeros(1, numel(A)+floor(numel(A)/m)*n); % preallocate to appropriate size
B(mod(0:end-1, m+n)<m) = A; % logical index. Fill values of A at desired positions of B
The result in this example is
B =
1 2 3 4 5 0 0 6 7 8 9 10 0 0 11 12
With A having number of elements a multiple of 5, you could use some reshaping and concatenation with zeros, like so -
reshape([reshape(A,5,[]) ; zeros(2,numel(A)/5)],1,[])

Counting rows without any NaNs in a struct

I need to count how many structs do not have any NaNs across all fields in an array of structures. The sample struct looks like this:
a(1).b = 11;
a(2).b = NaN;
a(3).b = 22;
a(4).b = 33;
a(1).c = 44;
a(2).c = 55;
a(3).c = 66;
a(4).c = NaN;
The output looks like this
Fields b c
1 44 11
2 55 NaN
3 66 22
4 NaN 33
The structs without NaNs are 1 and 3, so there should be 2 in total here.
I tried using size(a, 2), but it just tells me the total number of structs in the array. I need it to calculate N (the number of observations in the sample). NaNs don't count as observations as they are omitted in the analysis.
What is the simplest way to count structs without any NaNs in a struct array?
I would suggest using the following one-line command:
nnz(~any(cellfun(#isnan,struct2cell(a))))
struct2cell(a) converts your struct into the 3D cell array
cellfun(#isnan,___) applies isnan to each element of cell array
~any(__) works along first dimension and returns arrays that have no NaNs
nnz(__) counts how many rows have no NaNs
The result is just a number, 2 in this case.
The following:
find(~any(cellfun(#isnan,struct2cell(a))))
Would tell you which rows are without NaNs
This will tell you which ones have no NaNs
for ii=1:size(a,2)
hasNoNaNs(ii)=~any(structfun(#isnan,a(ii)));
end
The way it works is iterates trhoug each of the structures, and use structfun to call isnan in each of the elements of it, then checks if any of them is a NaN and negates the result, thus giving 1 in the ones that have no NaNs
Because bsxfun is never the wrong approach!
sum(all(bsxfun(#le,cell2mat(struct2cell(a)),inf)))
How this works:
This converts the struct to a cell, and then to a matrix:
cell2mat(struct2cell(a))
ans(:,:,1) =
11
44
ans(:,:,2) =
NaN
55
ans(:,:,3) =
22
66
ans(:,:,4) =
33
NaN
Then it uses bsxfun to check which of those elements are less than, or equal to zero. The only value that doesn't satisfy this condition is NaN.
bsxfun(#le,cell2mat(struct2cell(a)),inf)
ans(:,:,1) =
1
1
ans(:,:,2) =
0
1
ans(:,:,3) =
1
1
ans(:,:,4) =
1
0
Then, we check if all the values in each of those slices are true:
all(bsxfun(#le,cell2mat(struct2cell(a)),inf))
ans(:,:,1) =
1
ans(:,:,2) =
0
ans(:,:,3) =
1
ans(:,:,4) =
0
And finally, we sum it up:
sum(all(bsxfun(#le,cell2mat(struct2cell(a)),inf)))
ans =
2
(By the way: It's possible to just skip the bsxfun, but where's the fun in that)
sum(all(cell2mat(struct2cell(a))<=inf))
Use arrayfun to iterate over a and structfun to iterate over fields and you get a logical array of elements that do not have NaNs:
>> arrayfun(#(x) ~any(structfun(#isnan, x)), a)
ans =
1 0 1 0
Now you can just sum it
>> sum(arrayfun(#(x) ~any(structfun(#isnan, x)), a))
ans =
2
Taking the idea from this not working answer to use comma separated lists:
s=sum(~any(isnan([[a.b];[a.c]])));
It may look very dumb to hard-code the field names but it leads to fast code because it avoids both iterating and cell arrays.
Generalizing this approach to arbitriary field names, you end up with this solution:
n=true(size(a));
for f = fieldnames(a).'
n(isnan([a.(f{1})]))=false;
end
n=sum(n(:));
Assuming that you have a large struct with only few fieldnames this is very efficient, because it is only iterating the fieldnames.
Third solution maybe - may not be elegant depending on your data:
A = [[a.b];[a.c]]; %//EDIT -- Fixed based on #Daniel's correct solution
IndNotNaN = find (~isnan(A));
Depends if you have lots of structs you will have to concatenate a.b, a.c ....a.n

MATLAB function to replace randi to generate a matrix

I have a matlab problem to solve. In have two vectores that limit my space, x_low and x_high. The matrix pos needs to have values within this spaces and each column of the matrix has different bounds given by the two vectores. Now my problem is that randi gives valus between two integers but i need to change the bounds for each columns. There is another way to use randi or a different matlab function to do this?
I know there are better codes to do this but i'm starting to use matlab and i know to do it this way, any aid is welcome
x_low = [Io_low, Iirr_low, Rp_low, Rs_low, n_low]; % vector of constant values
x_high = [Io_high, Iirr_high, Rp_high, Rs_high, n_high]; % vector of constant values
pos = rand(particles, var);
var = length(x_high);
for i = 1: particles % rows
for k = 1: var %columns
if pos(i, k) < x_low(k) || pos(i, k) > x_high(k) % if the position is out of bounder
pos(i, k) = randi(x_low(k), x_high(k), 1); % fill it with a particle whithin the bounderies
end
end
end
If I understand correctly, you need to generate a matrix with integer values such that each column has different lower and upper limits; and those lower and upper limits are inclusive.
This can be done very simply with
rand (to generate random numbers between 0 and 1 ),
bsxfun (to take care of the lower and upper limits on a column basis), and
round (so that the results are integer values).
Let the input data be defined as
x_low = [1 6 11]; %// lower limits
x_high = [3 10 100]; %// upper limits
n_rows = 7; %// number of columns
Then:
r = rand(n_rows, numel(x_low)); %// random numbers between 0 and 1
r = floor(bsxfun(#times, r, x_high-x_low+1)); %// adjust span and round to integers
r = bsxfun(#plus, r, x_low); %// adjust lower limit
gives something like
r =
2 7 83
3 6 93
2 6 22
3 10 85
3 7 96
1 10 90
2 8 57
If you need to fill in values only at specific entries of matrix pos, you can use something like
ind = bsxfun(#lt, pos, x_low) | bsxfun(#gt, pos, x_high); %// index of values to replace
pos(ind) = r(ind);
This a little overkill, because the whole matrixd r is generated only to use some of its entries. To generate only the needed values the best way is probably to use loops.
You can use cellfun for this. Something like:
x_low = [Io_low, Iirr_low, Rp_low, Rs_low, n_low];
x_high = [Io_high, Iirr_high, Rp_high, Rs_high, n_high];
pos = cell2mat(cellfun(#randi, mat2cell([x_low' x_high'], ones(numel(x_low),1), 1), repmat({[particles 1]}, [numel(x_low) 1)])))';
Best,

How to get elements larger than x in a given range?

Given a matrix A, how do I get the elements (and their indices) larger than x in a specific range?
e.g.
A = [1:5; 2:6; 3:7; 4:8; 5:9]
A =
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
And for instance I want all elements larger than 5 and appear in the range A(2:4,3:5). I should get:
elements:
6 , 6 , 7 , 6 , 7 , 8
indices:
14, 18, 19, 22, 23, 24
A(A>5) would give me all entries which are larger than 5.
A(2:4,3:5) would give all elements in the range 2:4,3:5.
I want some combination of the two. Is it possible or the only way is to put the needed range in another array B and only then perform B(B>5)? Obviously 2 problems here: I'd lose the original indices, and it will be slower. I'm doing this on a large number of matrices.
Code. I'm trying to avoid matrix multiplication, so this may look a bit odd:
A = [1:5; 2:6; 3:7; 4:8; 5:9];
[r,c] = meshgrid(2:4,3:5);
n = sub2ind(size(A), r(:), c(:));
indices = sort(n(A(n) > 5)); %'skip sorting if not needed'
values = A(indices);
Explanation. The code converts the Cartesian product of the subscripts to linear indices in the A matrix. Then it selects the indices that respect the condition, then it selects the values.
However, it is slow.
Optimization. Following LuisMendo's suggestion, the code may be sped up by replacing the sub2ind-based linear index calculation with a handcrafted linear index calculation:
A = [1:5; 2:6; 3:7; 4:8; 5:9];
%'For column-first, 1-based-index array memory '
%'layout, as in MATLAB/FORTRAN, the linear index '
%'formula is: '
%'L = R + (C-1)*NR '
n = bsxfun(#plus, (2:4), (transpose(3:5) - 1)*size(A,1));
indices = n(A(n) > 5);
values = A(indices);
If you only need the values (not the indices), it can be done using the third output of find and matrix multiplication. I don't know if it will be faster than using a temporary array, though:
[~, ~, values] = find((A(2:4,3:5)>5).*A(2:4,3:5));
Assuming you need the linear indices and the values, then if the threshold is positive you could define a mask. This may be a good idea if the mask can be defined once and reused for all matrices (that is, if the desired range is the same for all matrices):
mask = false(size(A));
mask(2:4,3:5) = true;
indices = find(A.*mask>5);
values = A(indices);
its a little clunky, but:
R = 2:4;
C = 3:5;
I = reshape(find(A),size(A))
indicies = nonzeros(I(R,C).*(A(R,C)>5))
values = A(indicies)

Resources