Extract blocks of numbers from array in Matlab - arrays

I have a vector and I would like to extract all the blocks from it:
x = [1 1 1 4 4 5 5 4 6 1 2 4 4 4 9 8 4 4 4 4]
so that I will get vectors or a cell containing the blocks:
[1 1 1], [4 4], [5 5], [4], [6], [1], [2], [4 4 4], [9], [8], [4 4 4 4]
Is there an efficient way to do it without using for loops? Thanks!

You can use accumarray with a custom anonymous function:
y = accumarray(cumsum([true; diff(x(:))~=0]), x(:), [], #(x) {x.'}).';
This gives a cell array of vectors. In your example,
x = [1 1 1 4 4 5 5 4 6 1 2 4 4 4 9 8 4 4 4 4];
the result is
y{1} =
1 1 1
y{2} =
4 4
y{3} =
5 5
y{4} =
4
y{5} =
6
y{6} =
1
y{7} =
2
y{8} =
4 4 4
y{9} =
9
y{10} =
8
y{11} =
4 4 4 4

For loops aint as slow as you might think, especially not in more recent Matlab versions and especially not in our case. Maybe this will help
x = [1 1 1 4 4 5 5 4 6 1 2 4 4 4 9 8 4 4 4 4];
breakIdx = [0, find(diff(x)), length(x)];
groups = mat2cell(x,1,diff(breakIdx));
We find the groups by applying diff(x) and we get the group indices with find(). Then it's just a matter of moving the groups into the resulting cell groups.
There's very little edge case checks here so I recommend you add that.

If holding all blocks in a cell array is not so important, but ruther the full information about them, you can use this code:
x = [1 1 1 4 4 5 5 4 6 1 2 4 4 4 9 8 4 4 4 4];
elements = x(diff([0 x])~=0);
block_size = accumarray(cumsum(diff([0 x])~=0).',1).';
blocks = [elements; block_size];
to get a 2-row matrix with the element on the first row, and the block size on the second:
blocks =
1 4 5 4 6 1 2 4 9 8 4
3 2 2 1 1 1 1 3 1 1 4
Then define a function to create those blocks by need:
getBlock = #(k) ones(1,blocks(2,k))*blocks(1,k);
and call it with the number of block you want:
getBlock(8)
to get:
ans =
4 4 4

Related

MATLAB: Remove specific elements from array

Question 1: I have a 1x15 array, comprising of positive integers and negative integers. I wish to implement a MATLAB code which keeps all positive integers and skips the cells with negative contents.
I have tried the following:
X = [1 2 3 4 5 -10 1 -5 4 6 8 9 2 4 -2];
[r c] = size(X);
for i=1:r
for j=1:c
if X(i,j)<0
X(i,j)=X(i,j+1)
end
end
end
The output should be:
X_new = [1 2 3 4 5 1 4 6 8 9 2 4]
How do I do this?
Question 2:
X = [1 2 3 4 5 -10 1 -5 4 6 8 9 2 4 -2]
Y = [5 3 8 9 4 5 6 7 4 7 9 5 2 1 4]
From Question 1,
X_new = [1 2 3 4 5 1 4 6 8 9 2 4]
I need to delete the corresponding values in Y so that:
Y_new = [5 3 8 9 4 6 4 7 9 5 2 1]
How do I perform this?
In MATLAB, manipulating arrays and matrices can be done much easier than for-loop solutions,
in your task, can do find and delete negative value in the array, simply, as follows:
Idx_neg = X < 0; % finding X indices corresponding to negative elements
X ( Idx_neg ) = []; % removing elements using [] operator
Y ( Idx_neg ) = []; % removing corresponding elements in Y array

Ordered random numbers in Matlab

I am trying to generate random numbers between 1 and 5 using Matlab's randperm and calling randperm = 5.
Each time this gives me a different array let's say for example:
x = randperm(5)
x = [3 2 4 1 5]
I need the vector to be arranged such that 4 and 5 are always next to each other and 2 is always between 1 and 3... so for e.g. [3 2 1 4 5] or [4 5 1 2 3].
So essentially I have two "blocks" of unequal length - 1 2 3 and 4 5. The order of the blocks is not so important, just that 4 & 5 end up together and 2 in between 1 and 3.
I can basically only have 4 possible combinations:
[1 2 3 4 5]
[3 2 1 4 5]
[4 5 1 2 3]
[4 5 3 2 1]
Does anyone know how I can do this?
Thanks
I'm not sure if you want a solution that would somehow generalize to a larger problem, but based on how you've described your problem above it looks like you are only going to have 8 possible combinations that satisfy your constraints:
possible = [1 2 3 4 5; ...
1 2 3 5 4; ...
3 2 1 4 5; ...
3 2 1 5 4; ...
4 5 1 2 3; ...
5 4 1 2 3; ...
4 5 3 2 1; ...
5 4 3 2 1];
You can now randomly select one or more of these rows using randi, and can even create an anonymous function to do it for you:
randPattern = #(n) possible(randi(size(possible, 1), [1 n]), :)
This allows you to select, for example, 5 patterns at random (one per row):
>> patternMat = randPattern(5)
patternMat =
4 5 3 2 1
3 2 1 4 5
4 5 3 2 1
1 2 3 5 4
5 4 3 2 1
You can generate each block and shuffle each one then and set them as members of a cell array and shuffle the cell array and finally convert the cell array to a vector.
b45=[4 5]; % block 1
b13=[1 3]; % block 2
r45 = randperm(2); % indices for shuffling block 1
r13 = randperm(2); % indices for shuffling block 2
r15 = randperm(2); % indices for shuffling the cell
blocks = {b45(r45) [b13(r13(1)) 2 b13(r13(2))]}; % shuffle each block and set them a members of a cell array
result = [blocks{r15}] % shuffle the cell and convert to a vector

Extract blocks of certain number from array

I have a vector and I would like to extract all the 4's from it:
x = [1 1 1 4 4 5 5 4 6 1 2 4 4 4 9 8 4 4 4 4]
so that I will get 4 vectors or a cell containing the 4 blocks of 4's:
[4 4], [4], [4 4 4], [4 4 4 4]
Thanks!
You can create cells from the appropriate ranges using arrayfun:
x = [1 1 1 4 4 5 5 4 6 1 2 4 4 4 9 8 4 4 4 4];
x = [0, x, 0]; D = diff (x==4); % pad array and diff its mask
A = find (D == 1); B = find (D == -1); % find inflection points
out = arrayfun (# (a,b) {x(a+1 : b)}, A, B) % collect ranges in cells
This should be pretty fast, using accumarray:
X = 4;
%// data
x = [1 1 1 4 4 5 5 4 6 1 2 4 4 4 9 8 4 4 4 4]
%// mask all 4
mask = x(:) == X
%// get subs for accumarray
subs = cumsum( diff( [0; mask] ) > 0 )
%// filter data and sort into cell array
out = accumarray( subs(mask), x(mask), [], #(y) {y} )
idx=find(x==4);
for (i= 1:length(idx))
if (i==1 || idx(i-1)!=idx(i)-1)if(i!=1) printf(",") endif; printf("[") endif;
printf("4");
if (i<length(idx)&&idx(i+1)==idx(i)+1) printf(",") else printf("]") endif
endfor
Note this won't give the actual vectors, but it will give the output you wanted. The above is OCtave code. I am pretty sure changing endfor and endif to end would work in MAtlab, but without testing in matlab, I am not positive.[edited in light of comment]
with regionprops we can set property PixelValues so the function returns 1s instead of 4s
x = [1 1 1 4 4 5 5 4 6 1 2 4 4 4 9 8 4 4 4 4]
{regionprops(x==4,'PixelValues').PixelValues}
if we set property PixelIdxList the function returns a cell of indices of 4s:
{regionprops(x==4,'PixelIdxList').PixelIdxList}
Update(without image processing toolbox):
this way we can get number of elements of each connected components:
c = cumsum(x~=4)
h=hist(,c(1):c(end));
h(1)=h(1)+~c(1);
result = h(h~=1)-1

Taking averages of data based on logical filter

we have two columns ('A' and 'B') as follows.
A = [10 5 6 6 10 2 3 2 1 3 2 3 3 7 9 8 6 8 8 12]
B = [10 5 6 6 2 2 3 2 1 3 2 3 3 7 2 2 3 3 8 12]
logicalFilter= ~(B<=3 & B>1)
Now I need to take averages of data points in A corresponding to logicalFilter == 1 for three different blocks of logicalFilter == 1 separately and also ignoring first two points (for example) in A when logicalFilter == 1 in each block for the calculation of averages. How this can be done?
My mentalist skills leading me to this answer:
%// input
A = [10 5 6 6 10 2 3 2 1 3 2 3 3 7 9 8 6 8 8 12]
B = [10 5 6 6 2 2 3 2 1 3 2 3 3 7 2 2 3 3 8 12]
mask = (B<=3 & B>1)
%// get subs and vals for accumarray
C = cumsum(~mask) + 1
[~,~,subs] = unique(C(mask))
val = A(mask)
%// calculate mean starting with 3rd value of group
out = accumarray(subs(:),val(:),[],#(x) mean(x(3:end)) )
out =
2.5000 3.0000 7.0000

Building all possible arrays from vector of subarrays. With recursion [duplicate]

This question already has answers here:
Generate a matrix containing all combinations of elements taken from n vectors
(4 answers)
Closed 8 years ago.
I'm trying to build all possible arrays of length n of a vector of n elements with at least 2 integers in each position. I should be getting 2^n combinations, 16 in this case. My code is generating only half of them, and not saving the output to an array
allinputs = {[1 2] [2 3] [3 4] [5 6]}
A = []
the command I run is
inputArray = inputBuilder(A,[],allinputs,1)
for the function
function inputArray = inputBuilder(A,currBuild, allInputs, currIdx)
if currIdx <= length(allInputs)
for i = 1:length(allInputs{currIdx})
mybuild = [currBuild allInputs{currIdx}(i)];
inputBuilder(A,mybuild,allInputs,currIdx + 1);
end
if currIdx == length(allInputs)
A = [A mybuild];
%debug output
mybuild
end
if currIdx == 1
inputArray = A;
end
end
end
I want all 16 arrays to get output in a vector. Or some easy way to access them all. How can I do this?
EDIT:
Recursion may be a requirement because allinputs will have subarrays of different lengths.
allinputs = {[1] [2 3] [3 4] [5 6 7]}
with this array it will be 1*2*2*3 or 12 possible arrays built
Not sure exactly if this is what you want, but one way of doing what I think you want to do is as follows:
allinputs = {[1 2] [2 3] [3 4] [5 6]};
comb_results = combn([1 2],4);
A = zeros(size(comb_results));
for rowi = 1:size(comb_results, 1)
indices = comb_results(rowi,:);
for idxi = 1:numel(indices)
A(rowi, idxi) = allinputs{idxi}(indices(idxi));
end
end
This gives:
A =
1 2 3 5
1 2 3 6
1 2 4 5
1 2 4 6
1 3 3 5
1 3 3 6
1 3 4 5
1 3 4 6
2 2 3 5
2 2 3 6
2 2 4 5
2 2 4 6
2 3 3 5
2 3 3 6
2 3 4 5
2 3 4 6
combn is here.

Resources