📄 Executive Summary
From Python to NumPy is written for intermediate Python programmers who possess a basic familiarity with NumPy and seek to convert slow, iterative loops into fast, vectorized scientific code. Rather than serving as an introductory language primer or a basic reference manual, the book concentrates on the transition in computational thinking required to move from Python code to array-oriented execution. The text explores how vectorization operates at both the code and algorithmic levels, while deliberately setting aside beginner Python syntax and exhaustive coverage of domain-specific scientific theory.
The book begins by establishing the physical structure and internal mechanics of NumPy arrays. It explains the relationship between contiguous memory blocks, data types, shapes, and strides, illustrating how memory layout dictates access speeds and item indexing. A central focus is the mechanical distinction between array views and array copies, detailing how slicing, basic indexing, fancy indexing, and intermediate arithmetic create temporary memory allocations that impact performance. Readers learn how memory casting and byte-level manipulation can produce significant execution speedups, alongside an explicit acknowledgment of the trade-off between vectorized execution speed and code readability.
Building upon memory fundamentals, the text examines direct code vectorization across three distinct categories: uniform, temporal, and spatial. In uniform vectorization, all elements undergo identical transformations at each step, demonstrated through implementations of Conway's Game of Life and the Gray-Scott reaction-diffusion system. Temporal vectorization handles elements requiring variable numbers of iterations, illustrated by tracking non-divergent points in Mandelbrot set generation and calculating Minkowski–Bouligand fractal dimensions. Spatial vectorization addresses dynamic local interactions, demonstrated through Craig Reynolds' Boids flocking simulations, where distance matrices and boolean masks replace nested object-oriented loops.
The text then transitions to problem vectorization, demonstrating that significant performance gains often require reformulating mathematical models and choosing different algorithms altogether rather than merely rewriting loops into array operations. The author illustrates this distinction through path-finding in mazes using breadth-first search and Bellman–Ford diffusion gradients, stable Eulerian fluid dynamics for smoke simulations, and blue noise point distributions comparing the rejection-heavy DART method against Robert Bridson's fast Poisson disk sampling.
In its advanced sections, the book teaches custom vectorization techniques by extending the core library, showing how to wrap arrays to construct dynamic typed lists (ragged arrays) and how to subclass the fundamental array structure to create memory-aware buffers that track modified memory for GPU synchronization. The text concludes by surveying the broader scientific computing environment, evaluating pure Python generator performance, compilation tools such as Numba, Cython, and NumExpr, GPU interfaces like PyCUDA and PyOpenCL, and domain packages including scikit-learn, scikit-image, SymPy, and Astropy. After studying this work, readers will be equipped to analyze memory overhead, transform iterative procedures into array-oriented algorithms, and construct high-performance numerical routines.