Vectorized Reshaping of Columns in an Array - arrays

I have an array A, and want to reshape the last four elements of each column into a 2x2 matrix. I would like the results to be stored in a cell array B.
For example, given:
A = [1:6; 3:8; 5:10]';
I would like B to contain three 2x2 arrays, such that:
B{1} = [3, 5; 4, 6];
B{2} = [5, 7; 6, 8];
B{3} = [7, 9; 8, 10];
I can obviously do this in a for loop using something like reshape(A(end-3:end, ii), 2, 2) and looping over ii. Can anyone propose a vectorized method, perhaps using something similar to cellfun that can apply an operation repeatedly to columns of an array?

The way I do this is to look at the desired indices and then figure out a way to generate them, usually using some form of repmat. For example, if you want the last 4 items in each column, the (absolute) indices into A are going to be 3,4,5,6, then add the number of rows to that to move to the next column to get 9,10,11,12 and so on. So the problem becomes generating that matrix in terms of your number of rows, number of columns, and the number of elements you want from each column (I'll call it n, in your case n=4).
octave:1> A = [1:6; 3:8; 5:10]'
A =
1 3 5
2 4 6
3 5 7
4 6 8
5 7 9
6 8 10
octave:2> dim=size(A)
dim =
6 3
octave:3> n=4
n = 4
octave:4> x=repmat((dim(1)-n+1):dim(1),[dim(2),1])'
x =
3 3 3
4 4 4
5 5 5
6 6 6
octave:5> y=repmat((0:(dim(2)-1)),[n,1])
y =
0 1 2
0 1 2
0 1 2
0 1 2
octave:6> ii=x+dim(1)*y
ii =
3 9 15
4 10 16
5 11 17
6 12 18
octave:7> A(ii)
ans =
3 5 7
4 6 8
5 7 9
6 8 10
octave:8> B=reshape(A(ii),sqrt(n),sqrt(n),dim(2))
B =
ans(:,:,1) =
3 5
4 6
ans(:,:,2) =
5 7
6 8
ans(:,:,3) =
7 9
8 10
Depending on how you generate x and y, you can even do away with the multiplication, but I'll leave that to you. :D
IMO you don't need a cell array to store them either, a 3D matrix works just as well and you index into it the same way (but don't forget to squeeze it before you use it).
I gave a similar answer in this question.

Related

Matrix transformation in MATLAB

For example, I have a matrix A (Figure 1). When the variable n = 2, I want it to be transformed to the matrix B. The red rectangle shows the transformation rule of every column. According to this rule, when the n = 3, it can become the matrix C.
I have written a script using a for loop method, but it is a waste of time when the matrix A is very large (e.g. 11688* 140000). Is there an efficient way to solve this problem?
Figure 1:
Here is a way using reshape and implicit expansion:
result = reshape(A((1:size(A,1)-n+1) + (0:n-1).', :), n, []);
For example assume that n = 3. Implicit expansion is used to extract indices of rows:
row_ind = (1:size(A,1)-n+1) + (0:n-1).';
The following matrix is created:
1 2
2 3
3 4
Extract the desired rows of A:
A_expanded = A(row_ind, :)
When the matrix row_ind is used as an index it behaves like a vector:
1
2
1 2 3
2 3 -> 2
3 4 3
4
A_expanded =
3 5 7
6 8 9
2 6 3
6 8 9
2 6 3
1 2 1
Now A_expanded can be reshaped to the desired size:
result = reshape(A_expanded, n, []);
>>result =
3 6 5 8 7 9
6 2 8 6 9 3
2 1 6 2 3 1
If you have the Image Processing Toolbox you can use im2col as follows:
result = im2col(A, [n 1], 'sliding');

Pairs of random numbers Matlab

I am trying to generate random numbers between 1 and 6 using Matlab's randperm and calling randperm = 6.
Each time this gives me a different array let's say for example:
x = randperm(6)
x = [3 2 4 1 5 6]
I was wondering if it was possible to create pairs of random numbers such that you end up with x like:
x = [3 4 1 2 5 6]
I need the vector to be arranged such that 1 and 2 are always next to each other, 3 and 4 next to each other and 5 and 6 next to each other. As I'm doing something in Psychtoolbox and this order is important.
Is it possible to have "blocks" of random order? I can't figure out how to do it.
Thanks
x=1:block:t ; %Numbers
req = bsxfun(#plus, x(randperm(t/block)),(0:block-1).'); %generating random blocks of #
%or req=x(randperm(t/block))+(0:block-1).' ; if you have MATLAB R2016b or later
req=req(:); %reshape
where,
t = total numbers
block = numbers in one block
%Sample run with t=12 and block=3
>> req.'
ans =
10 11 12 4 5 6 1 2 3 7 8 9
Edit:
If you also want the numbers within each block in random order, add the following 3 lines before the last line of above code:
[~, idx] = sort(rand(block,t/block)); %generating indices for shuffling
idx=bsxfun(#plus,idx,0:block:(t/block-1)*block); %shuffled linear indices
req=req(idx); %shuffled matrix
%Sample run with t=12 and block=3
req.'
ans =
9 8 7 2 3 1 12 10 11 5 6 4
I can see a simple 3 step process to get your desired output:
Produce 2*randperm(3)
Double up the values
Add randperm(2)-2 (randomly ordered pair of (-1,0)) to each pair.
In code:
x = randperm(3)
y = 2*x([1 1 2 2 3 3])
z = y + ([randperm(2),randperm(2),randperm(2)]-2)
with result
x = 3 1 2
y = 6 6 2 2 4 4
z = 6 5 2 1 3 4

Matlab delete specific elements from an array

I have for example a=[1 2 3 4 5 6 7 8 9 10]; and I have to delete each 2 following numbers from 3.
like at the end it should be a=[1 4 7 10];
How to do this without a for loop.
And also if there is a way to guarantee that at the end the resulting array will have exact number of entries, like here it should be a with 4 entries at the end.
But for example we have b=[1 2 3 4 5 6 7 8 9 ]; and if I want make sure that at the end I still have 4 entries in the rest array, so that b can't be equal to [1 4 7] because I need 4 entries for sure.
You can use indexing for this:
A = 1:10;
B = A(1:3:end)
B =
[1 4 7 10]
Or, if you really want to remove elements:
A = 1:10;
A(2:3:end) = [];
A(3:3:end) = [];
For your second question regarding length checking, it's unclear what you're asking. Would an if comparison be enough ?
if numel(A) ~= 4
% ... Handle unexpected values here
end
Best,
As you mentioned in the question and in the comments that you need 4 elements at the end and if elements are less than 4 then you want to include the last element/s of b, the following should work:-
b=[1 2 3 4 5 6 7 8 9]
b_req=b(1:3:end);
temp=length(b_req);
if temp<4 b_req(end+1:4)=b(end-3+temp:end); % for including the elements of b so that total elements are 4 at the end
elseif temp>4 b_req=b_req(1:4); % for removing the extra elements
end
b_req
Output:-
b =
1 2 3 4 5 6 7 8 9
b_req =
1 4 7 9
and
if instead b=[1 2 3 4 5 6 7 8 9 10]; then the same code gives what you require, i.e. b_req = [1 4 7 10]
This code speaks for itself:
a = 1:15; % some vector
% returns every third element after the first one:
third_elemets = a(1:3:end);
% returns the missing elements for the vector to be in size 4 from the end of a
last_elements = a(end-3+length(third_elemets):end);
% take maximum 4 elements from a
logic_ind = true(min(4,length(third_elemets)),1);
% and concatanate them with last_elements (if there are any)
a = [third_elemets(logic_ind) last_elements]
and under the assumption that whenever there are less than 4 elements you simply take the last one(s) - it should always work.

Remove one element from each row of a matrix, each in a different column

How can I remove elements in a matrix, that aren't all in a straight line, without going through a row at a time in a for loop?
Example:
[1 7 3 4;
1 4 4 6;
2 7 8 9]
Given a vector (e.g. [2,4,3]) How could I remove the elements in each row (where each number in the vector corresponds to the column number) without going through each row at a time and removing each element?
The example output would be:
[1 3 4;
1 4 4;
2 7 9]
It can be done using linear indexing at follows. Note that it's better to work down columns (because of Matlab's column-major order), which implies transposing at the beginning and at the end:
A = [ 1 7 3 4
1 4 4 6
2 7 8 9 ];
v = [2 4 3]; %// the number of elements of v must equal the number of rows of A
B = A.'; %'// transpose to work down columns
[m, n] = size(B);
ind = v + (0:n-1)*m; %// linear index of elements to be removed
B(ind) = []; %// remove those elements. Returns a vector
B = reshape(B, m-1, []).'; %'// reshape that vector into a matrix, and transpose back
Here's one approach using bsxfun and permute to solve for a 3D array case, assuming you want to remove indexed elements per row across all 3D slices -
%// Inputs
A = randi(9,3,4,3)
idx = [2 4 3]
%// Get size of input array, A
[M,N,P] = size(A)
%// Permute A to bring the columns as the first dimension
Ap = permute(A,[2 1 3])
%// Per 3D slice offset linear indices
offset = bsxfun(#plus,[0:M-1]'*N,[0:P-1]*M*N) %//'
%// Get 3D array linear indices and remove those from permuted array
Ap(bsxfun(#plus,idx(:),offset)) = []
%// Permute back to get the desired output
out = permute(reshape(Ap,3,3,3),[2 1 3])
Sample run -
>> A
A(:,:,1) =
4 4 1 4
2 9 7 5
5 9 3 9
A(:,:,2) =
4 7 7 2
9 6 6 9
3 5 2 2
A(:,:,3) =
1 7 5 8
6 2 9 6
8 4 2 4
>> out
out(:,:,1) =
4 1 4
2 9 7
5 9 9
out(:,:,2) =
4 7 2
9 6 6
3 5 2
out(:,:,3) =
1 5 8
6 2 9
8 4 4

Matlab- moving numbers to new row if condition is met

I have a variable like this that is all one row:
1 2 3 4 5 6 7 8 9 2 4 5 6 5
I want to write a for loop that will find where a number is less than the previous one and put the rest of the numbers in a new row, like this
1 2 3 4 5 6 7 8 9
2 4 5 6
5
I have tried this:
test = [1 2 3 4 5 6 7 8 9 2 4 5 6 5];
m = zeros(size(test));
for i=1:numel(test)-1;
for rows=1:size(m,1)
if test(i) > test(i+1);
m(i+1, rows+1) = test(i+1:end)
end % for rows
end % for
But it's clearly not right and just hangs.
Let x be your data vector. What you want can be done quite simply as follows:
ind = [find(diff(x)<0) numel(x)]; %// find ends of increasing subsequences
ind(2:end) = diff(ind); %// compute lengths of those subsequences
y = mat2cell(x, 1, ind); %// split data vector according to those lenghts
This produces the desired result in cell array y. A cell array is used so that each "row" can have a different number of columns.
Example:
x = [1 2 3 4 5 6 7 8 9 2 4 5 6 5];
gives
y{1} =
1 2 3 4 5 6 7 8 9
y{2} =
2 4 5 6
y{3} =
5
If you are looking for a numeric array output, you would need to fill the "gaps" with something and filling with zeros seem like a good option as you seem to be doing in your code as well.
So, here's a bsxfun based approach to achieve the same -
test = [1 2 3 4 5 6 7 8 9 2 4 5 6 5] %// Input
idx = [find(diff(test)<0) numel(test)] %// positions of row shifts
lens = [idx(1) diff(idx)] %// lengths of each row in the proposed output
m = zeros(max(lens),numel(lens)) %// setup output matrix
m(bsxfun(#le,[1:max(lens)]',lens)) = test; %//'# put values from input array
m = m.' %//'# Output that is a transposed version after putting the values
Output -
m =
1 2 3 4 5 6 7 8 9
2 4 5 6 0 0 0 0 0
5 0 0 0 0 0 0 0 0

Resources