Reproducible Random Seeds
Learn to generate reproducible random seeds in Python for data science. This tutorial covers the core concept, hands-on steps, troubleshooting, and what to study next.
Focus: generate reproducible random seeds
You've just trained a model, and the accuracy is great. You close the notebook, present the results, and try to rerun the same script the next day — but the numbers are different. The accuracy dropped, the AUC changed, and your validation split now holds different rows. This is the pain of non-reproducible randomness: your data science work looks like a lottery instead of an experiment. The solution is simple but foundational: generating reproducible random seeds. By the end of this lesson, you'll know how to set seeds inside NumPy, pandas, and scikit-learn, and you'll never lose a result to a silent random draw again.
The problem this lesson solves
Randomness is everywhere in data science. You use it to split data, initialize neural networks, shuffle batches, or sample a fraction of rows. That's powerful — randomness gives you different views of data and helps models escape local minima. But it's also treacherous: if you don't control that randomness, every run of your script produces a different outcome.
Imagine you're building a customer churn model. You run the notebook today and get an F1 score of 0.82. Tomorrow you rerun the same notebook, and the F1 is 0.79. You didn't change a line of code — but your train set now has different customers than yesterday, because the train_test_split function randomly chose a different slice. Now your stakeholders question your rigor, and you can't debug whether a code change helped or hurt, because the baseline keeps moving.
In production, reproducibility matters even more. You need to prove that the model you deployed behaves identically in staging and production. In experimentation, you need a fixed baseline to compare new features. In education, you want your students to see the same outputs as you do. Without a reproducible random seed, all of that is impossible.
This lesson solves that problem by teaching you how to generate and set reproducible random seeds in the Python data science stack — NumPy, pandas, scikit-learn, and even plain Python's random module. You'll learn the mental model, the exact function calls, and the common pitfalls that trip up even experienced developers.
Core concept / mental model
The core concept is simple: a random seed is the starting point for a deterministic sequence of pseudo-random numbers. Once you set that seed, the random number generator (RNG) produces the exact same sequence every time you rerun your code.
Think of a random number generator as a very long book of random numbers. Without a seed, the book opens to a random page every time you start reading. With a seed, you always open to the same page — say, page 42 — and read the numbers in the same order. The word "random" is a bit misleading; in practice, you're using a pseudo-random number generator (PRNG), which is a deterministic algorithm. Given the same seed, the PRNG always generates the same sequence.
Pro Tip: A seed doesn't make your data less random — it just makes the randomness repeatable. The statistical properties (like the distribution of samples) remain unchanged; only the sequence is fixed.
In Python's data science ecosystem, you'll deal with three main RNGs:
- Python's built-in
randommodule — used for simple sampling and general utilities. - NumPy's
np.randommodule — the workhorse for arrays, data shuffling, and most scientific computing. - scikit-learn's internal RNG — used in
train_test_split, model estimators, and cross-validation.
Each of these needs its own seed call. Setting one doesn't automatically set the others.
How it works step by step
Here's the step-by-step mental model for achieving reproducible randomness in your data science workflow:
- Identify every source of randomness in your pipeline — data splitting, model initialization, shuffling, sampling, and any augmentation steps.
- Set the global seed for each RNG library you use. For most data science work, that means:
-
np.random.seed(42)for NumPy -random.seed(42)for Python's random module -seed=42as a parameter in scikit-learn functions or by passing aRandomStateinstance - Use deterministic parameters in functions that accept a
random_stateargument (liketrain_test_split,KFold, orRandomForestClassifier). - Run your script twice and compare outputs — they should be identical.
- Record the seed value in a config file, environment variable, or a comment. That way, anyone can rerun your analysis later.
The mapping between libraries is straightforward:
| Library | Function to set seed | Example |
|---|---|---|
| Python random | random.seed(42) |
random.seed(42) |
| NumPy | np.random.seed(42) |
np.random.seed(42) |
| scikit-learn | random_state=42 arg |
train_test_split(..., random_state=42) |
| pandas | uses NumPy (set np.random.seed first) |
— |
Once you've set the seed, the generator's state is initialized. As you call random functions, the state advances, but on the next run it resets to the same initial state — if you call the seed function again.
Hands-on walkthrough
Let's put that into practice. We'll build a minimal pipeline that splits a dataset and fits a model, then verify the results are identical across two runs.
Step 1: Set the seed in NumPy and Python's random
Create a file reproducible_seed_demo.py:
import numpy as np
import random
# Seed both NumPy and Python's random
np.random.seed(42)
random.seed(42)
# Generate the same random array every time
arr = np.random.rand(3)
print("Array:", arr)
print("Python random:", random.random())
Run it twice:
python reproducible_seed_demo.py
python reproducible_seed_demo.py
You'll see the exact same output every time:
Array: [0.37454012 0.95071431 0.73199394]
Python random: 0.6394267984578837
Step 2: Reproducible train_test_split with scikit-learn
Now apply the same concept to a realistic split:
from sklearn.model_selection import train_test_split
import numpy as np
np.random.seed(42)
X = np.arange(10).reshape(-1, 1)
y = np.arange(10) * 2
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
print("Train indices:", X_train.flatten())
print("Test indices:", X_test.flatten())
Run it twice — the output is identical each time. The random_state=42 parameter is the seed for the splitter.
Step 3: Full reproducible ML pipeline
Let's bring it all together with a random forest classifier:
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
# Reproducibility seed
SEED = 42
np.random.seed(SEED)
# Create synthetic data
X, y = make_classification(n_samples=100, n_features=5, random_state=SEED)
# Split with seed
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=SEED)
# Model with explicit random_state
model = RandomForestClassifier(n_estimators=10, random_state=SEED)
model.fit(X_train, y_train)
print("Accuracy:", model.score(X_test, y_test))
Run the script twice and you'll see the same accuracy printed each time:0.95 (or whatever your run produces). The seed propagates through the data generation, the split, and the model's internal randomness.
Compare options / when to choose what
You have several ways to manage reproducibility in Python. Each has its strengths and trade-offs. Use the table below to decide what fits your project.
| Approach | Pros | Cons | Best for |
|---|---|---|---|
Global np.random.seed() |
Simple, one call | Affects all downstream NumPy code (can be global and hidden) | Quick experiments, scripts, education |
random_state parameters |
Explicit, scoped to that function | Must remember to pass it to every function | Production code, reusable functions, libraries |
RandomState instance |
Isolated and shiftable | More boilerplate to pass around | Complex pipelines, parallel processing, deterministic substreams |
| Config files / environment variables | Centralized, auditable | Adds indirection | Team projects, ML experiment tracking |
In practice, most data scientists use a hybrid: a global seed at the top of a script for convenience, plus random_state on every scikit-learn call for safety. For serious projects, consider using a RandomState instance and passing it explicitly — it prevents accidental reproducibility leaks across modules.
Troubleshooting & edge cases
Even with the best intentions, you'll hit a few classic traps. Here are the most common ones and their fixes:
You set np.random.seed() but random_state is still random
This happens when you call a scikit-learn function without passing random_state. The global seed sets NumPy's RNG, but some scikit-learn estimators use their own internal RNG that is not coupled to np.random.seed(). Always pass random_state explicitly.
You get the same output, but only within one notebook run
If you call np.random.seed(42) at the top of the notebook, that only affects the kernel's current state. If you run the same cell again, the generator has advanced to the next state, so the output changes. To repeat, you must re-execute the seed line. In a script, that happens naturally because the process restarts.
Your pandas .sample(frac=0.1) gives different rows
pandas uses NumPy's RNG under the hood. If you set np.random.seed() before the call, the same rows will be selected each time. If you don't, you'll get a different sample. For per-call control, use df.sample(frac=0.1, random_state=42) instead.
Different versions of libraries can break reproducibility
Even with the same seed, NumPy 1.19 and NumPy 2.0 might produce different pseudo-random sequences because the algorithms can change. Document your library versions in a requirements.txt or a lock file if reproducibility must persist across machines.
random.seed() and np.random.seed() are separate
You must set both if your code uses both libraries. Forgetting one leads to non-reproducible behavior in whichever module you forgot.
Pro Tip: Use a single constant like
SEED = 42at the top of your script and reuse it everywhere. It makes your code self-documenting and easy to change if you need to test a different seed.
What you learned & what's next
You've learned how to generate reproducible random seeds in Python — the core skill for ensuring your data science experiments are consistent and trustworthy. You can now:
- Explain why seeds matter for reproducibility in data science
- Set the seed in Python's
random, NumPy, and scikit-learn - Use
random_stateparameters in data splits and models - Troubleshoot common reproducibility pitfalls
This is the foundation for the next lesson in your data science path, where you'll apply these reproducibility techniques to design robust cross-validation workflows. With seeds at your side, every experiment you run will give you answers that you — and your colleagues — can confidently reproduce. Keep your seeds organized, document them, and you'll never chase a ghost result again.
Now, take a fresh dataset and run your entire workflow with and without a seed. Observe the difference — then commit to always keeping your experiments reproducible.
Practice recap
Take the full pipeline example from this lesson and modify the seed from 42 to 123. Run the script twice to confirm the outputs match. Then, write a small function that accepts a seed as an argument and returns reproducible split indices. Finally, try removing the random_state from one function and observe how it breaks reproducibility — this will help you remember why it's essential.
Common mistakes
- Forgetting to pass
random_stateto scikit-learn functions even after settingnp.random.seed()— global seeds don't cover all internal RNGs. - Re-running a Jupyter notebook cell without resetting the seed — the generator's state advances, so outputs change between runs.
- Using
np.random.seed()but notrandom.seed()when your code uses Python'srandommodule — the two are independent. - Hard-coding a seed inside a function without exposing it as a parameter, making it hard to change or reuse in different experiments.
Variations
- Use a dedicated
RandomStateinstance in NumPy, e.g.,rng = np.random.RandomState(42), and pass it to functions that accept a random generator. - Store your seed in an environment variable or config file so you can change it without editing code.
- Use
random_stateas a named parameter in every function that supports it, fromtrain_test_splittoRandomForestClassifier.
Real-world use cases
- Reproducing a churn prediction model's exact train-test split and AUC score for compliance audits.
- A/B testing platform that must re-run identical data splits to compare model versions fairly.
- Educational code examples where students must see the same output to verify their implementation.
Key takeaways
- Random seeds make pseudo-random sequences repeatable, turning noise into a deterministic experiment.
- Set
np.random.seed()andrandom.seed()at the top of your script for global control. - Pass
random_stateto every scikit-learn function for per-call reproducibility. - Document your seed in a constant or config to keep your experiments auditable.
- Library version changes can break reproducibility even with the same seed.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.