Matrix periodic boundary conditions Matlab - matlab

I need some help, i have a matrix rapresenting points on a grid and when given a element i would like to find the indices of its nearest neighbors keeping in mind that i have periodic boundary conditions, so that if i have the element A(1,1) its nearest neighbors are
A(1,N)
A(2,1)
A(1,2)
A(N, 1)
Where A is my matrix and N is the dimension, and i need a code which will find the indices of n.n of a given element.
Thanks in advance.

Here's my interpretation of your problem:
Given some periodic matrix A:
>> A = magic(4)
A =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
and some element x (example 1), then find the (i,j) indices of the 4 neighbours of x. In this case, the indices (3, 4), (4,3), (4, 1), (1, 4) correspond to 12, 15, 4, 13.
Since I don't know your use case, I don't know in what format the indices are most convenient for you. But as an example, we can write a function neighbors which returns a struct with the 4 indices of the element x.
function out = neighbors(A, x)
[m, n] = size(A);
[i, j] = find(A == x);
mod2 = #(x) mod(x-1, [m, n])+1;
out.down = mod2([i+1, j ]);
out.up = mod2([i-1, j ]);
out.right = mod2([i , j+1]);
out.left = mod2([i , j-1]);
end
We can then run the function as follows.
A = magic(4);
out = neighbors(A, 1);
A(out.left(1), out.left(2)); % this returns 15
A(out.right(1), out.right(2)); % this returns 4
A(out.up(1), out.up(2)); % this returns 12
A(out.down(1), out.down(2)); % this returns 13

Related

Multiply matrix columns with decreasing elements starting by the last column

I have a matrix with following shape:
A = [1 2 3;
4 5 6;
7 8 9]
Now I want starting with the last column to multiply the column with a number and then decrease the number and move to the next column.
So if we start with the number 1 and use for step 0.2 to modify all columns:
Anew = [1*0.6 2*0.8 3*1;
4*0.6 5*0.8 6*1;
7*0.6 8*0.8 9*1]
Or for second example we start with 0.9 with 0.1 as step and modify 3 columns:
B = [1 2 3 4;
5 6 7 8;
9 10 11 12;
13 14 15 16]
And to get:
Bnew = [1 2*0.7 3*0.8 4*0.9;
5 6*0.7 7*0.8 8*0.9;
9 10*0.7 11*0.8 12*0.9;
13 14*0.7 15*0.8 16*0.9]
The matrices might vary in their amount of columns, and I would like to set starting number, ending number, step number and the amount of columns I want to modify.
What you are describing can be achieved with broadcasted element-wise multiplication in matlab R2016b and beyond.
Let's say your inputs are the matrix A, start value start, step size step, and number n. You can start by constructing the factors you want to multiply by. I am going to assume that when n > size(A, 2), you want to just use the first n steps rather than error out:
k = size(A, 2);
n = min(n, k);
factors = ones(1, k);
factors(1 + k - n:end) = linspace(start - (n - 1) * step, start, n);
Now you can just multiply your matrix:
result = A .* factors;
This solution has the advantage of being extremely simple and fully vectorized.
If you have an older version of MATLAB, do the following instead:
result = A .* repmat(factors, size(A, 1), 1);
Or use Tony's trick:
result = A .* factors(ones(3, 1), :)
I just found the solution:
count = 0;
A = randi([-10,10],4,4);
Anew = [];
for i=0.9:-0.1:0
number_columns = 3;
if count == number_columns
rest = existing_columns - count;
for i=rest:-1:1
Anew = [(A(:,i)) Anew];
end
break
end
existing_columns = size(A,1);
Anew = [(A(:,existing_columns-count)*i) Anew];
count = count + 1;
end

Loop through three arrays, apply function to elements and store the outputs in a matrix

I want to loop through different elements of 3 arrays and create a matrix as a function of their values.
My a vector ranges from 1 to 5, my b vector ranges from 1 to 5 and my x vector ranges from 2 to 10 as shown below. Then for particular values from a and b, using the equation y=a*x+b, I want the resulting y vector corresponding to the x values stored in the 1st column vector of the y matrix.
After that, changing a and b one by one, I want the results of different y to be stored in corresponding columns of the y matrix . How can I proceed to achieve that?
Here is the code I tried:
function mathstrial
a = [1:1:5];
b = [1:1:5];
x = [2:2:10];
for e1 = a
for e2 = b
for e3 = x
y = e1*x+e2;
end
end
end
disp(y)
end
I want the result to be
y =
3 4 5 6 7 ..
5 6 7 8 9 ..
7 8 9 10 11 ..
9 10 11 12 13 ..
11 12 13 14 15 ..
...
You can do this without any loops - the more "MATLAB-esque" way of doing things.
% Your a and b, to get combinations as a 5x5 grid we use meshgrid
[a,b] = meshgrid(1:5, 1:5);
% We want to make a 5x5x5 3D matrix, where the 2D layers each use a different value
% for x, and the gridded a and b we just generated. Get the layered x:
x = repmat(reshape(2:2:10, 1, 1, []), 5, 5, 1);
% Now we want the corresponding layered a and b
a = repmat(a, 1, 1, 5); b = repmat(b, 1, 1, 5);
% Now calculate the result, ensuring we use element-wise multiplication .*
y = a.*x + b;
% Reshape to be a 2D array, collapsing the 3rd dimension
y = reshape(y(:,:).', [], 5, 1);
Result as you wanted:
y =
[3, 4, 5, 6, 7
5, 6, 7, 8, 9
7, 8, 9, 10, 11
9, 10, 11, 12, 13
...
41, 42, 43, 44, 45
51, 52, 53, 54, 55]
You could easily make this more generic by using size in place of the 5s to get the appropriate sizes.
you can build up y in a single for loop
a = [1:1:5];
b = [1:1:5];
x = [2:2:10];
y = zeros(5,5,5);
for ct = 1:length(a)
y(:,:,ct) = (a(ct).*x)'+b;
end
with b over the second and a over the third dimension.
Or even in a single unreadable line
y=repmat((a'.*x),[1,1,length(b)])+repmat(permute(b,[1,3,2]),[length(x),length(a),1])
with a over the second, and b over the third dimension
a = [1:1:5];
b = [1:1:5];
x = [2:2:10];
y = zeros(5,5,5);
for i = 1:5
for j = 1:5
for k =1:5
y(i,j,k) = i*x(k)+j
end
end
end
final_y = reshape(y, [5, 25])

Get the first four minimum values in a matrix

I have a matrix:
X =
0 81 13 15 100 2
11 0 6 10 200 8
19 22 0 20 300 23
I want to get the first four minimal values in the whole array X with the indices of each value in the array. For example I should get vector v = [2 6 8 10] and the index of each value in X.
Also, I want to ignore the zero values when the row number equals the column number.
I have tried to use the min and sort functions, but I am not sure how to do it.
I would suggest the following
X2 = X;
X2(~~eye(size(X2))) = inf; %// or X2(logical(eye(size(X2)))) = inf
[val, idx] = sort(X2(:));
result = val(1:4);
[idxRow, idxCol] = ind2sub(size(X), idx(1:4));
Use:
vals = sort(X(~eye(size(X)))); %takes non diagonal values and sort the result
res = vals(1:4) %finds the first 4 elements (which are the smallest)
[row, col] = find(ismember(X,res)); %gets the indices
result:
res = [2; 6; 8; 10]
By The way, if you don't want to ignore all the diagonal values, only the zero ones, use:
vals = sort(X(~eye(size(X)) | (eye(size(X)) & X~=0)));
Sort all but the ones on the diagonal and then find the indices of the ones which are smaller than or equal to the 4th element of sorted array and not on the diagonal:
T=sort(X(~eye(size(X))));
v = T(1:4);
[I,J] = find(X <= v(end) & ~eye(size(X)));
Just want to add to drorco's perfect answer how to find indexes of this first elements:
indexes = arrayfun( #(a) find(X==a), res);
or if you want to get numbers of rows and columns:
[r,c] = arrayfun( #(a) find(X==a), res);
P.S. it works perfectly if all elements except zeros in X are unique.

Find part of vector in another vector matlab

I would like to know if there is an easy way to find the indices of a vector in another vector in matlab:
a = [1 2 3 5 7 10 2 3 6 8 7 5 2 4 7 2 3]
b = [2 3]
So how to get the indices of a when comparing it with b (index of first element is needed)
In this case:
ans = [2 7 16]
Thanks in advance
find(a(1:end-1) == b(1) & a(2:end) == b(2) == 1)
You can re-purpose strfind by converting the elements of both vectors to byte arrays (uint8) with typecast:
bytesPerEl = numel(typecast(a(1),'uint8'));
byteLocs = strfind(char(typecast(a,'uint8')),char(typecast(b,'uint8')));
locsb = (byteLocs-1)/bytesPerEl + 1
locsb =
2 7 16
Just make sure a and b are of the same type. Also note that this works for 1D vectors, not matrixes or higher dimensional arrays.
General approach with length of b arbitrary (not necessarily 2 as in the example), and avoiding the use of strings:
match1 = bsxfun(#eq, a(:), b(:).'); %'// now we just need to make the diagonals
%// horizontal (in order to apply "all" row-wise). For that we'll use indices
%// ind, ind1, ind2
ind = reshape(1:numel(match1), numel(a), numel(b));
ind1 = nonzeros(tril(ind)); %// source indices
ind2 = sort(nonzeros(tril(flipud(ind)))); %// destination indices
match2 = zeros(size(match1));
match2(ind2) = match1(ind1); %// diagonals have become horizontal
result = find(all(match2.'));

average number of different values in a column

I had a question in Matlab. It is so, I try to take average of the different number of values ​​in a column. For example, if we have the column below,
X = [1 1 2 3 4 3 8 2 1 3 5 6 7 7 5]
first I want to start by taking the average of 5 values ​​and plot them. In the case above, I should receive three averages that I could plot. Then take 10 values ​​at a time and so on.
I wonder if you have to write custom code to fix it.
The fastest way is probably to rearrange your initial vector X into some matrix, with each column storing the required values to average:
A = reshape(X, N, []);
where N is the desired number of rows in the new matrix, and the empty brackets ([]) tell MATLAB to calculate the number of columns automatically. Then you can average each column using mean:
X_avg = mean(A);
Vector X_avg stores the result. This can be done in one line like so:
X_avg = mean(reshape(X, N, []));
Note that the number of elements in X has to be divisible by N, otherwise you'll have to either pad it first (e.g with zeroes), or handle the "leftover" tail elements separately:
tail = mod(numel(X), N);
X_avg = mean(reshape(X(1:numel(X) - tail), N, [])); %// Compute average values
X_avg(end + 1) = mean(X(end - tail + 1:end)); %// Handle leftover elements
Later on you can put this code in a loop, computing and plotting the average values for a different value of N in each iteration.
Example #1
X = [1 1 2 3 4 3 8 2 1 3 5 6 7 7 5];
N = 5;
tail = mod(numel(X), N);
X_avg = mean(reshape(X(1:numel(X) - tail), N, []))
X_avg(end + 1) = mean(X(end - tail + 1:end))
The result is:
X_avg =
2.2000 3.4000 6.0000
Example #2
Here's another example (this time the length of X is not divisible by N):
X = [1 1 2 3 4 3 8 2 1 3 5 6 7 7 5];
N = 10;
tail = mod(numel(X), N);
X_avg = mean(reshape(X(1:numel(X) - tail), N, []))
X_avg(end + 1) = mean(X(end - tail + 1:end))
The result is:
X_avg =
2.8000 6.0000
This should do the trick:
For a selected N (the number of values you want to take the average of):
N = 5;
mean_vals = arrayfun(#(n) mean(X(n-1+(1:N))),1:N:length(X))
Note: This does not check if Index exceeds matrix dimensions.
If you want to skip the last numbers, this should work:
mean_vals = arrayfun(#(n) mean(X(n-1+(1:N))),1:N:(length(X)-mod(length(X),N)));
To add the remaining values:
if mod(length(X),N) ~= 0
mean_vals(end+1) = mean(X(numel(X)+1-mod(length(X),N):end))
end
UPDATE: This is a modification of Eitan's first answer (before it was edited). It uses nanmean(), which takes the mean of all values that are not NaN. So, instead of filling the remaining rows with zeros, fill them with NaN, and just take the mean.
X = [X(:); NaN(mod(N - numel(X), N), 1)];
X_avg = nanmean(reshape(X, N, []));
It would be helpful if you posted some code and point out exactly what is not working.
As a first pointer. If
X = [1 1 2 3 4 3 8 2 1 3 5 6 7 7 5]
the three means in blocks of 5 you are interested in are
mean(X(1:5))
mean(X(6:10))
mean(X(11:15))
You will have to come up with a for loop or maybe some other way to iterate through the indices.
I think you want something like this (I didn't use Matlab in a while, I hope the syntax is right):
X = [1 1 2 3 4 3 8 2 1 3 5 6 7 7 5],
currentAmount=5,
block=0,
while(numel(X)<=currentAmount)
while(numel(X)<=currentAmount+block*currentAmount)
mean(X(block*currentAmount+1:block*currentAmount+currentAmount));
block =block+1;
end;
currentAmount = currentAmount+5;
block=0;
end
This code will first loop through all elements calculating means of 5 elements at a time. Then, it will expand to 10 elements. Then to 15, and so on, until the number of elements from which you want to make the mean is bigger than the number of elements in the column.
If you are looking to average K random samples in your N-dimensional vector, then you could use:
N = length(X);
K = 20; % or 10, or 30, or any integer less than or equal to N
indices = randperm(N, K); % gives you K random indices from the range 1:N
result = mean(X(indices)); % averages the values of X at the K random
% indices from above
A slightly more compact form would be:
K = 20;
result = mean(X(randperm(length(X), K)));
If you are just looking to take every K consecutive samples from the list and average them then I am sure one of the previous answers will give you what you want.
If you need to do this operation a lot, it might be worth writing your own function for it. I would recommend using #EitanT's basic idea: pad the data, reshape, take mean of each column. However, rather than including the zero-padded numbers at the end, I recommend taking the average of the "straggling" data points separately:
function m = meanOfN(x, N)
% function m = meanOfN(x, N)
% create groups of N elements of vector x
% and return their mean
% if numel(x) is not a multiple of N, the last value returned
% will be for fewer than N elements
Nf = N * floor( numel( x ) / N ); % largest multiple of N <= length of x
xr = reshape( x( 1:Nf ), N, []);
m = mean(xr);
if Nf < N
m = [m mean( x( Nf + 1:end ) )];
end
This function will return exactly what you were asking for: in the case of a 15 element vector with N=5, it returns 3 values. When the size of the input vector is not a multiple of N, the last value returned will be the "mean of what is left".
Often when you need to take the mean of a set of numbers, it is the "running average" that is of interest. So rather than getting [mean(x(1:5)) mean(x(6:10)) mean(11:15))], you might want
m(1) = mean(x(1:N));
m(2) = mean(x(2:N+1));
m(3) = mean(x(3:N+2));
...etc
That could be achieved using a simple convolution of your data with a vector of ones; for completeness, here is a possible way of coding that:
function m = meansOfN(x, n)
% function m = meansOfN(x, n)
% taking the running mean of the values in x
% over n samples. Returns a row vector of size (sizeof(x) - n + 1)
% if numel(x) < n, this returns an empty matrix
mv = ones(N,1) / N; % vector of ones, normalized
m = convn(x(:), mv, 'valid'); % perform 1D convolution
With these two functions in your path (save them in a file called meanOfN.m and meansOfN.m respectively), you can do anything you want. In any program you will be able to write
myMeans = meanOfN(1:30, 5);
myMeans2 = meansOfN(1:30, 6);
etc. Matlab will find the function, perform the calculation, return the result. Writing your custom functions for specific operations like this can be very helpful - not only does it keep your code clean, but you only have to test the function once...