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

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

Related

Reshape the array along the reverse dimensions

>> A = [ 1 2 3 3 4 5 5 6 7 7 8 9 ];
>>
>> B = reshape(A, 2, 2, 3)
B(:,:,1) =
1 3
2 3
B(:,:,2) =
4 5
5 6
B(:,:,3) =
7 8
7 9
Since reshape can only change the size of the given array in the way of preserving the linear indices, however I would like to reshape the array along the reverse dimensions.
For example, convert A into
>> C = reverse-reshape(A, 2, 2, 3) % not required to be only one function
C(:,:,1) =
1 3
5 7
C(:,:,2) =
2 4
6 8
C(:,:,3) =
3 5
7 9
Is there any better method than writing loops and fill numbers one by one in version R2017b?
You would first reshape with the dimensions in reverse order, then swap the first and third dimensions with permute to reorder the elements so that they are populated in reverse order:
>> B = permute(reshape(A, 3, 2, 2), [3 2 1])
B(:,:,1) =
1 3
5 7
B(:,:,2) =
2 4
6 8
B(:,:,3) =
3 5
7 9
To do this in general independent of the matrix dimensions and assuming it is a 3D matrix, declare an array called dims that contains the output desired matrix size, reverse the elements and supply this into reshape:
dims = [2 2 3];
B = permute(reshape(A, fliplr(dims)), [3 2 1]);
fliplr reverses the elements in a matrix horizontally.

Find an element in a specific row and column in a 2D array

I'm having trouble in looking for an element in a specific.
I have the array
A = [ 1 2 3 7 2 ; 2 8 5 7 2; 1 9 8 4 1; 8 7 2 10 9; 10 9 4 3 8]
I just want to get the index of A(3,4) for the element of 4. However my code spits out the two locations of the element 4, which is A(5,3) and A(3,4).
I used [row, col] = find(E==4)
Use
[row, col] = find(E==4, 1)
The second parameter is the number of elements you want to find. Find more details at https://www.mathworks.com/help/matlab/ref/find.html
Matlab searches a matrix in column-by-column order. If you want to find the first element by rows, you could transpose E before calling find. But you need to swap the resulting indices then:
[col, row] = find(E'==4, 1)
Here's a lengthier, iterative way to find the first index:
A = [ 1 2 3 7 2 ;...
2 8 5 7 2;...
1 9 8 4 1;...
8 7 2 10 9;...
10 9 4 3 8];
[a,b] = size(A);
for i = 1:a
for j = 1:b
if A(i,j) == 4
break
end
end
if A(i,j) == 4
break
end
end
index = [i,j]
It returned [3, 4] for me.

Efficient "window-select" array blocks?

Suppose I have the following array:
x = [a b
c d
e f
g h
i j];
I want to "swipe a window of two rows" progressively (one row at a time) along the array to generate the following array:
y = [a b c d e f g h
c d e f g h i j];
What is the most efficient way to do this? I don't want to use cellfun or arrayfun or for loops.
im2col is going to be your best bet here if you have the Image Processing Toolbox.
x = [1 2
3 4
5 6
7 8];
im2col(x.', [1 2])
% 1 2 3 4 5 6
% 3 4 5 6 7 8
If you don't have the Image Processing Toolbox, you can also easily do this with built-ins.
reshape(permute(cat(3, x(1:end-1,:), x(2:end,:)), [3 2 1]), 2, [])
% 1 2 3 4 5 6
% 3 4 5 6 7 8
This combines the all rows with the next row by concatenating a row-shifted version along the third dimension. Then we use permute to shift the dimensions around and then we reshape it to be the desired size.
If you don't have the Image Processing Toolbox, you can do this using simple indexing:
x =
1 2
3 4
5 6
7 8
9 10
y = x.'; %% Transpose it, for simplicity
z = [y(1:end-2); y(3:end)] %% Take elements 1:end-2 and 3:end and concatenate them
z =
1 2 3 4 5 6 7 8
3 4 5 6 7 8 9 10
You can do the transposing and reshaping in a simple step (see Suever's edit), but the above might be easier to read, understand and debug for beginners.
Here's an approach to solve it for a generic case of selecting L rows per window -
[m,n] = size(x) % Store size
% Extend rows by indexing into them with a progressive array of indices
x_ext = x(bsxfun(#plus,(1:L)',0:m-L),:);
% Split the first dim at L into two dims, out of which "push" back the
% second dim thus created as the last dim. This would bring in the columns
% as the second dimension. Then, using linear indexing reshape into the
% desired shape of L rows for output.
out = reshape(permute(reshape(x_ext,L,[],n),[1,3,2]),L,[])
Sample run -
x = % Input array
9 1
3 1
7 5
7 8
4 9
6 2
L = % Window length
3
out =
9 1 3 1 7 5 7 8
3 1 7 5 7 8 4 9
7 5 7 8 4 9 6 2

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

Vectorized Reshaping of Columns in an Array

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.

Resources