NumPy nditer() for Array Iteration

numpy.nditer() is a flexible iterator for NumPy arrays. You can use it for simple element traversal, but its real value is the extra control it gives over traversal order, index tracking, writable operands, and chunked loops.

Most NumPy code should use vectorized operations first. Use nditer() when you need explicit element-by-element logic, when you need to inspect coordinates while looping, or when you need a controlled loop over one or more arrays.

The official NumPy documentation includes the nditer guide and the numpy.nditer reference. Related PythonPool guides cover NumPy reshape, NumPy amin, NumPy infinity, and NumPy nth roots.

The examples below show practical patterns. They intentionally keep arrays small so the output is easy to inspect. For large numeric work, prefer built-in NumPy operations unless a custom loop is genuinely needed.

Think of nditer() as a lower-level tool. It is helpful when you need iterator features that a normal for value in array.flat loop does not provide. Those features include Fortran-style order, coordinate tracking, in-place writes, and multiple operands under one iterator.

It is not a replacement for broadcasting, ufuncs, reductions, or boolean indexing. If NumPy already has a vectorized function for the calculation, that function will usually be shorter and faster than a Python loop.

Basic nditer Loop

The simplest use of nditer() loops over each element in an array.

import numpy as np

array = np.array([[1, 2, 3], [4, 5, 6]])

for value in np.nditer(array):
    print(int(value))

Each value is returned as a NumPy scalar-like object. Convert it with int(), float(), or another type only when plain Python output is needed.

This form is useful for inspection and teaching, but many real calculations can be written more clearly as vectorized NumPy expressions.

For example, printing values one at a time is reasonable in a tutorial. Adding one to every value should usually be written as array + 1, not as a manual iterator loop.

Control Iteration Order

The order argument controls whether traversal follows row-major or column-major order.

import numpy as np

array = np.array([[1, 2, 3], [4, 5, 6]])

print("C order:")
for value in np.nditer(array, order="C"):
    print(int(value))

print("F order:")
for value in np.nditer(array, order="F"):
    print(int(value))

"C" order walks rows first. "F" order walks columns first.

Order matters when the loop is used for display, serialization, or a custom algorithm that expects a specific traversal shape.

When performance matters, order can also affect memory access patterns. A traversal order that matches the array layout can reduce unnecessary work. Keep this in mind when iterating over large arrays or arrays created by reshaping and transposing data.

Track Coordinates With multi_index

The multi_index flag exposes the current array coordinates while the iterator advances.

import numpy as np

array = np.array([[10, 20], [30, 40]])

iterator = np.nditer(array, flags=["multi_index"])

for value in iterator:
    print(iterator.multi_index, int(value))

This is helpful when the coordinate is as important as the value.

Use it for debugging, sparse updates, reporting, or custom checks that need row and column positions.

The coordinate tuple follows the shape of the array. A two-dimensional array reports pairs such as (0, 1), while higher-dimensional arrays report longer tuples.

Modify Values With readwrite

By default, operands are read-only. Use op_flags=["readwrite"] when the loop should update the array.

import numpy as np

array = np.array([1, 2, 3])

for value in np.nditer(array, op_flags=["readwrite"]):
    value[...] = value * 10

print(array)

The value[...] assignment writes back into the original array element.

Only use this form when in-place mutation is intended. If you need a new array, a vectorized expression such as array * 10 is usually clearer.

Writable operands deserve extra care because the original array changes immediately. Make a copy first when the old data is still needed later in the program.

Use external_loop For Chunks

The external_loop flag returns one-dimensional chunks instead of one scalar at a time.

import numpy as np

array = np.arange(12).reshape(3, 4)

for chunk in np.nditer(array, flags=["external_loop"], order="C"):
    print(chunk)

This can reduce Python-level loop overhead because each iteration handles a slice-like chunk.

Chunked iteration is useful when you still need a Python loop but want to operate on blocks of contiguous values.

Each chunk can be handled with NumPy operations, so this approach can be a middle ground between scalar loops and one large vectorized expression.

Iterate Over Multiple Arrays

nditer() can loop over multiple operands together when their shapes are compatible.

import numpy as np

left = np.array([1, 2, 3])
right = np.array([10, 20, 30])

for a, b in np.nditer([left, right]):
    print(int(a + b))

This pattern is useful for coordinated traversal across arrays.

For simple arithmetic, use vectorized NumPy operations such as left + right. Reserve a multi-operand iterator for custom logic that cannot be expressed cleanly as a built-in operation.

When using more than one operand, confirm that shapes are compatible before relying on the loop. Broadcasting rules still matter, and shape mismatches should be caught during testing.

In short, nditer() is best when you need controlled traversal. Use direct vectorized NumPy operations for ordinary math, and use nditer() when you need flags, coordinates, writable operands, chunks, or coordinated loops over multiple arrays.

Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Johan
Johan
4 years ago

That’s a really useful and clear explanation! Thanks you so much ?