Chapter 8: SciPy Spatial Data
scipy spatial, the part of SciPy that people usually call “SciPy Spatial” or “SciPy Spatial Data” or “SciPy spatial data structures”.
This submodule is all about computational geometry + efficient spatial queries on point clouds in 2D, 3D, or higher dimensions (up to ~6–10D in practice).
It helps answer questions like:
- Which points are closest to this one? (nearest neighbors)
- What is the shape that encloses all my points tightly? (convex hull)
- How can I divide space into regions closest to each point? (Voronoi diagram)
- How can I triangulate scattered points for interpolation/meshing? (Delaunay triangulation)
- What’s the distance between every pair of points?
- How do I rotate/align point sets in 3D?
Current status (Feb 2026): SciPy 1.17.0 (released Jan 2026) — very mature, stable, leverages Qhull for hull/triangulation/Voronoi + fast Cython/C KD-trees.
Two big worlds inside scipy.spatial
- scipy.spatial (core)
- Point-based geometry: KDTree, Delaunay, Voronoi, ConvexHull, distance computations
- This is what most people mean by “SciPy spatial”
- scipy.spatial.transform (separate submodule since ~1.4)
- 3D rotations, rigid transforms, quaternions, Euler angles, Slerp interpolation
- Very useful in robotics, computer vision, molecular modeling
Today we’ll focus mostly on the core scipy.spatial (the “spatial data” part), but I’ll mention transform at the end.
Main Classes & Functions You’ll Actually Use (2026 ranking)
| What you want | Main class/function | Speed notes / When to choose it | Typical dimension |
|---|---|---|---|
| Fast nearest-neighbor search | KDTree or cKDTree | cKDTree is faster (Cython) — use this almost always | 2D–20D |
| All-pairs distances | distance_matrix, pdist, cdist | From scipy.spatial.distance — pdist for condensed form | 2D–high D |
| Convex hull (smallest convex polygon) | ConvexHull | Qhull-based, gives vertices/simplices/area/volume | 2D–~8D |
| Delaunay triangulation | Delaunay | Triangulates points → great for scattered interpolation | 2D–~6D |
| Voronoi diagram | Voronoi | Regions closest to each point → dual of Delaunay | 2D–~6D |
| Spherical linear interpolation (rotations) | geometric_slerp (in transform) | Smooth rotation between orientations | 3D rotations |
| 3D rotations / rigid transforms | Rotation, RigidTransform (in transform) | Quaternion, matrix, Euler, axis-angle → very powerful | 3D |
Let’s Do Hands-On Examples (copy-paste into Jupyter)
Always start like this:
|
0 1 2 3 4 5 6 7 8 9 |
import numpy as np import matplotlib.pyplot as plt from scipy.spatial import KDTree, cKDTree, ConvexHull, Delaunay, Voronoi from scipy.spatial.distance import pdist, cdist, distance_matrix |
Example 1 — Nearest Neighbors with cKDTree (most common use)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Fake 2D points (e.g. sensor locations, cities, molecules) np.random.seed(42) points = np.random.rand(100, 2) * 10 # 100 points in [0,10]×[0,10] tree = cKDTree(points) # build tree (very fast) # Query nearest neighbor to a new point query_pt = np.array([4.2, 7.8]) dist, idx = tree.query(query_pt) print(f"Nearest point: {points[idx]} at distance {dist:.3f}") # Find all points within radius 1.5 of query_pt indices = tree.query_ball_point(query_pt, r=1.5) print(f"{len(indices)} points within 1.5 units") |
→ cKDTree is your go-to for fast kNN, radius search, k-nearest, etc. (used in DBSCAN, KDE, matching algorithms)
Example 2 — Convex Hull (enclosing shape)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
hull = ConvexHull(points) print(f"Convex hull vertices (indices): {hull.vertices}") print(f"Area (2D) or volume (3D): {hull.area:.2f} / volume: {hull.volume:.2f}") # Plot plt.plot(points[:,0], points[:,1], 'o', label='points') for simplex in hull.simplices: plt.plot(points[simplex, 0], points[simplex, 1], 'r-') plt.fill(points[hull.vertices,0], points[hull.vertices,1], 'r', alpha=0.2) plt.title("Convex Hull"); plt.legend(); plt.grid(alpha=0.3); plt.show() |
→ Gives the smallest convex polygon containing all points.
Example 3 — Delaunay Triangulation (connect points without crossing edges)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
tri = Delaunay(points) print(f"Number of triangles: {tri.simplices.shape[0]}") # Plot triangulation plt.triplot(points[:,0], points[:,1], tri.simplices, 'k-', lw=0.8) plt.plot(points[:,0], points[:,1], 'ro', ms=4) plt.title("Delaunay Triangulation"); plt.grid(alpha=0.3); plt.show() |
→ Very useful for interpolating scattered data (scipy.interpolate.LinearNDInterpolator(tri, values))
Example 4 — Voronoi Diagram (regions closest to each point)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
vor = Voronoi(points) # Plot (simple version) from scipy.spatial import voronoi_plot_2d fig = voronoi_plot_2d(vor, ax=plt.gca()) plt.plot(points[:,0], points[:,1], 'ro') plt.title("Voronoi Diagram"); plt.show() |
→ Each cell contains all locations closer to its seed point than to any other.
Example 5 — Pairwise Distances (very handy)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
# Condensed distance vector (for clustering, MDS, etc.) dists_condensed = pdist(points, metric='euclidean') # shape (n*(n-1)/2,) # Full matrix (M×N if two sets) dmat = cdist(points[:10], points[10:]) # 10×90 matrix print(f"Max distance: {dists_condensed.max():.2f}") |
Quick Decision Table — Which One First?
| Your problem | Start with | Why? |
|---|---|---|
| Find closest point(s) / neighbors | cKDTree | Fastest, memory-efficient |
| Enclose points (bounding shape) | ConvexHull | Area/volume, vertices, check if point inside |
| Mesh / interpolate scattered data | Delaunay → interpolator | Natural triangulation |
| Regions of influence / nearest-owner map | Voronoi | Dual to Delaunay, nice visualization |
| Compare two point clouds / alignment | cdist + Rotation (transform) | Distances + rigid transform |
| 3D pose / orientation math | scipy.spatial.transform.Rotation | Quaternion, Euler, matrix ops |
Teacher’s Final Tips (Feb 2026 edition)
- Prefer cKDTree over KDTree — it’s faster and has more features (periodic boundaries via boxsize, parallel query with n_jobs)
- Qhull-based things (ConvexHull, Delaunay, Voronoi) work best in low dimensions (<8–10D); high-D → curse of dimensionality hurts
- For rotations → go to scipy.spatial.transform — Rotation.from_matrix(), from_euler(), from_quat(), .apply() to vectors
- Docs are gold: • Tutorial: https://docs.scipy.org/doc/scipy/tutorial/spatial.html • Reference: https://docs.scipy.org/doc/scipy/reference/spatial.html • Transform: https://docs.scipy.org/doc/scipy/reference/spatial.transform.html
Which part excites you most (or matches your current need)?
- Nearest neighbors / clustering?
- Convex hull / bounding volume?
- Triangulation for interpolation?
- Voronoi for partitioning space?
- 3D rotations / alignments?
- Something with real data (GPS, molecules, images)?
Tell me and we’ll build a more detailed, domain-specific example together (maybe even with fake realistic data). 🚀
