Written and maintained by CASRAI Editorial Board
Last updated
numpy random choice draws one or more values at random from an array or from the integers 0 to n-1. In current NumPy you call it as Generator.choice on a generator created with np.random.default_rng(); the older np.random.choice still works but belongs to the frozen legacy API. This guide covers both, how weights change the sampler, how to make a draw reproducible, and the errors you are most likely to meet.
What it is used for in research
Researchers use random selection to draw a sample from a list of participants, records or files, to assign units to groups, to bootstrap or resample data, and to build simulated datasets for checking an analysis. For the statistical idea behind drawing a sample without replacement, see the guide to simple random sampling. For splitting a dataset into training and test sets, see train-test split in Python.
The NumPy manual states the signature as Generator.choice(a, size=None, replace=True, p=None, axis=0, shuffle=True). The arguments are:
- a: an array-like, or an integer. An integer means
np.arange(a). - size: the output shape. With the default
None, a single value is returned. - replace:
Trueby default, so the same element can be drawn more than once. - p: probabilities for each element of
a. If omitted, every element is equally likely. - axis: the axis to sample along; the default 0 selects rows of a 2-D array.
- shuffle:
Trueby default. It only matters whenreplace=False, and setting it toFalseis a speedup.
Install or access
NumPy’s official install page gives these routes. With pip: pip install numpy. With conda: conda create -n my-env, then conda activate my-env, then conda install numpy. To keep a project isolated with a virtual environment: python -m venv my-env, then source my-env/bin/activate on macOS or Linux, or my-env\Scripts\activate on Windows, then install with pip.
Step-by-step task
Which API to use: Generator or legacy np.random.choice
Use the Generator API. The NumPy quick start says users create a Generator with default_rng and call its methods, and the legacy documentation says it recommends transitioning to Generator. The legacy numpy.random.choice(a, size=None, replace=True, p=None) differs in three documented ways: it has no axis argument, so it cannot sample rows of a 2-D array; it uses the C long dtype, which is 32-bit on Windows and 64-bit on most other 64-bit platforms; and module-level legacy functions run on a global RandomState instance, which the manual says is discouraged because results change as the code changes.
1. Create a generator and draw with replacement
- Import NumPy and create the generator:
import numpy as np rng = np.random.default_rng() - Draw three values from the integers 0 to 4:
rng.choice(5, 3)The manual notes this is equivalent to
rng.integers(0, 5, 3). - Draw from your own array:
names = ['pooh', 'rabbit', 'piglet', 'Christopher'] rng.choice(names, 5)
2. Draw without replacement
Set replace=False so no element appears twice: rng.choice(5, 3, replace=False). The manual notes this is equivalent to rng.permutation(np.arange(5))[:3]. The sample size cannot exceed the population when replace=False. To pick whole rows from a 2-D array, pass the array as a; the default axis=0 returns rows, as in the manual’s example rng.choice([[0,1,2],[3,4,5],[6,7,8]], 2, replace=False), which returns 2 rows.
3. Draw with weights
Pass p, one probability per element of a: rng.choice(5, 3, p=[0.1, 0, 0.3, 0.6, 0]). An element with probability 0 is never drawn. For strings, rng.choice(names, 5, p=[0.5, 0.1, 0.1, 0.3]) draws the first name about half the time.
One documented detail matters for reproducibility: passing p uses a more general but less efficient sampler, and it produces a different sample than the optimized sampler even when every element of p is 1/len(a). Adding a uniform p to existing code therefore changes the draws for a given seed.
4. Make the draw reproducible: numpy random seed
Without a seed, the generator is seeded from nondeterministic operating system data, so each run differs. Pass an integer seed to default_rng to reproduce a sequence: rng = np.random.default_rng(12345). The quick start says that the same seed gives the same output and that seeds should be large positive integers; it shows seed = secrets.randbits(128) as a way to generate one. Record the seed in your methods notes or code repository so another researcher can repeat the draw, which supports computational reproducibility.
The older np.random.seed reseeds the singleton RandomState behind the legacy functions. The legacy page says that class is frozen and guaranteed to produce the same values as the final NumPy 1.16 release, so old seeds keep reproducing old results. The quick start adds that Generator streams differ from legacy ones, so changing API changes the numbers even with the same seed number.
Related functions that people search alongside choice
- numpy random permutation:
Generator.permutation(x, axis=0)randomly permutes a sequence or returns a permuted range. Ifxis an integer it permutesnp.arange(x); if it is an array it returns a shuffled copy. - numpy random normal:
Generator.normal(loc=0.0, scale=1.0, size=None)draws from a normal distribution with the given mean and standard deviation. The unit Gaussian is also available asrng.standard_normal(10). - numpy random uniform:
Generator.uniformdraws samples from a uniform distribution betweenlowandhigh. - numpy random rand and randn: these are legacy names. The Generator equivalents are
rng.random(), a float in [0, 1), andrng.standard_normal(). - numpy random integer: the legacy
randintcorresponds torng.integers(low=0, high=10, size=5), which returns integers in [0, 10).
Output and how to read it
- With
size=Noneyou get a single value; for an integerait is a native Pythonint. - With a
sizeyou get anndarrayof that shape, with the dtype ofa. For a list of strings the dtype is a fixed-width string type. - With
replace=True, repeated values are expected. Withreplace=False, every value in one call is unique. - The values are pseudorandom: a seeded generator gives the same output every run, which is what makes an analysis repeatable.
Troubleshooting
The NumPy manual lists the conditions under which choice raises ValueError:
| Cause | Fix |
|---|---|
a is an integer less than 0 |
Pass a non-negative integer or an array. |
a is an array with size 0 |
Check that the list or array you pass is not empty. |
p is not 1-dimensional |
Pass a flat list or 1-D array of probabilities. |
a and p have different lengths |
Give exactly one probability per element of a. |
p is not a valid probability vector |
Probabilities must be non-negative and sum to 1. The manual gives p = p / np.sum(p, dtype=np.float64) to normalize. |
replace=False and size is larger than the population |
Lower size, or use replace=True if repeats are acceptable for your design. |
Alternatives (standard library and pandas)
| Tool | What the official documentation says |
|---|---|
Python random.sample(population, k, *, counts=None) |
Returns a list of k unique elements chosen from the population, for sampling without replacement. It raises ValueError if the sample size is larger than the population. |
Python random.choices(population, weights=None, *, cum_weights=None, k=1) |
Returns a list of k elements chosen with replacement, with optional weights. |
pandas DataFrame.sample(n=None, frac=None, replace=False, weights=None, random_state=None, axis=None, ignore_index=False) |
Samples rows (or columns) from a DataFrame, by count or fraction, with optional weights and a random_state for reproducibility. |
Use random.sample or random.choices for plain Python lists, and DataFrame.sample when the data already live in a pandas table. See also how to choose a statistical test and ANOVA in Python with statsmodels for what to do with the sampled data.
FAQ
What does numpy random choice do?
It generates a random sample from a given array, or from np.arange(a) when a is an integer, with or without replacement and with optional probabilities.
How do I sample without replacement in NumPy?
Call rng.choice(a, size, replace=False). The size must not exceed the population.
How do I get the same numpy random choice every time?
Create the generator with a fixed integer seed, np.random.default_rng(seed). For the legacy function, call np.random.seed first, remembering that legacy and Generator streams differ.
Should I use np.random.choice or rng.choice?
Use rng.choice. NumPy recommends moving to Generator, and only Generator.choice has the axis argument.
Does numpy random choice accept weights?
Yes, through p. It must be a valid probability vector, and it switches to a different sampler than the unweighted call.
Sources
- NumPy manual, numpy.random.Generator.choice (checked 2026-10-29).
- NumPy manual, numpy.random.choice (legacy) (checked 2026-10-29).
- NumPy manual, Legacy random generation and Random sampling quick start (checked 2026-10-29).
- NumPy manual, Generator.permutation and Generator.normal (checked 2026-10-29).
- NumPy installation (checked 2026-10-29).
- Python documentation, random and pandas DataFrame.sample (checked 2026-10-29).








