My question is pretty standard but can't find a solution of that.
I have points=[x,y,z] and want to plot best fit line.
I am using function given below (and Thanx Smith)
% LS3DLINE.M Least-squares line in 3 dimensions.
%
% Version 1.0
% Last amended I M Smith 27 May 2002.
% Created I M Smith 08 Mar 2002
% ---------------------------------------------------------------------
% Input
% X Array [x y z] where x = vector of x-coordinates,
% y = vector of y-coordinates and z = vector of
% z-coordinates.
% Dimension: m x 3.
%
% Output
% x0 Centroid of the data = point on the best-fit line.
% Dimension: 3 x 1.
%
% a Direction cosines of the best-fit line.
% Dimension: 3 x 1.
%
% <Optional...
% d Residuals.
% Dimension: m x 1.
%
% normd Norm of residual errors.
% Dimension: 1 x 1.
% ...>
%
% [x0, a <, d, normd >] = ls3dline(X)
I have a.
So equation may be
points*a+dist=0
where dist is min. distance from origon.
Now my question is how to plot best filt line in 3D.
It helps to actually read the content of the function, which uses Singular Value Decomposition.
% calculate centroid
x0 = mean(X)';
% form matrix A of translated points
A = [(X(:, 1) - x0(1)) (X(:, 2) - x0(2)) (X(:, 3) - x0(3))];
% calculate the SVD of A
[U, S, V] = svd(A, 0);
% find the largest singular value in S and extract from V the
% corresponding right singular vector
[s, i] = max(diag(S));
a = V(:, i);
The best orthogonal fitting line is
P = x0 + a.*t
as the parameter t varies. This is the direction of maximum variation which means that variation in the orthogonal direction is minimum. The sum of the squares of the points' orthogonal distances to this line is minimized.
This is distinct from linear regression which minimizes the y variation from the line of regression. That regression assumes that all errors are in the y coordinates, whereas orthogonal fitting assumes the errors in both the x and y coordinates are of equal expected magnitudes.
[Credit: Roger Stafford , http://www.mathworks.com/matlabcentral/newsreader/view_thread/294030]
Then you only need to create some t and plot it:
for t=0:100,
P(t,:) = x0 + a.*t;
end
scatter3(P(:,1),P(:,2),P(:,3));
You may want to use plot3() instead, in which case you need only a pair of points. Since a line is infinite by definition, it is up to you to determine where it should begin and end (depends on application).
Related
I'm trying to generate a 1D mesh with unequal step length, and with a fixed number of elements [same as the initial mesh].
The length should be proportional to a node density. In the example, this density is inversely proportional to the gradient of a function.
[imagine for example that you have a distribution of the temperature in a 1D mesh, and you want to make the mesh denser in the parts of the mesh where the temperature gradient is higher]
This is what I coded so far:
% % % Initial fixed-step 1D mesh
X=(0:.01:2)';
% % % Values of a function at each mesh node [in this example, f(x)=5*sin(2*pi*x)*x ]
Y=5*sin(2*pi*X).*X;
% % % Calculate density of mesh points based on the Y function gradient
rho=[1e-9; abs(diff(Y))];
% % % Calculate x-steps from the original mesh
h = diff(X);
% % % Rescale the steps based on the inverse of the density
F = cumsum([0; h]./rho);
% % % Make sure F is between 0 and 1
F = F/F(end);
% % % Calculate the new mesh with scaled steps
X2 = X(1) + F * (X(end)-X(1));
% % % Interpolate the function Y at the new positions
Y2 = interp1(X,Y,X2);
% % % Plot
figure
subplot(2,1,1)
hold on
plot(X,Y,'ko-')
plot(X2,Y2,'r.-')
xlabel('x')
ylabel('y')
subplot(2,1,2)
plot(X,rho,'ko-')
xlabel('x')
ylabel('rho')
There are a few problems with this approach:
1. as you see from this example, there are big jumps when the density is very low (gradient almost zero). How could I implement a minimum/maximum time step size?
2. the node density is calculated correctly, but after "integrating" the unequal steps it can happen that the imposed large time step when the gradient is small causes to skip a high-gradient region that should have finer time-steps. [for example, please take a look at the region 1.8-1.9 in the example below, which should have small time step because it has high node density, but the large step size at ~1.75 causes to skip a large section of X]
Any suggestion to improve my code will be appreciated!
Calculate the cumulative sum (CDF) of rho. Take equally spaced samples from the CDF. Map from CDF to X to get new X3. Map from X3 to Y to get Y3:
CDF = cumsum(rho);
eq_smpl = linspace(CDF(1), CDF(end), numel(CDF)+1).';
eq_smpl = eq_smpl(1:end-1) + diff(eq_smpl)/2; %use center points
X3 = interp1(CDF, X, eq_smpl);
Y3 = interp1(X, Y, X3);
plot(X3,Y3,'ro-')
hold on
plot(X,Y,'k.')
The third subplot shows the the result.
rahnema1's answer gave me a huge help, but there were still two remaining issues:
1- the first element of the new mesh is not identical to the first element of the original mesh
2- in case the gradient is zero at some point, the interp1 function will give error ["The grid vectors are not strictly monotonic increasing."]
For the 1st point, I replaced the two lines defining eq_smpl with the following line:
eq_smpl = linspace(CDF(1), CDF(end), numel(CDF))';
[taking as many elements as CDF, and not centering the points]
For the 2nd point, I added a line after the calculation of rho to replace eventual 0 with small non-zero values:
rho(rho==0)=1e-12;
The final code that does what I want is the following:
% % % Initial fixed-step 1D mesh
X=(0:.01:2)';
% % % Values of a function at each mesh node [in this example, f(x)=5*sin(2*pi*x)*x ]
Y=5*sin(2*pi*X).*X;
% % % Calculate density of mesh points based on the Y function gradient
rho=[0; abs(diff(Y)./abs(diff(X)))];
% % % Replace eventual 0 with small non-zero values
rho(rho==0)=1e-12;
CDF = cumsum(rho);
eq_smpl = linspace(CDF(1), CDF(end), numel(CDF))';
% % % Calculate new mesh
X3 = interp1(CDF, X, eq_smpl);
% % % Interpolate the function Y at the new positions
Y3 = interp1(X, Y, X3);
% % % Plot
figure
subplot(2,1,1)
hold on
plot(X,Y,'ko-')
plot(X3,Y3,'r.-')
xlabel('x')
ylabel('y')
subplot(2,1,2)
plot(X,rho,'ko-')
xlabel('x')
ylabel('rho')
Thank you again to rahnema1 for providing 90% of the answer [probably I didn't explain very well the original request]!
Can anyone explain the two lines of code highlighted below which use repmat? This is taken directly from the MathWorks documentation for learning data analysis:
bin_counts = hist(c3); % Histogram bin counts
N = max(bin_counts); % Maximum bin count
mu3 = mean(c3); % Data mean
sigma3 = std(c3); % Data standard deviation
hist(c3) % Plot histogram
hold on
plot([mu3 mu3],[0 N],'r','LineWidth',2) % Mean
% --------------------------------------------------------------
X = repmat(mu3+(1:2)*sigma3,2,1); % WHAT IS THIS?
Y = repmat([0;N],1,2); % WHY IS THIS NECESSARY?
% --------------------------------------------------------------
plot(X,Y,'g','LineWidth',2) % Standard deviations
legend('Data','Mean','Stds')
hold off
Could anyone explain the X = repmat(...) line to me? I know it will be plotted for the 1 and 2 standard deviation lines.
Also, I tried commenting out the Y = ... line, and the plot looks the exact same, so what is the purpose of this line?
Thanks
Lets break the expression into multiple statements
X = repmat(mu3+(1:2)*sigma3,2,1);
is equivalent to
% First create a row vector containing one and two standard deviations from the mean.
% This is equivalent to xvals = [mu3+1*sigma3, mu3+2*sigma3];
xval = mu3 + (1:2)*sigma3;
% Repeat the matrix twice in the vertical dimension. We want to plot two vertical
% lines so the first and second point should be equal so we just use repmat to repeat them.
% This is equivalent to
% X = [xvals;
% xvals];
X = repmat(xval,2,1);
% To help understand how repmat works, if we had X = repmat(xval,3,2) we would get
% X = [xval, xval;
% xval, xval;
% xval, xval];
The logic is similar for the Y matrix except it repeats in the column direction. Together you end up with
X = [mu3+1*sigma3, mu3+2*sigma3;
mu3+1*sigma3, mu3+2*sigma3];
Y = [0, 0;
N, N];
When plot is called it plots one line per column of the X and Y matrices.
plot(X,Y,'g','LineWidth',2);
is equivalent to
plot([mu3+1*sigma3; mu3+1*sigma3], [0, N], 'g','LineWidth',2);
hold on;
plot([mu3+2*sigma3; mu3+2*sigma3], [0, N], 'g','LineWidth',2);
which plots two vertical lines, one and two standard deviations from the mean.
If you comment out Y then Y isn't defined. The reason the code still worked is probably that the previous value of Y was still stored in the workspace. If you run the command clear before running the script again you will find that the plot command will fail.
I found an example using triangular elements here. I then went on to modify the way it generates the mesh to replace the triangular elements with rectangular ones, but I don't know how to integrate over them. Here is my version of it:
%3.6 femcode.m
% [p,t,b] = squaregrid(m,n) % create grid of N=mn nodes to be listed in p
% generate mesh of T=2(m-1)(n-1) right triangles in unit square
m=6; n=5; % includes boundary nodes, mesh spacing 1/(m-1) and 1/(n-1)
[x,y]=ndgrid((0:m-1)/(m-1),(0:n-1)/(n-1)); % matlab forms x and y lists
p=[x(:),y(:)]; % N by 2 matrix listing x,y coordinates of all N=mn nodes
nelems=(m-1)*(n-1);
t=zeros(nelems,3);
for e=1:nelems
t(e) = e + floor(e/6);
t(e, 2) = e + floor(e/6) + 1;
t(e, 3) = e + floor(e/6) + 6;
t(e, 4) = e + floor(e/6) + 7;
end
% final t lists 4 node numbers of all rectangles in T by 4 matrix
b=[1:m,m+1:m:m*n,2*m:m:m*n,m*n-m+2:m*n-1]; % bottom, left, right, top
% b = numbers of all 2m+2n **boundary nodes** preparing for U(b)=0
% [K,F] = assemble(p,t) % K and F for any mesh of rectangles: linear phi's
N=size(p,1);T=nelems; % number of nodes, number of rectangles
% p lists x,y coordinates of N nodes, t lists rectangles by 4 node numbers
K=sparse(N,N); % zero matrix in sparse format: zeros(N) would be "dense"
F=zeros(N,1); % load vector F to hold integrals of phi's times load f(x,y)
for e=1:T % integration over one rectangular element at a time
nodes=t(e,:); % row of t = node numbers of the 3 corners of triangle e
Pe=[ones(4,1),p(nodes,:),p(nodes,1).*p(nodes,2)]; % 4 by 4 matrix with rows=[1 x y xy]
Area=abs(det(Pe)); % area of triangle e = half of parallelogram area
C=inv(Pe); % columns of C are coeffs in a+bx+cy to give phi=1,0,0 at nodes
% now compute 3 by 3 Ke and 3 by 1 Fe for element e
grad=C(2:3,:);Ke=Area*grad'*grad; % element matrix from slopes b,c in grad
Fe=Area/3; % integral of phi over triangle is volume of pyramid: f(x,y)=1
% multiply Fe by f at centroid for load f(x,y): one-point quadrature!
% centroid would be mean(p(nodes,:)) = average of 3 node coordinates
K(nodes,nodes)=K(nodes,nodes)+Ke; % add Ke to 9 entries of global K
F(nodes)=F(nodes)+Fe; % add Fe to 3 components of load vector F
end % all T element matrices and vectors now assembled into K and F
% [Kb,Fb] = dirichlet(K,F,b) % assembled K was singular! K*ones(N,1)=0
% Implement Dirichlet boundary conditions U(b)=0 at nodes in list b
K(b,:)=0; K(:,b)=0; F(b)=0; % put zeros in boundary rows/columns of K and F
K(b,b)=speye(length(b),length(b)); % put I into boundary submatrix of K
Kb=K; Fb=F; % Stiffness matrix Kb (sparse format) and load vector Fb
% Solving for the vector U will produce U(b)=0 at boundary nodes
U=Kb\Fb % The FEM approximation is U_1 phi_1 + ... + U_N phi_N
% Plot the FEM approximation U(x,y) with values U_1 to U_N at the nodes
% surf(p(:,1),p(:,2),0*p(:,1),U,'edgecolor','k','facecolor','interp');
% view(2),axis equal,colorbar
I started to edit the section of the code which used to integrate over triangular elements but I'm not sure how to proceed or if it even is done in a similar way for rectangular elements.
UPDATE
So I've tried to incorporate what Dohyun suggested with my limited understanding and here's what I've got now:
m=12; n=12; % includes boundary nodes, mesh spacing 1/(m-1) and 1/(n-1)
[x,y]=ndgrid((0:m-1)/(m-1),(0:n-1)/(n-1)); % matlab forms x and y lists
p=[x(:),y(:)]; % N by 2 matrix listing x,y coordinates of all N=mn nodes
nelems=(m-1)*(n-1);
t=zeros(nelems,4);
a=0;
for e=1:nelems
t(e) = e + a;
t(e, 2) = e + a + 1;
t(e, 3) = e + a + m + 1;
t(e, 4) = e + a + m;
a = floor(e/n);
end
% final t lists 4 node numbers of all rectangles in T by 4 matrix
b=[1:m,m+1:m:m*n,2*m:m:m*n,m*n-m+2:m*n-1]; % bottom, left, right, top
% b = numbers of all 2m+2n **boundary nodes** preparing for U(b)=0
N=size(p,1);T=nelems; % number of nodes, number of rectangles
% p lists x,y coordinates of N nodes, t lists rectangles by 4 node numbers
K=sparse(N,N); % zero matrix in sparse format: zeros(N) would be "dense"
F=zeros(N,1); % load vector F to hold integrals of phi's times load f(x,y)
for e=1:T % integration over one rectangular element at a time
nodes=t(e,:); % row of t = node numbers of the 3 corners of triangle e
Pe=[ones(4,1),p(nodes,:),p(nodes,1).*p(nodes,2)]; % 4 by 4 matrix with rows=[1 x y xy]
Area=abs(det(Pe(1:3,1:3))); % area of triangle e = half of parallelogram area
C=inv(Pe); % columns of C are coeffs in a+bx+cy to give phi=1,0,0 at nodes
% now compute 3 by 3 Ke and 3 by 1 Fe for element e
% grad=C(2:3,:);
% constantKe=Area*grad'*grad; % element matrix from slopes b,c in grad
for i=1:4
for j=1:4
syms x y
Kn = int(int( ...
C(i,2)*C(j,2)+ ...
(C(i,2)*C(j,4)+C(i,4)*C(j,2))*y + ...
C(i,4)*C(j,4)*y^2 + ...
C(i,3)*C(j,3) + ...
(C(i,4)*C(j,3)+C(i,3)*C(j,4))*x + ...
C(i,4)*C(j,4)*x^2 ...
, x, Pe(1, 2), Pe(2, 2)), y, Pe(1, 3), Pe(3, 3));
K(nodes(i),nodes(j)) = K(nodes(i),nodes(j)) + Kn;
end
end
Fe=Area / 3; % integral of phi over triangle is volume of pyramid: f(x,y)=1
% multiply Fe by f at centroid for load f(x,y): one-point quadrature!
% centroid would be mean(p(nodes,:)) = average of 3 node coordinates
% K(nodes,nodes)=K(nodes,nodes)+Ke; % add Ke to 9 entries of global K
F(nodes)=F(nodes)+Fe; % add Fe to 4 components of load vector F
end % all T element matrices and vectors now assembled into K and F
% [Kb,Fb] = dirichlet(K,F,b) % assembled K was singular! K*ones(N,1)=0
% Implement Dirichlet boundary conditions U(b)=0 at nodes in list b
K(b,:)=0; K(:,b)=0; F(b)=0; % put zeros in boundary rows/columns of K and F
K(b,b)=speye(length(b),length(b)); % put I into boundary submatrix of K
Kb=K; Fb=F; % Stiffness matrix Kb (sparse format) and load vector Fb
% Solving for the vector U will produce U(b)=0 at boundary nodes
U=Kb\Fb; % The FEM approximation is U_1 phi_1 + ... + U_N phi_N
U2=reshape(U',m,n);
% Plot the FEM approximation U(x,y) with values U_1 to U_N at the nodes
surf(U2)
I think I can use definite integrals instead of numerical ones, but the result doesn't match that of the original program.
Result of my program
Result of original program
For P1 triangular element case, gradient happens to be constant. Hence integration is simply area*grad'*grad.
However, for bilinear case, inner product of gradient is 2nd order polynomial. Hence you need to use numerical integration.
So, inside of simple multiplication, you need another loop which computes basis value at quadrature points.
Also, your formula for area is wrong. Search for the affine mapping.
I have repository on github which implements poisson equation from 1D to 3D with arbitrary order polynomial. If you are interested, come visit https://github.com/dohyun64/fem_dohyun
I have a set of 3D points (x,y,z) and I would like to fit a straight line using Least absolute deviation method to those data.
I found a function from the internet which works pretty well with 2D data, how could I modify this to adapt 3D data points?
function B = L1LinearRegression(X,Y)
% Determine size of predictor data
[n m] = size(X);
% Initialize with least-squares fit
B = [ones(n,1) X] \ Y;
% Least squares regression
BOld = B;
BOld(1) = BOld(1) + 1e-5;
% Force divergence
% Repeat until convergence
while (max(abs(B - BOld)) > 1e-6) % Move old coefficients
BOld = B; % Calculate new observation weights (based on residuals from old coefficients)
W = sqrt(1 ./ max(abs((BOld(1) + (X * BOld(2:end))) - Y),1e-6)); % Floor to avoid division by zero
% Calculate new coefficients
B = (repmat(W,[1 m+1]) .* [ones(n,1) X]) \ (W .* Y);
end
Thank you very much!
I know that this is not answer to the question but rather to different problem leading to the question.
We can use fit function several times.
% XYZ=[x(:),y(:),z(:)]; % suppose we have data in this format
M=size(XYZ,1); % read size of our data
t=((0:M-1)/(M-1))'; % create arbitrary parameter t
% fit all coordinates as function x_i=a_i*t+b_i
fitX=fit(t,XYZ(:,1),'poly1');
fitY=fit(t,XYZ(:,2),'poly1');
fitZ=fit(t,XYZ(:,3),'poly1');
temp=[0;1]; % define the interval where the line shall be plotted
%Evaluate and plot the line coordinates
Line=[feval(fitX(temp)),feval(fitY(temp)),feval(fitZ(temp))];
plot(Line)
The advantage is that this work for any cloud, even if it is parallel to any axis. another advantage is that you are not limitted only to polynomes of 1st order, you can choose any function for different axis and fit any 3D curve.
I am looking for a command in MATLAB which could help me to plot a graph given the adjacency matrix. Can anyone help me? Further I need some graph tools to compute shortest distances between points on a graph, diameter of a set, distance between sets etc. Thanks
Check this Matlab Function by Haruna Matsushita
function [Xout,Yout,Zout]=gplot3(A,xy,lc)
% gplot‚Ì3ŽŸŒ³•\Ž¦
%
% 2005/04/11 Haruna MATSUSHITA
%GPLOT Plot graph, as in "graph theory".
% GPLOT(A,xy) plots the graph specified by A and xy. A graph, G, is
% a set of nodes numbered from 1 to n, and a set of connections, or
% edges, between them.
%
% In order to plot G, two matrices are needed. The adjacency matrix,
% A, has a(i,j) nonzero if and only if node i is connected to node
% j. The coordinates array, xy, is an n-by-2 matrix with the
% position for node i in the i-th row, xy(i,:) = [x(i) y(i)].
%
% GPLOT(A,xy,LineSpec) uses line type and color specified in the
% string LineSpec. See PLOT for possibilities.
%
% [X,Y] = GPLOT(A,xy) returns the NaN-punctuated vectors
% X and Y without actually generating a plot. These vectors
% can be used to generate the plot at a later time if desired.
%
% See also SPY, TREEPLOT.
% John Gilbert, 1991.
% Modified 1-21-91, LS; 2-28-92, 6-16-92 CBM.
% Copyright 1984-2002 The MathWorks, Inc.
% $Revision: 5.12 $ $Date: 2002/04/09 00:26:12 $
[i,j] = find(A);
[ignore, p] = sort(max(i,j));
i = i(p);
j = j(p);
% Create a long, NaN-separated list of line segments,
% rather than individual segments.
X = [ xy(i,1) xy(j,1) repmat(NaN,size(i))]';
Y = [ xy(i,2) xy(j,2) repmat(NaN,size(i))]';
Z = [ xy(i,3) xy(j,3) repmat(NaN,size(i))]';
X = X(:);
Y = Y(:);
Z = Z(:);
if nargout==0,
if nargin<3,
plot3(X, Y, Z)
else
plot3(X, Y, Z,lc,'MarkerFaceColor','none','MarkerEdgeColor','b','MarkerSize',5);
end
else
Xout = X;
Yout = Y;
Zout = Z;
end
Given the adjacency matrix M, plotting the corresponding directed graph in Matlab will be as easy as:
G = digraph(M);
plot(G)