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

Sponsored

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 random module — used for simple sampling and general utilities.
  • NumPy's np.random module — 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:

  1. Identify every source of randomness in your pipeline — data splitting, model initialization, shuffling, sampling, and any augmentation steps.
  2. 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=42 as a parameter in scikit-learn functions or by passing a RandomState instance
  3. Use deterministic parameters in functions that accept a random_state argument (like train_test_split, KFold, or RandomForestClassifier).
  4. Run your script twice and compare outputs — they should be identical.
  5. 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 = 42 at 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_state parameters 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_state to scikit-learn functions even after setting np.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 not random.seed() when your code uses Python's random module — 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

  1. Use a dedicated RandomState instance in NumPy, e.g., rng = np.random.RandomState(42), and pass it to functions that accept a random generator.
  2. Store your seed in an environment variable or config file so you can change it without editing code.
  3. Use random_state as a named parameter in every function that supports it, from train_test_split to RandomForestClassifier.

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() and random.seed() at the top of your script for global control.
  • Pass random_state to 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.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.