Here are some timing tests, on larger arrays, which makes the difference clearer.
import numpy as np
from timeit import timeit
# original
def f1(x, y, z):
points = np.stack([x, y, z], axis=1).reshape(-1, 1, 3)
return np.concatenate([points[:-1], points[1:]], axis = 1)
# preallocating and then assigning
def f2(x, y, z):
segments = np.empty((len(x)-1, 2, 3))
segments[:,0,0] = x[:-1]
segments[:,1,0] = x[1:]
segments[:,0,1] = y[:-1]
segments[:,1,1] = y[1:]
segments[:,0,2] = z[:-1]
segments[:,1,2] = z[1:]
return segments
# stacking, but in one go
def f3(x, y, z):
segments = np.stack([x[:-1], y[:-1], z[:-1], x[1:], y[1:],z[1:]], axis=1)
return segments.reshape(-1, 2, 3)
# list comparison
def f4(x, y, z):
z_ = [i for i in zip(x,y,z)]
return [[[z_[i]],[z_[i+1]]] for i in range(len(z_)-1)]
#np.lib.stride_tricks approach
def f5(x, y, z):
a = np.transpose([x, y, z])
window = (2, 3)
view_shape = (len(a) - window[0] + 1,) + window # (4,2,3) if len(a) == 5
return np.lib.stride_tricks.as_strided(a, shape = view_shape, strides = (a.itemsize,) + a.strides)
ntime = 5000 #number of test runs
nxd = 500 #array length
Xd = np.random.randn(nxd)
Yd = np.random.randn(nxd)
Zd = np.random.randn(nxd)
print(timeit(lambda: f1(Xd, Yd, Zd), number=ntime))
#0.11369249999999999
print(timeit(lambda: f2(Xd, Yd, Zd), number=ntime))
#0.0480651
print(timeit(lambda: f3(Xd, Yd, Zd), number=ntime))
#0.10202380000000003
print(timeit(lambda: f4(Xd, Yd, Zd), number=ntime))
#1.8407391
print(timeit(lambda: f5(Xd, Yd, Zd), number=ntime))
#0.09132560000000023
ntime = 50 #number of test runs
nxd = 500000 #array length
Xd = np.random.randn(nxd)
Yd = np.random.randn(nxd)
Zd = np.random.randn(nxd)
print(timeit(lambda: f1(Xd, Yd, Zd), number=ntime))
#1.7519548999999999
print(timeit(lambda: f2(Xd, Yd, Zd), number=ntime))
#1.504727
print(timeit(lambda: f3(Xd, Yd, Zd), number=ntime))
#1.5010566
print(timeit(lambda: f4(Xd, Yd, Zd), number=ntime))
#22.6208157
print(timeit(lambda: f5(Xd, Yd, Zd), number=ntime))
#0.46465339999999955
As you can see, @Miguel's way is the way to go: preallocating the array and then assigning is the most efficient way to do this. Even if you stack them in a smarter manner like in f3(), it's still slower than f2(). But nothing beats f5() when the array length increases substantially.