Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert arrays of x,y,z coordinates to 3D path in numpy

Given three 1D arrays of X, Y and Z coordinates, how to convert into a 3D mesh path using numpy?

I managed to do this for 2D using numpy (ie no for loops):

import numpy

def path_2d_numpy(x, y):
    m1, m2 = numpy.meshgrid(x, y)
    m1[1::2] = m1[1::2,::-1]
    r = numpy.append(m1, m2)
    r.shape = 2,-1
    return r.T

from matplotlib import lines
from matplotlib import pyplot

def plot_path_2d(path):
    x, y = path.T
    pyplot.plot(x, y, '-ro', lw=3)
    pyplot.show()

x = numpy.linspace(4, 1, 4)
y = numpy.linspace(1, 5, 5)
path = path_2d_numpy(x, y)
plot_path_2d(path)

which outputs:

2D mesh path

...but was unable to do it for 3D. Showing pure python solution (ie without numpy):

import numpy

def path_3d(x, y, z):
    nb_points =len(x)*len(y)*len(z)
    path = numpy.empty((nb_points, 3))

    xord, yord, i = True, True, 0
    for zi in z:
        for yi in y[::1 if yord else -1]:
            for xi in x[::1 if xord else -1]:
                path[i] = xi, yi, zi
                i += 1
            xord = not xord
        yord = not yord
    return path

from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

def plot_path_3d(path):
    fig = pyplot.figure()
    ax = fig.gca(projection='3d')
    xx, yy, zz = path.T
    ax.plot(xx, yy, zz, '-bo', lw=3)
    pyplot.show()

x = numpy.linspace(4, 1, 4)
y = numpy.linspace(1, 5, 5)
z = numpy.linspace(-3, 0, 3)

path = path_3d(x, y, z)
plot_path_3d(path)

which outputs:

3D mesh path

Essencialy, what I am looking for is for a numpy implementation of path_3d as I did for path_2d_numpy.

I need this because the actual arrays I am dealing with are quite big. Doing it without numpy is just too slow.

like image 982
Tiago Coutinho Avatar asked Dec 10 '16 13:12

Tiago Coutinho


1 Answers

How's this look?

import numpy as np

def path_3d_numpy(x, y, z):
    coords = np.stack(np.meshgrid(x, y, z), axis=-1)  # shape = (nx, ny, nz, 3)
    coords[1::2,:,:] = coords[1::2,::-1,:]
    coords[:,1::2,:] = coords[:,1::2,::-1]
    return coords.reshape(-1, 3)  # flatten out the other axes

Doesn't iterate the points in quite the same order as yours, but you could fix that simply by swapping some indices around


Similarly, your 2d case could be written as

def path_2d_numpy(x, y):
    coords = np.stack(np.meshgrid(x, y), axis=-1)
    coords[1::2] = coords[1::2,::-1]
    return coords.reshape(-1, 2)

For some real overkill, you can extend this to N dimensions:

def path_nd(*args):
    coords = np.stack(np.meshgrid(*args), axis=-1)
    N = len(args)

    axes = np.arange(N)
    for i in range(N-1):
        # the last axis isn't part of our mesh, so don't roll it
        rolled_axes = tuple(np.roll(axes, -i)) + (N,)
        rolled_view = np.transpose(coords, rolled_axes)
        rolled_view[1::2,:] = rolled_view[1::2,::-1]

    return coords.reshape(-1, N)
like image 195
Eric Avatar answered Oct 19 '22 23:10

Eric