Finding the closest point from Point Cloud Data to point (0,0,0)
Question:
I made a program to plot a figure from point cloud data (x,y,z coordinates shown as list x_list, y_list, z_list). Now, I have to find the closest point to (0,0,0). Anybody has an idea to do that? Here is the program:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import math
from mpl_toolkits.mplot3d import axes3d
cloud = np.loadtxt('c1.txt')
rows = len(cloud)
columns = int(len(cloud[0])/5)
x_list = []
y_list = []
z_list = []
for i in range(rows):
for j in range(columns):
x_list.append(cloud[i][j])
y_list.append(cloud[i][j+columns])
z_list.append(cloud[i][j+2*columns])
#x_list = x_list[~pd.isnull(x_list)]
X = x_list
Y = y_list
Z = z_list
#Eliminating 'nan' values
newlist_x = [X for X in x_list if math.isnan(X) == False]
newlist_y = [Y for Y in y_list if math.isnan(Y) == False]
newlist_z = [Z for Z in z_list if math.isnan(Z) == False]
display(newlist_x, newlist_y, newlist_z)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.scatter(newlist_x, newlist_y, newlist_z, c=newlist_z, cmap='plasma', linewidth=0.01)
#3D plotting of points
plt.rcParams["figure.figsize"] = (12,15) #making plot more viewable
plt.show()
Answers:
This should do the trick.
def closest(X, Y, Z, P):
"""
X: x-axis series
Y: y-axis series
Z: z-axis series
P: centre point to measure distance from
Returns: tuple(closest point, distance between P and closest point)
"""
# Here we combine the X, Y, Z series to get a data struct. with all our points (similar to using the builtin's zip())
points = np.column_stack((X, Y, Z))
# Now we compute the distances between each of the points and the desired point
distances = [np.linalg.norm(p) for p in (points - P)]
# Finally we get the index of the smalles value in the array so that we can determine which point is closest and return it
idx_of_min = np.argmin(distances)
return (points[idx_of_min], distances[idx_of_min])
You can call closest
as such:
X = [-1, -6, 8, 1, -2, -5, -1, 5, -3, -10, 6, 3, -4, 9, -5, -2, 4, -1, 3, 0]
Y = [-2, -1, -9, -6, -8, 7, 9, -2, -9, -9, 0, -2, -2, -3, 6, -5, -1, 3, 8, -5]
Z = [-1, 2, 3, 2, 6, -2, 9, 3, -10, 4, -6, 9, 8, 3, 3, -6, 4, 1, -10, 1]
closest_to_point(X, Y, Z, (0, 0, -100))
An example output looks like this:
(array([ 3, 8, -10]), 90.40464589831653)
This should do the trick quite a lot faster
def closest_pythagoras(X, Y, Z, P):
"""
X: x-axis series
Y: y-axis series
Z: z-axis series
P: centre point to measure distance from
Returns: tuple(closest point, distance between P and closest point)
"""
# Compute the distances between each of the points and the desired point using Pythagoras' theorem
distances = np.sqrt((X - P[0]) ** 2 + (Y - P[1]) ** 2 + (Z - P[2]) ** 2)
# Get the index of the smallest value in the array to determine which point is closest and return it
idx_of_min = np.argmin(distances)
return (np.array([X[idx_of_min], Y[idx_of_min], Z[idx_of_min]]), distances[idx_of_min])
If performance is critical in your application, consider the above changes to Mieszko’s answer.
This code will run several orders of magnitude faster for large X Y Z arrays.
The original answer contains a vanilla python loop that quickly ruins your performance when the size of points
increases. Removing the loop and letting numpy do the heavy lifting lets the cost increase linearly with the input size allowing it to scale much better. On top of this, using Pythagoras’ theorem directly roughly doubles the performance over np.linalg.norm
, not because it’s any faster, but because syntactically it can omit the np.column_stack
step and the connected cost.
- Red: Mieszko’s answer /
[np.linalg.norm(p) for p in (points - P)]
- Orange: Optimized version of Mieszko’s answer /
np.linalg.norm(points - P, axis=1)
- Green: Good old Pythagoras /
np.sqrt((X - P[0]) ** 2 + (Y - P[1]) ** 2 + (Z - P[2]) ** 2)
All measurements were taken with timeit, taking the lowest of 5 runs at any given input size.
Input sizes started at 10 and were increased by an order of magnitude until function execution time exceeded 1 second. (In some places intermediate values were added to keep the graph tidy)
Measurements were taken using one thread of a Ryzen 9 5900X.
I made a program to plot a figure from point cloud data (x,y,z coordinates shown as list x_list, y_list, z_list). Now, I have to find the closest point to (0,0,0). Anybody has an idea to do that? Here is the program:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import math
from mpl_toolkits.mplot3d import axes3d
cloud = np.loadtxt('c1.txt')
rows = len(cloud)
columns = int(len(cloud[0])/5)
x_list = []
y_list = []
z_list = []
for i in range(rows):
for j in range(columns):
x_list.append(cloud[i][j])
y_list.append(cloud[i][j+columns])
z_list.append(cloud[i][j+2*columns])
#x_list = x_list[~pd.isnull(x_list)]
X = x_list
Y = y_list
Z = z_list
#Eliminating 'nan' values
newlist_x = [X for X in x_list if math.isnan(X) == False]
newlist_y = [Y for Y in y_list if math.isnan(Y) == False]
newlist_z = [Z for Z in z_list if math.isnan(Z) == False]
display(newlist_x, newlist_y, newlist_z)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.scatter(newlist_x, newlist_y, newlist_z, c=newlist_z, cmap='plasma', linewidth=0.01)
#3D plotting of points
plt.rcParams["figure.figsize"] = (12,15) #making plot more viewable
plt.show()
This should do the trick.
def closest(X, Y, Z, P):
"""
X: x-axis series
Y: y-axis series
Z: z-axis series
P: centre point to measure distance from
Returns: tuple(closest point, distance between P and closest point)
"""
# Here we combine the X, Y, Z series to get a data struct. with all our points (similar to using the builtin's zip())
points = np.column_stack((X, Y, Z))
# Now we compute the distances between each of the points and the desired point
distances = [np.linalg.norm(p) for p in (points - P)]
# Finally we get the index of the smalles value in the array so that we can determine which point is closest and return it
idx_of_min = np.argmin(distances)
return (points[idx_of_min], distances[idx_of_min])
You can call closest
as such:
X = [-1, -6, 8, 1, -2, -5, -1, 5, -3, -10, 6, 3, -4, 9, -5, -2, 4, -1, 3, 0]
Y = [-2, -1, -9, -6, -8, 7, 9, -2, -9, -9, 0, -2, -2, -3, 6, -5, -1, 3, 8, -5]
Z = [-1, 2, 3, 2, 6, -2, 9, 3, -10, 4, -6, 9, 8, 3, 3, -6, 4, 1, -10, 1]
closest_to_point(X, Y, Z, (0, 0, -100))
An example output looks like this:
(array([ 3, 8, -10]), 90.40464589831653)
This should do the trick quite a lot faster
def closest_pythagoras(X, Y, Z, P):
"""
X: x-axis series
Y: y-axis series
Z: z-axis series
P: centre point to measure distance from
Returns: tuple(closest point, distance between P and closest point)
"""
# Compute the distances between each of the points and the desired point using Pythagoras' theorem
distances = np.sqrt((X - P[0]) ** 2 + (Y - P[1]) ** 2 + (Z - P[2]) ** 2)
# Get the index of the smallest value in the array to determine which point is closest and return it
idx_of_min = np.argmin(distances)
return (np.array([X[idx_of_min], Y[idx_of_min], Z[idx_of_min]]), distances[idx_of_min])
If performance is critical in your application, consider the above changes to Mieszko’s answer.
This code will run several orders of magnitude faster for large X Y Z arrays.
The original answer contains a vanilla python loop that quickly ruins your performance when the size of points
increases. Removing the loop and letting numpy do the heavy lifting lets the cost increase linearly with the input size allowing it to scale much better. On top of this, using Pythagoras’ theorem directly roughly doubles the performance over np.linalg.norm
, not because it’s any faster, but because syntactically it can omit the np.column_stack
step and the connected cost.
- Red: Mieszko’s answer /
[np.linalg.norm(p) for p in (points - P)]
- Orange: Optimized version of Mieszko’s answer /
np.linalg.norm(points - P, axis=1)
- Green: Good old Pythagoras /
np.sqrt((X - P[0]) ** 2 + (Y - P[1]) ** 2 + (Z - P[2]) ** 2)
All measurements were taken with timeit, taking the lowest of 5 runs at any given input size.
Input sizes started at 10 and were increased by an order of magnitude until function execution time exceeded 1 second. (In some places intermediate values were added to keep the graph tidy)
Measurements were taken using one thread of a Ryzen 9 5900X.