# Perform the matrix product on the coordinates
vecs = camera.dot(points.T).T
# Divide resulting coordinates by their z-value
pixel_coords = vecs/vecs[:, 2, np.newaxis]
The dot function
3
implements the matrix prod-
uct, in contrast to the element-wise product *. It
can be applied to one- or two-dimensional arrays.
This code executes in 9 miliseconds–a 70x
speedup over a Python for-loop version.
Aside from the optimized NumPy dot prod-
uct, we make use of NumPy’s array opera-
tions with element-by-element division and the
broadcasting machinery. The code new
vecs /
new
vecs[:, 2, np.newaxis] divides each col-
umn of new
vecs by its thir d column (in other
words, each row is divided by its third ele-
ment). The np.newaxis index is used to change
new
vecs[:, 2] into a column-vector s o that
broadcasting may take place.
The above examples show h ow vectorization pro-
vides a powerful and efficient m eans of operating
on large arrays, without compromising clear and
concise code or relinquishing control over aspects
such as memory allocation.
It should be noted that vectorization and broad-
casting is n o panacea; for example, when repeated
operations take place on very large chunks of
memory, it may be better to use an outer for-loop
combined with a vectorised inner loop to make
optimal use of the system cache.
Sharing data
As shown above, performance is often improved
by preventing repeated copying of data in mem-
ory. In this section, we show how NumPy may
make use of foreign memory–in other words, mem-
ory that is not allocated or controlled by NumPy–
without copying data.
Efficient I/O with memory mapping
An array stored on disk may be addressed
directly without copying it to memory in its
entirety. This technique, kn own as memory
mapping, is useful for addressing only a small
portion of a very large array. NumPy supports
3
The dot function leverages accelerated BLAS imple-
mentations, if available.
memory mapped arrays w ith the same inter-
face as any other NumPy array. First, let us
construct such an array and fill it with some data:
In [50]: a = np.memmap(’/tmp/myarray.memmap’,
...: mode=’write’, shape=(300, 300),
...: dtype=np.int)
# Pretend "a" is a one-dimensional, 300*300
# array and assign values into it
In [51]: a.flat = np.arange(300* 300)
In [52]: a
Out[52]:
memmap([[ 0, 1, ..., 298, 299],
[ 300, 301, ..., 598, 599],
[ 600, 601, ..., 898, 899],
...,
[89100, 89101, ..., 89398, 89399],
[89400, 89401, ..., 89698, 89699],
[89700, 89701, ..., 89998, 89999]])
When the “flush” method is called, its data is
written to disk:
In [53]: a.flush()
The array can now be loaded and parts of it ma-
nipulated; calling “flush” writes the altered data
back to disk:
# Load the memory mapped array
In [54]: b = np.memmap(’/tmp/myarray.memmap’,
...: mode=’r+’, shape=(300, 300),
...: dtype=np.int)
# Perform some operation on the elements of b
In [55]: b[100, :] *= 2
# Store the modifications to disk
In [56]: b.flush()
The array interface for foreign blocks of memory
Often, NumPy arrays have to be created from
memory constructed and populated by foreign
co de, e.g., a result produced by an external C++
or Fortran library.
To facilitate such exchanges without copying the
already allocated memory, NumPy defines an ar-
ray interface that specifies how a given object
exposes a block of memory. NumPy knows how to
view any object with a valid
array interface
dictionary attribute as an array. Its most impor-
tant values are data (address of the data in mem-
ory), shape and typestr (the kind of elements
stored).
6 IEEE Computing in science and Engineering