array manipulation with circshift octave / matlab - arrays

1) I'm trying to shift an array's (outer cells) first and last cell inward at the same time. Here's an inward animation of what I'm trying to do with the array
inward animation. As you can see the outer cells are moving inward (from the ends) at the same time
Here's a pic but the animation shows it much better please note the array could have either an even or odd amount of cells
Inner rotation steps
1 2 3 4 5 6 7
4 1 2 3 6 7 5
3 4 1 2 7 5 6
2) I'm trying to shift an array's middle cells outward using circshift (which I think is fastest) at the same time.
Here's an outward animation of what I'm trying to do also
outward animation. As you can see the middle of the signal is moving outward (left and right) at the same time.
Here's a pic but the animation shows it much better please note the array could have either an even or odd amount of cells
Outer rotation steps
1 2 3 4 5 6 7
2 3 4 1 7 5 6
3 4 1 2 6 5 7
Example: inward
a = (1:7)
y=circshift(A,[0 -2]) %shift end of array inward
3 4 5 6 7 1 2
a = (1:7)
y=circshift(A,[0 2]) %shift beginning of array inward
6 7 1 2 3 4 5
Not to sure how to do the middle cells shifting outward using circshift or the outer cells shifting inward at the same time
I'm not sure about how to start circshift from the center and move the array outwards / inwards to get this effect.
Please note I'm not trying to get this equation I'm just trying to get the arrays to move in the same way. I'm using octave 3.8.1 which is compatible with matlab.

A = 1:7;
split = ceil(numel(A)/2);
n = 2;
A(1:split) = circshift(A(1:split), [0, n]);
A(split+1:end) = circshift(A(split+1:end), [0, -n]);
Put the last three lines in a loop if you like. Also just change the signs of n for inwards or outwards

what about constructing new indices instead of using circshift:
A = 1:7;
halfLen = ceil(length(A)/2); % or use ceil to
idcsOutward = [2:halfLen,1,length(A),(halfLen+1):(length(A)-1)];
B1 = A(idcsOutward)
B2 = B1(idcsOutward)
% and inward:
idcsInward = [halfLen,1:(halfLen-1),(halfLen+2):length(A),halfLen+1];
C1 = A(idcsInward)
C2 = C1(idcsInward)
Result is:
B1 =
2 3 4 1 7 5 6
B2 =
3 4 1 2 6 7 5
C1 =
4 1 2 3 6 7 5
C2 =
3 4 1 2 7 5 6

Related

Creating multidimensional shifting array using a vectorize approach instead of FOR loop

I can "Vectorize" the circshift command but I'm having trouble adding dimensions to it.
See code below with working FOR loop that I'm trying to vectorize using dimensions
clear all,clf reset,tic,clc , close all
function [outMat] = vectcircshift(vectToShift,shiftVector)
%This function generates a matrix where each row is a circshift of the
%original vector from the specified interval in the shiftVector;
%
%Inputs
%vectToShift: is the original vector you want to circshift multiple times
%shiftVector: is the vector of the circshift sizes;
%
%Outputs
%outMat: is a matrix were every row is circshift by the amount in the
% shiftVector
[n,m]=size(vectToShift);
if n>m
inds=(1:n)';
i=toeplitz(flipud(inds),circshift(inds,[1 0]));
outMat=vectToShift(i(shiftVector,:));
outMat=circshift(outMat,[0,-1]); %shift to include original signal first
else
inds=1:m;
i=toeplitz(fliplr(inds),circshift(inds,[0 1]));
outMat=vectToShift(i(shiftVector,:));
outMat=circshift(outMat,[0,-1]); %shift to include original signal first
end
end
%%----Working FOR LOOP below I'm trying to vectorize.
ndim=0;
ndim_tot=[1:3] %total dimensions
for ndim=1:length(ndim_tot)
ndim=ndim+0
if ndim==1
array_sort(ndim,:)=circshift(ndim_tot,[0 ndim-1]) %start at row of sort array
else
array_sort(ndim,:)=circshift(ndim_tot,[0 mod(-ndim,length(ndim_tot))+1]) %next start of row of sort array
endif
array_sort= array_sort(ndim,:)
array_dim(:,:,ndim)=vectcircshift([1:5],array_sort)
endfor
I tired the syntax below but that logic won't work.
ndim_tot=[1:3]; %number of dimensions
array_dim2(:,:,ndim_tot)=vectcircshift([1:5],[1:3])
I get an error nonconformant arguments(op1 is 0x0x1, op2 is 3x5)
My goal is to create a multidimensional array that circshifts a signal / array and also creates and shifts it in multiple dimensions.
Example: of what the multidimensional array would look like
if I start with a signal / array a1=[1 2 3 4 5]
I'm trying to have it create.
array_dim(:,:,1)=
[
1 2 3 4 5
5 1 2 3 4
4 5 1 2 3
]
array_dim(:,:,2)=
[
5 1 2 3 4
4 5 1 2 3
1 2 3 4 5
]
array_dim(:,:,3)=
[
4 5 1 2 3
1 2 3 4 5
5 1 2 3 4
]
Please note: the the numbers won't be sequential I just used it as an example to help explain things a little easier.
PS: I'm using Octave 4.2.2
Not clear why you are shifting in mod 3, but here is a loop assignment using shift
a1=[1 2 3 4 5];
array_dim=zeros(3,5,3);
for i=0:2
array_dim(:,:,i+1)=[shift(a1,i);
shift(a1,mod(i+1,3));
shift(a1,mod(i+2,3))];
endfor
array_dim
and the output fits your example
array_dim =
ans(:,:,1) =
1 2 3 4 5
5 1 2 3 4
4 5 1 2 3
ans(:,:,2) =
5 1 2 3 4
4 5 1 2 3
1 2 3 4 5
ans(:,:,3) =
4 5 1 2 3
1 2 3 4 5
5 1 2 3 4

Does the function movmean in Matlab create an average of all dimensions of the matrix?

I am trying to smooth the temporal history of each pixel in my matrix- in other words, trying to smooth each pixel through both 'space' (mxn) and 'time'(third dimension). I am using the function movmean to create an average of each pixel in time of a 1000x1000x8 matrix.
I am currently using the following code to take an average, using a window size of 5, operating along the third dimension:
av_matrix = movmean(my_matrix,5,3)
This is creating an average as expected, but I'm wondering if the window is just operating in the mxn direction and not taking the average along the third dimension as well.
To compute a moving average along the n dimensions of an n-dimensional array (the "window" is an n-dimensional rectangle), the simplest way is to use convolution (see convn).
You need to be careful with edge effects, that is, when the convolution kernel (or n-dimensional window) partially slides out of the data. What movmean does is average over the actual data points only. To achieve that behaviour you can
compute the sum over the kernel via convolution with the 'same' option; and then
divide each entry by the number of actual data points from which it was computed. This number can also be obtaind via convolution, namely, applying the kernel to an array of ones.
So, all you need is:
my_matrix = randi(9,5,5,3); % example 3D array
sz = [3 3 2]; % 3D window size
av_matrix = convn(my_matrix, ones(sz), 'same') ... % step 1
./convn(ones(size(my_matrix)), ones(sz), 'same'); % step 2
Check:
The following examples use
>> my_matrix
my_matrix(:,:,1) =
6 8 2 1 8
4 6 7 9 8
4 5 1 4 3
5 5 8 7 9
3 6 6 4 9
my_matrix(:,:,2) =
8 8 5 3 6
8 9 6 9 1
9 5 6 2 2
1 7 4 1 2
5 4 7 4 9
my_matrix(:,:,3) =
6 5 8 6 6
1 6 8 6 1
5 5 1 6 7
1 1 2 9 8
1 2 6 1 2
With edge effects:
>> mean(mean(mean(my_matrix(1:2,1:2,1:2))))
ans =
7.125000000000000
>> av_matrix(1,1,1)
ans =
7.125000000000000
Without edge effects:
>> mean(mean(mean(my_matrix(1:3,1:3,1:2))))
ans =
5.944444444444445
>> av_matrix(2,2,1)
ans =
5.944444444444445

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');

How to repeat every 3rd element of a vector?

I have a vector like this:
h = [1,2,3,4,5,6,7,8,9,10,11,12]
And I want to repeat every third element like so:
h_rep = [1,2,3,3,4,5,6,6,7,8,9,9,10,11,12,12]
How do I accomplish this elegantly in MATLAB? The actual arrays are huge, so ideally I don't want to write a for loop. Is there a vectorized way to do this?
One way to do this would be to use the recent repelem function that was released in version R2015b where you can repeat each element in a vector a certain amount of times. In this case, specify a vector where every third element is a 2 with the rest of the values being a 1 as the number of times to repeat the corresponding element, then use the function:
N = numel(h);
rep = ones(1, N);
rep(3:3:end) = 2;
h_rep = repelem(h, rep);
Using your example: h = 1 : 12, we thus get:
>> h_rep
h_rep =
1 2 3 3 4 5 6 6 7 8 9 9 10 11 12 12
If repelem is not available to you, then a clever use of cumsum may help. Basically, note that for every three elements, the next one is a copy of the previous element. If we had an indicator vector of [1 1 1 0] where 1 is the position that we want to copy and 0 tells us to copy the last value, using cumulative sum or cumsum on repeated versions of this vector - exactly 1 + (numel(h) / 4) will give us exactly where we would need to index into h. Therefore, create a vector of ones that is the length of h added with 1 + (numel(h) / 4 to ensure that we make space for the duplicate elements, then make sure every fourth element is set to 0 before applying the cumsum:
N = numel(h);
rep = ones(1, N + 1 + (N / 4));
rep(4:4:end) = 0;
rep = cumsum(rep);
h_rep = h(rep);
Thus:
>> h_rep
h_rep =
1 2 3 3 4 5 6 6 7 8 9 9 10 11 12 12
One last suggestion (thanks to user #bremen_matt) would be to reshape your vector into a matrix so that it has 3 rows, duplicate the last row, then reshape the resulting duplicated matrix back to a single vector:
h_rep = reshape(h, 3, []);
h_rep = reshape([h_rep; h_rep(end,:)], 1, []);
We again get:
>> h_rep
h_rep =
1 2 3 3 4 5 6 6 7 8 9 9 10 11 12 12
Of course the obvious caveat with the above code is that the length of vector h is evenly divisible by 4.
(Modified according to rayryeng's correct observations)...
Another solution is to play around with the reshape function. If you reshape the matrix to a 3xn matrix first...
B = reshape(h,3,[])
And then copy the last row
B = [B;B(end,:)]
And finally vectorize the solution...
B(:).'
You can use just indexing:
h = [1,2,3,4,5,6,7,8,9,10,11,12]; % initial data
n = 3; % step for repetition
h_rep = h(ceil(n/(n+1):n/(n+1):end));
An index-based approach (using sort):
h_rep = h(sort([1:numel(h) 3:3:numel(h)]));
Or a slightly shorter syntax...
h_rep = h(sort([1:end 3:3:end]));
I think this will do it:
h = [1,2,3,4,5,6,7,8,9,10,11,12];
h0=kron(h,[1 1])
h_rep=h0(mod(1:length(h0),2)==0 | mod(1:length(h0),3)==2)
Answer:
1 2 3 3 4 5 6 6 7 8 9 9 10 11 12 12
Explanation:
After duplicating every element, you select only those that you wants. You can extend this idea to duplicate second and third. etc..

average operation in the first 2 of 3 dimensions of a matrix

Suppose A is a 3-D matrix as below (2 rows-2 columns-2 pages).
A(:,:,1)=[1,2;3,4];
A(:,:,2)=[5,6;7,8];
I want to have a vector, say "a", whose inputs are the average of diagonal elements of matrices on each page. So in this simple case, a=[(1+4)/2;(5+8)/2].
But I have difficulties in matlab to do so. I tried the codes below but failed.
mean(A(1,1,:),A(2,2,:))
You can use "partially linear indexing" in the two dimensions that define the diagonal, as follows:
Since partially linear indexing can only be applied on trailing dimensions, you first need to apply permute to rearrange dimensions, so that the first and second dimensions become second and third.
Now you leave the first dimension untouched, linearly-index the diagonals in the second and third dimensions (which effectly reduces those two dimensions to one), and apply mean along the (combined) second dimension.
Code:
B = permute(A, [3 1 2]); %// step 1: permute
result = mean(B(:,1:size(A,1)+1:size(A,1)*size(A,2)), 2); %// step 2: index and mean
In your example,
A(:,:,1)=[1,2;3,4];
A(:,:,2)=[5,6;7,8];
this gives
result =
2.5000
6.5000
You can use bsxfun for a generic solution -
[m,n,r] = size(A)
mean(A(bsxfun(#plus,[1:n+1:n^2]',[0:r-1]*m*n)),1)
Sample run -
>> A
A(:,:,1) =
8 4 1
7 6 3
1 5 8
A(:,:,2) =
1 7 6
8 5 2
1 2 7
A(:,:,3) =
6 2 8
1 1 6
1 4 5
A(:,:,4) =
8 1 6
1 5 1
9 2 7
>> [m,n,r] = size(A);
>> sum(A(bsxfun(#plus,[1:n+1:n^2]',[0:r-1]*m*n)),1)
ans =
22 13 12 20
>> mean(A(bsxfun(#plus,[1:n+1:n^2]',[0:r-1]*m*n)),1)
ans =
7.3333 4.3333 4 6.6667

Resources