Removing array components that are outside given limit - arrays

In matlab I have calculated an array representing a stress field of an elliptic cross-section. That I have done by
% Input
a = 4; b = 2; M = 5;
K = pi*a^3*b^3/(a^2+b^2);
% Stress function
y = linspace(-a,a);
z = linspace(-b,b);
[Y,Z] = meshgrid(y,z);
X = 2*M/K*(a^4*Z.^2+b^4*Y.^2)^(1/2)/(a^2+b^2);
At the same time I have an ellipsis defined as
t = -pi:0.01:pi;
YEllipsis = a*cos(t);
ZEllipsis = b*sin(t);
I need to remove all components of the array X that lies outside the border of the ellipsis defined above. My aim is to plot the contour of the ellipsis by lines, and plot the stress field (X) with contour lines in the same plot.
Any suggestions on how to do that?

Learn about logical indexing. Here's an article that should get you going.
And here's the code to set all the values of X to zero that lie outside the ellipse. (I assume that's what you mean by "remove all components of the array X that lie outside the border", that is the typical way this is done.)
X(y.^2/a^2 + z.^2/b^2 < 1) = 0;
Or, if you really just want that array, you can do it this way:
XNew = X(y.^2/a^2 + z.^2/b^2 < 1);

Related

How do I delete all-zero pages from a 3D matrix in a loop?

How can I delete all-zero pages from a 3D matrix in a loop?
I have come up with the following code, though it is not 'entirely' correct, if at all. I am using MATLAB 2019b.
%pseudo data
x = zeros(3,2,2);
y = ones(3,2,2);
positions = 2:4;
y(positions) = 0;
xy = cat(3,x,y); %this is a 3x2x4 array; (:,:,1) and (:,:,2) are all zeros,
% (:,:,3) is ones and zeros, and (:,:,4) is all ones
%my aim is to delete the arrays that are entirely zeros i.e. xy(:,:,1) and xy(:,:,2),
%and this is what I have come up with; it doesn't delete the arrays but instead,
%all the ones.
for ii = 1:size(xy,3)
for idx = find(xy(:,:,ii) == 0)
xy(:,:,ii) = strcmp(xy, []);
end
end
Use any to find indices of the slices with at least one non-zero value. Use these indices to extract the required result.
idx = any(any(xy)); % idx = any(xy,[1 2]); for >=R2018b
xy = xy(:,:,idx);
I am unsure what you'd expect your code to do, especially given you're comparing strings in all-numerical arrays. Here's a piece of code which does what you desire:
x = zeros(3,2,2);
y = ones(3,2,2);
positions = 2:4;
y(positions) = 0;
xy = cat(3,x,y);
idx = ones(size(xy,3),1,'logical'); % initialise catching array
for ii = 1:size(xy,3)
if sum(nnz(xy(:,:,ii)),'all')==0 % If the third dimension is all zeros
idx(ii)= false; % exclude it
end
end
xy = xy(:,:,idx); % reindex to get rid of all-zero pages
The trick here is that sum(xy(:,:,ii),'all')==0 is zero iff all elements on the given page (third dimension) are zero. In that case, exclude it from idx. Then, in the last row, simply re-index using logical indexing to retain only pages whit at least one non-zero element.
You can do it even faster, without a loop, using sum(a,[1 2]), i.e. the vectorial-dimension sum:
idx = sum(nnz(xy),[1 2])~=0;
xy = xy(:,:,idx);

How to return the leaves of a struct as vector in Matlab?

Often I need to access the leaves of data in a structured array for calculations.
How is this best done in Matlab 2017b?
% Minimal working example:
egg(1).weight = 30;
egg(2).weight = 33;
egg(3).weight = 34;
someeggs = mean([egg.weight]) % works fine
apple(1).properties.weight = 300;
apple(2).properties.weight = 330;
apple(3).properties.weight = 340;
someapples = mean([apple.properties.weight]) %fails
weights = [apple.properties.weight] %fails too
% Expected one output from a curly brace or dot indexing expression,
% but there were 3 results.
If only the top level is a non-scalar structure array, and every entry below is a scalar structure, you can collect the leaves with a call to arrayfun, then do your calculation on the returned vector:
>> weights = arrayfun(#(s) s.properties.weight, apple) % To get the vector
weights =
300 330 340
>> someapples = mean(arrayfun(#(s) s.properties.weight, apple))
someapples =
323.3333
The reason [apple.properties.weight] fails is because dot indexing returns a comma-separated list of structures for apple.properties. You would need to collect this list into a new structure array, then apply dot indexing to that for the next field weight.
You can collect properties into a temporary structure array, and then use it as normal:
apple_properties = [apple.properties];
someapples = mean([apple_properties.weight]) %works
This wouldn't work if you had even more nested levels. Perhaps something like this:
apple(1).properties.imperial.weight = 10;
apple(2).properties.imperial.weight = 15;
apple(3).properties.imperial.weight = 18;
apple(1).properties.metric.weight = 4;
apple(2).properties.metric.weight = 7;
apple(3).properties.metric.weight = 8;
Not that I would advise such a structure, but it works as a toy example. In that case you could, do the same as the previous in two steps... or you could use arrayfun.
weights = arrayfun(#(x) x.properties.metric.weight, apple);
mean(weights)

Interpolating trajectory from unsorted array of 2D points where order matters

I need a way of obtaining a Lx2 trajectory from a Nx2 array of points, i.e. a way of connecting those points into a single trajectory (for example, create a 10000x2 array of points from a 5x2 array of points). I have tried using interp1 and interp2 but either I don't fully understand them or they don't do what I need.
It sounds like you need to be using interp1 in a loop (i.e. to preserve original order) interpolating between each consecutive pair of points:
X = [10; 10.0001; 9; 48]; %// You can consider something like X = [10;10;9;48]; X=X+rand(size(X))*0.0001 instead of dealing with equal X values manually
Y = [10; 20; 50; 6];
m = 3333; %//num points between nodes
n = m*(length(X)-1);
Yi = zeros(n,1);
Xi = [];
for k = 1:length(X)-1
xi = linspace(X(k), X(k+1), m);
Xi = [Xi, xi];
Yi(((k-1)*m+1):k*m) = interp1(X(k:k+1), Y(k:k+1),xi);
end
plot(X,Y,'or');
hold on
plot(Xi,Yi);
To get a pentagon (not a W) try this looping code with these inputs:
X = [0.25; 0.75; 1; 0.5; 0; 0.25];
Y = [0; 0; 1; 1.8; 1; 0];
Result:

Basic MATLAB variable manipulation

I have a dwc = [3001 x 2 double] which more or less is a sin function, I have a for loop finding top values in dwc(:,2). Lets say that there is a top value in dwc(531,2) which way is best way or what is easy to take dwc(531,1) and dwc(531,2) and make an M = [num_of_top_points x 2 double]?
For the following loop, what do I do?
j = 0;
for i = 2:size(dwcL01,1)-1
if dwcL01(i,2) > dwcL01(i-1,2) && dwcL01(i,2) > dwcL01(i+1,2)
j = j+1;
?? = dwcL01(i,:);
end
end
This is how you complete your loop
j = 0;
M = [];
for i = 2:size(dwcL01,1)-1
if dwcL01(i,2) > dwcL01(i-1,2) && dwcL01(i,2) > dwcL01(i+1,2)
j = j+1;
M(j, :) = dwcL01(i, :);
end
end
But you could do this much more efficiently by vectorizing
%//Some example data
x = -4*pi:0.5:4*pi;
y = cos(x);
dwcL01 = [x(:), y(:)]; %// (:) just makes it a column
%// Finding the peaks using diff and sign. Note that I add the first element to the beginning as diff reduces the size by one so this prevents offsetting
F = diff(sign(diff([dwcL01(1,2);dwcL01(:,2)]))) < 0;
M = [dwcL01(F,:)', dwcL01(F,:)'];
plot(x, y, M(:,1), M(:,2), '*r')
How that works is first we find the difference of each element consecutive element pair. Now when the sign changes, that means we've hit a max or min. If the sign change is negative then the gradient went from positive to negative which is a max. So I use diff(sign()) to find the points where the sign changes and then > 0 to create a logical matrix with false everywhere expect for the max. Then I use logical indexing to extract the max.
You could append it to a matrix (let's call it dwcL01_max) - this isn't the fastest way because the matrix size changes each loop but it works:
dwcL01_max = [dwcL01_max dwcL01(i,:)];
The other option would be to use the builtin findpeaks (from the signal proc toolbox)
[~, dwcL01_peaks] = findpeaks(dwcL01(:,2));
dwcL01_max = dwcL01(dwcL01_peaks, :);

store data in array from loop in matlab

I want to store data coming from for-loops in an array. How can I do that?
sample output:
for x=1:100
for y=1:100
Diff(x,y) = B(x,y)-C(x,y);
if (Diff(x,y) ~= 0)
% I want to store these values of coordinates in array
% and find x-max,x-min,y-max,y-min
fprintf('(%d,%d)\n',x,y);
end
end
end
Can anybody please tell me how can i do that. Thanks
Marry
So you want lists of the x and y (or row and column) coordinates at which B and C are different. I assume B and C are matrices. First, you should vectorize your code to get rid of the loops, and second, use the find() function:
Diff = B - C; % vectorized, loops over indices automatically
[list_x, list_y] = find(Diff~=0);
% finds the row and column indices at which Diff~=0 is true
Or, even shorter,
[list_x, list_y] = find(B~=C);
Remember that the first index in matlab is the row of the matrix, and the second index is the column; if you tried to visualize your matrices B or C or Diff by using imagesc, say, what you're calling the X coordinate would actually be displayed in the vertical direction, and what you're calling the Y coordinate would be displayed in the horizontal direction. To be a little more clear, you could say instead
[list_rows, list_cols] = find(B~=C);
To then find the maximum and minimum, use
maxrow = max(list_rows);
minrow = min(list_rows);
and likewise for list_cols.
If B(x,y) and C(x,y) are functions that accept matrix input, then instead of the double-for loop you can do
[x,y] = meshgrid(1:100);
Diff = B(x,y)-C(x,y);
mins = min(Diff);
maxs = max(Diff);
min_x = mins(1); min_y = mins(2);
max_x = maxs(1); max_y = maxs(2);
If B and C are just matrices holding data, then you can do
Diff = B-C;
But really, I need more detail before I can answer this completely.
So: are B and C functions, matrices? You want to find min_x, max_x, but in the example you give that's just 1 and 100, respectively, so...what do you mean?

Resources