Quick answer: NumPy permutation returns a randomized ordering without modifying an array input. For new code, create a Generator with default_rng(), use its permutation() method, and choose shuffle() only when in-place mutation is intentional.

numpy.random.permutation() returns data in a randomized order. When the input is an integer, NumPy builds a shuffled range from zero up to that integer. When the input is an array, NumPy returns a permuted copy instead of rearranging the source array in place.
For new code, prefer a generator from np.random.default_rng() and call rng.permutation(). The official NumPy documentation covers Generator.permutation(), default_rng(), and the legacy numpy.random.permutation() function.
The most important rule is that array permutation happens along an axis. For a normal two-dimensional array, the default axis is the first axis, so complete rows move together. NumPy does not mix every value across the whole matrix unless you flatten the array first and then reshape it intentionally.
This behavior is useful for shuffling training examples, creating random index orders, reordering table rows, selecting batches, and testing algorithms against randomized input. It also prevents accidental data corruption when rows contain connected fields that must stay together.
Reproducibility is a separate decision. A fixed seed makes tutorial output, tests, and debugging repeatable. Without a fixed seed, each generator starts from fresh entropy and the order can change every run. Both choices are valid, but the code should make the choice obvious.
Shuffle A One-Dimensional Array
Use rng.permutation() when you want a shuffled copy of an array. The original array remains in its existing order.
try:
import numpy as np
except ModuleNotFoundError:
print("Install numpy to run this example.")
else:
rng = np.random.default_rng(42)
values = np.array([10, 20, 30, 40, 50])
shuffled = rng.permutation(values)
print(shuffled)
print(values)
This is different from an in-place shuffle. Keeping the source unchanged is helpful when the original order has meaning or when the same data is reused later in the program.
If you need to mutate an array directly, NumPy also has shuffle APIs. For most article examples, tests, and data preparation steps, returning a separate shuffled result is easier to reason about.
Permute Rows In A Matrix
Passing a two-dimensional array permutes rows by default. Each row moves as a complete unit, and the values inside a row keep their left-to-right order.
try:
import numpy as np
except ModuleNotFoundError:
print("Install numpy to run this example.")
else:
rng = np.random.default_rng(7)
matrix = np.array([
[1, 10],
[2, 20],
[3, 30],
[4, 40],
])
print(rng.permutation(matrix))
This row behavior is why permutation is commonly used before splitting data into batches. Features, labels, timestamps, or identifiers in the same row stay aligned because NumPy moves the row together.
If the goal is to randomize every element independently, flatten the data first. Do that only when row structure does not matter.
Create A Random Index Order
Another common pattern is to permute integer positions and then apply those positions to multiple arrays. This keeps related arrays aligned without merging them into one table first.
try:
import numpy as np
except ModuleNotFoundError:
print("Install numpy to run this example.")
else:
rng = np.random.default_rng(15)
features = np.array([[5, 1], [6, 1], [7, 0], [8, 0]])
labels = np.array(["cat", "cat", "dog", "dog"])
order = rng.permutation(len(labels))
print(order)
print(features[order])
print(labels[order])
This is usually safer than shuffling each array separately. If each array receives a different random order, labels can separate from the rows they describe.
An index order also makes a shuffle auditable. You can print, store, or reuse the order when you need to apply the same rearrangement to validation data, weights, or metadata.
Permute Columns With Generator.permutation
The generator method supports an axis argument. Set axis=1 when the column order should change while row positions stay in place.
try:
import numpy as np
except ModuleNotFoundError:
print("Install numpy to run this example.")
else:
rng = np.random.default_rng(21)
grid = np.arange(12).reshape(3, 4)
columns = rng.permutation(grid, axis=1)
print(grid)
print(columns)
This is useful for matrix experiments, grid examples, and tests that need column order to vary. In older legacy style, people often transpose or slice manually, but the axis argument states the intent clearly.
Always choose the axis based on what that axis represents in your data. Rows, columns, channels, and batches have different meanings, even when their numeric shapes look similar.
Make A Shuffle Reproducible
A seed lets two generators produce the same permutation sequence. This is helpful for documentation, lessons, regression tests, and bug reports.
try:
import numpy as np
except ModuleNotFoundError:
print("Install numpy to run this example.")
else:
first_rng = np.random.default_rng(2026)
second_rng = np.random.default_rng(2026)
first_order = first_rng.permutation(6)
second_order = second_rng.permutation(6)
print(first_order)
print(np.array_equal(first_order, second_order))
For production simulations, record the seed only when the result must be repeatable later. For everyday randomized behavior, a generator without an explicit seed is often the better choice.
Do not mix several independent random systems unless you have a reason. Passing a generator through your code makes the random source easier to test and easier to replace.
Compare Generator And Legacy Calls
The legacy np.random.permutation() function still works, but it relies on NumPy’s older module-level random state. Generator-based code keeps state in an object, which is easier to pass around and control.
try:
import numpy as np
except ModuleNotFoundError:
print("Install numpy to run this example.")
else:
np.random.seed(3)
legacy_order = np.random.permutation(5)
rng = np.random.default_rng(3)
generator_order = rng.permutation(5)
print(legacy_order)
print(generator_order)
The two outputs do not need to match. They come from different random implementations, so the same seed value is not a promise of identical sequences across APIs.
In short, use default_rng() plus Generator.permutation() for new projects, use integer input when you need shuffled indices, and remember that a two-dimensional array is shuffled by rows unless you choose another axis. Keep seeds explicit when examples must be reproducible, and keep related arrays aligned by applying one shared permutation order. permutation() returns a rearranged copy, whereas NumPy shuffle() In-Place Guide explains when shuffle() changes an array in place.
Permute A Range Or Array
With an integer, permutation(n) shuffles the range from 0 through n – 1. With an array-like input, it returns a reordered copy. That copy behavior is useful when the original labels or rows must remain unchanged.
import numpy as np
print(np.random.permutation(6))
values = np.array([10, 20, 30, 40])
result = np.random.permutation(values)
print(values)
print(result)
Prefer An Explicit Generator
The legacy np.random.permutation() interface uses global random state. A Generator makes the random stream an explicit object that can be seeded, passed to a function, and tested without changing unrelated code. Reuse one generator when a sequence of results must be reproducible.
rng = np.random.default_rng(42)
values = np.array(["a", "b", "c", "d"])
print(rng.permutation(values))
print(rng.permutation(5))
Permutation Versus Shuffle
permutation() returns a result; shuffle() modifies the supplied mutable array and returns None. For a multidimensional array, both operate along the first axis by default, so rows move while each row’s internal values stay together. Select the operation based on ownership and mutation rules.
rng = np.random.default_rng(7)
rows = np.arange(9).reshape(3, 3)
shuffled = rng.permutation(rows)
rng.shuffle(rows)
print(shuffled)
print(rows)
Compare the NumPy permutation documentation with Generator and shuffle behavior before choosing a random-state design.
For related NumPy array operations, compare arange(), reshape(), and flattening arrays before choosing a transformation.
Frequently Asked Questions
What does NumPy random.permutation() do?
It randomly permutes a sequence or returns a permuted range; when given an array, it returns a shuffled copy.
Does np.random.permutation() modify the original array?
No. For an array input it makes a copy, unlike shuffle(), which changes the array in place.
What should new NumPy code use instead?
Use a numpy.random.Generator created by default_rng() and call its permutation() method for explicit, reproducible random state.
How do I make a permutation reproducible?
Create a Generator with a fixed seed and reuse that generator for the run whose results must be repeatable.