Written and maintained by CASRAI Editorial Board
Last updated
Researchers searching for this usually have a list of participants, records or samples and need to draw a random subset reproducibly, then report how they did it. Python offers four standard routes: random.sample(), random.choices(), NumPy’s Generator.choice() and pandas’ DataFrame.sample(). The short answer: use DataFrame.sample() when your sampling frame is a table, Generator.choice() when it is an array or a list of IDs, and the standard-library random.sample() for a plain list. Details below follow each library’s official documentation (read 2026-11-08).
What random sampling in Python is used for in research
A simple random sample gives every unit in the sampling frame the same chance of selection. In practice researchers use it to pick participants from a registry, to select records for a manual audit or coding check, to build a validation subset, or to draw a pilot sample from a larger dataset. Two choices matter before any code runs: whether units may be selected more than once (sampling with replacement) and whether selection probabilities are equal or weighted. Python’s functions differ on exactly these points.
Install or access
The random module ships with Python itself (docs.python.org). NumPy and pandas are separate packages from their official projects; install them with pip install numpy pandas and check the installed versions with numpy.__version__ and pandas.__version__, which you will want to record for your methods section.
Choosing the right function
| Function | Replacement | Weights | Seed / reproducibility | Input |
|---|---|---|---|---|
random.sample(population, k, *, counts=None) |
Without: returns k unique elements | None (the counts argument, added in 3.9, repeats elements instead) |
random.seed(a) on the module |
A sequence (since Python 3.11 a set is no longer converted automatically) |
random.choices(population, weights=None, *, cum_weights=None, k=1) |
With | weights or cum_weights, same length as the population |
random.seed(a) |
A sequence |
Generator.choice(a, size=None, replace=True, p=None, axis=0, shuffle=True) |
Either, set by replace |
p, must sum to 1 |
Seed passed to np.random.default_rng() |
Array-like, or an integer n meaning arange(n) |
DataFrame.sample(n=None, frac=None, replace=False, weights=None, random_state=None, axis=None, ignore_index=False) |
Either, set by replace |
weights: a column name or an aligned Series |
random_state (int, BitGenerator, RandomState or Generator) |
A DataFrame or Series |
Sources: Python random docs, NumPy Generator.choice docs and pandas DataFrame.sample docs (all read 2026-11-08). The default of replace differs: Generator.choice defaults to True, while random.sample and DataFrame.sample sample without replacement by default.
Step-by-step: draw a reproducible simple random sample
1. Sampling from a table with pandas
- Load your sampling frame into a DataFrame, one row per eligible unit, with duplicates and ineligible units already removed. Sampling cannot correct a flawed frame.
- Call
samplewith either a count or a fraction, and a fixed seed:sample_df = df.sample(n=100, random_state=42)or, for a proportion of the rows:
sample_df = df.sample(frac=0.10, random_state=42)nandfracare mutually exclusive; if neither is given,n=1. - Set
replace=Trueonly if you want sampling with replacement. It is required whenfracis greater than 1. - Optionally pass
ignore_index=Trueto relabel the result 0 to n-1.
2. Sampling IDs or an array with NumPy
- Create a generator with a recorded seed:
rng = np.random.default_rng(12345). - Draw without replacement:
chosen = rng.choice(ids, size=100, replace=False). For a 2-D array,axis=0(the default) selects rows.
According to the NumPy documentation, rng.choice(5, 3, replace=False) is equivalent to rng.permutation(np.arange(5))[:3], and supplying p uses a slower sampler.
3. Sampling a plain list with the standard library
import random, then optionallyrandom.seed(2026). An integer seed is used directly; withNone, Python uses system time or operating-system randomness, so results will differ on each run.subset = random.sample(ids, k=100)returns a list of 100 unique elements.- For sampling with replacement or unequal weights use
random.choices(ids, weights=w, k=100).
4. Record what you did
For the methods section, report the sampling frame and its size, the function used, the sample size, whether sampling was with or without replacement, the seed, and the Python, NumPy and pandas versions. A seed alone does not reproduce a draw across libraries, so name the exact function. This supports reproducibility of the selection step.
Output and how to read it
Each function returns the selected elements: random.sample and random.choices return a list, Generator.choice an array, and DataFrame.sample a DataFrame or Series whose index labels are those of the original rows (unless ignore_index=True). In the pandas documentation example, df["num_legs"].sample(n=3, random_state=1) returns three index-labelled rows (fish, spider, falcon): the labels show which original records were drawn, so keep them as your record of selection. Row order in the output is random too; sort by index if you need the original order.
Weighted draws: pandas normalises weights that do not sum to 1, treats NaN weights as zero and does not allow infinite weights. A selected sample is one realisation; its estimates differ from the population value by sampling error, which a larger n reduces.
Troubleshooting
| Symptom | Cause (per official docs) | Fix |
|---|---|---|
ValueError from random.sample |
k larger than the population | Reduce k, or sample with replacement using random.choices |
Error passing a set to random.sample (Python 3.11+) |
Population must be a sequence; sets are no longer converted | Convert first, for example sorted(my_set) |
TypeError from random.choices |
Both weights and cum_weights were given |
Supply only one |
ValueError from random.choices |
All weights are zero (3.9+) | Give at least one non-zero weight |
IndexError from random.choices |
Empty population | Check the filter that built the list |
ValueError from Generator.choice |
replace=False with size larger than the population |
Reduce size or set replace=True |
Error from DataFrame.sample with frac above 1 |
replace must be True when frac is greater than 1 |
Set replace=True or use frac of 1 or less |
Error combining n and frac |
The two are mutually exclusive | Pass one |
| Weighted pandas draw rejected | With replace=False, n*max(weights)/sum(weights) above 1 is not allowed |
Lower n or use replace=True |
| Different sample each run | No seed set (seed(None), no random_state) |
Set and record a seed |
Do not use the random module for security purposes; the Python documentation directs security uses to the secrets module. Sampling within groups (stratified draws) is available through pandas’ DataFrameGroupBy.sample, listed under “See also” in the DataFrame.sample documentation; see our stratified sampling guide for the design side.
Alternatives within Python
All four options below are part of Python or the NumPy and pandas packages. Pick by input type:
| If your data is | Use | Why (from the docs) |
|---|---|---|
| A DataFrame | DataFrame.sample |
Keeps index labels, accepts frac, takes weights from a column |
| A NumPy array or ID vector | Generator.choice |
Seeded generator object; replace and p arguments |
| A short list, no extra packages | random.sample |
Standard library; unique elements only |
| Need replacement or weights on a list | random.choices |
Weights or cumulative weights, k draws with replacement |
For deeper treatment of NumPy’s function see NumPy random choice; for fixed-interval selection see systematic sampling.
FAQ
How do I take a random sample without replacement in Python?
Use random.sample(population, k), rng.choice(a, size, replace=False) with a NumPy generator, or df.sample(n=...) in pandas, which does not replace by default.
How do I make a random sample reproducible?
Set a seed: random.seed(n) for the standard library, np.random.default_rng(n) for NumPy, or random_state=n in pandas. Record the seed and library versions.
What is the difference between random.sample and random.choices?
random.sample returns unique elements (without replacement); random.choices draws with replacement and accepts weights.
Can I sample a percentage of rows in pandas?
Yes: df.sample(frac=0.2) returns 20% of rows. Do not combine frac with n.
Is Python’s random module suitable for cryptography?
No. The documentation says to use secrets for security purposes.
Sources
- Python Software Foundation, random: Generate pseudo-random numbers, read 2026-11-08.
- pandas documentation, pandas.DataFrame.sample (pandas 3.1.1 docs), read 2026-11-08.
- NumPy documentation, numpy.random.Generator.choice, read 2026-11-08.







