## imports
import numpy as np
import matplotlib.pyplot as plt
import plotly.graph_objects as go
Linear Algebra - SVD Fit
Fit a line with Singular Value Decomposition
# fixing numpy random state for reproducibility
0) np.random.seed(
# input data
= 25
n = np.array( [5*np.arange(n),5+5*np.arange(n),5*np.ones(n)] ).T + 0.5-np.random.rand(n,3)
points points
array([[ -0.43239394, 5.2846018 , 4.64166236],
[ 4.69710663, 10.34085376, 4.89428804],
[ 10.38433813, 14.77211184, 4.86253772],
[ 14.68806144, 20.02061545, 4.58513691],
[ 20.45065105, 25.20711143, 4.7849474 ],
[ 25.08189079, 30.32704865, 5.39278925],
[ 29.68266089, 35.02685702, 4.61771633],
[ 34.76671087, 40.09027379, 5.12648899],
[ 39.98436165, 44.61094005, 4.76272142],
[ 45.49484704, 49.80584215, 4.58049259],
[ 49.78954424, 55.32299422, 5.01648187],
[ 55.35968398, 60.14100472, 4.56288296],
[ 59.57669469, 65.21716315, 5.16036896],
[ 64.89978713, 69.5368027 , 5.35219867],
[ 70.24308336, 74.62644317, 5.00810777],
[ 74.60103891, 80.3144821 , 4.96733141],
[ 80.17373037, 85.18345744, 5.05312304],
[ 85.06692255, 90.14265312, 4.58502923],
[ 89.76825581, 94.77245301, 5.21008655],
[ 94.92229058, 99.72082057, 4.70440963],
[100.15546954, 104.72912724, 4.7641061 ],
[105.35849351, 109.63405453, 5.05867853],
[110.01358955, 115.05163082, 4.932154 ],
[114.87883075, 120.00182043, 4.63321146],
[119.87226524, 125.09857205, 5.08330824]])
# calculating the mean of the points
= np.mean(points, axis=0)
avg
# subtracting the mean from all points
= points - avg
subtracted
# performing SVD
= np.linalg.svd(subtracted) _, _, V
# find the direction vector (which is the right singular vector corresponding to the largest singular value)
= V[0, :]
direction
# A line is defined by the average and its direction
= avg
p0 = direction
d print(d)
[7.08153575e-01 7.06058100e-01 6.87968476e-04]
= np.array([0, 0, 1])
d0 = np.arccos(np.dot(d0,d)/(np.linalg.norm(d0) * np.linalg.norm(d)))
angle print(angle*180/np.pi)
89.96058230676098
= p0 + (-100)*d
pa = p0 + 100*d
pb print(pa, pb)
[-10.83624093 -5.60662059 4.82481353] [130.79447411 135.60499941 4.96240723]
## plotly
= go.Scatter3d(
trace1 =[pa[0],pb[0]],
x=[pa[1],pb[1]],
y=[pa[2],pb[2]],
z='lines',
mode='3D fitted line',
name=go.scatter3d.Line(color='rgb(255,0,0)', width=10),
line='none')
hoverinfo
= []
labels for i in range(n): labels += [str(i)]
= go.Scatter3d(
trace2 =points[:,0],
x=points[:,1],
y=points[:,2],
z='markers',
mode='Points',
name=go.scatter3d.Marker(
marker='cross',
symbol=1,
opacity='rgb(0,200,127)'),
color=labels,
text='text')
hoverinfo
= go.Layout(
layout ="3D line fitting",
title=go.layout.Scene(
scene="x",
xaxis_title="y",
yaxis_title="z",
zaxis_title=dict(
camera=dict(x=0, y=0, z=1),
up=dict(x=0, y=0, z=0),
center=dict(x=0, y=2.5, z=0))))
eye
=go.Figure(data=[trace1, trace2], layout=layout)
fig="iframe_connected", config={'showLink': False}) fig.show(renderer
fitline
fitline (points)