Setting Up a Reproducible Analytical Environment
In Data Science Foundations, you learned how to inspect, clean, summarize, and visualize data.
In this guide, you will extend those skills into applied workflows that must remain:
- reproducible
- structured
- reliable over time
- ready for extension
This requires a more deliberate setup.
An environment is not just where code runs. It is the foundation of the analytical workflow you are building.
Why Setup Matters
A data science project may begin as a notebook, a script, or a small experiment. As soon as the work becomes important, however, its setup also becomes important.
A reliable project should make it clear:
- where data is stored
- where code lives
- where outputs are written
- which dependencies are required
- how the project can be rebuilt
- how another person can reproduce the work
Without this structure, an analysis becomes difficult to trust, review, and reuse.
The goal of this lesson is to create a stable project foundation before moving into feature engineering, model building, model evaluation, interpretation, and deployment.
What This Setup Supports
By the end of this lesson, you should have an environment that supports:
- consistent execution across sessions
- controlled Python dependencies
- structured project organization
- reproducible checks and builds
- reusable scripts
- preparation for modeling, pipelines, and deployment
The setup is intentionally simple. The goal is to make the project understandable, repeatable, and ready to grow without over-engineering it.
Project Structure
A recommended structure for this guide is shown below.
project-root/
│
├── data/
│ ├── raw/
│ ├── processed/
│ └── reference/
│
├── scripts/
│ ├── bash/
│ └── python/
│
├── notebooks/
├── models/
├── reports/
├── docs/
│
├── requirements.txt
├── requirements-lock.txt
├── _quarto.yml
│
├── index.qmd
├── 00-preface.qmd
├── 01-setting-up-environment.qmd
├── 02-feature-engineering.qmd
├── 03-model-building.qmd
├── 04-model-evaluation.qmd
└── ...
Each folder or file has a clear role.
| Folder or file | Purpose |
|---|---|
data/raw/ |
Original input data that should not be edited manually |
data/processed/ |
Cleaned or transformed data used for analysis |
data/reference/ |
Lookup tables, metadata, labels, or supporting files |
scripts/bash/ |
Shell scripts for setup, checks, and project builds |
scripts/python/ |
Python scripts for preparation, modeling, and validation |
notebooks/ |
Exploratory notebooks, when needed |
models/ |
Saved models and model-related outputs |
reports/ |
Analytical summaries, tables, and generated outputs |
docs/ |
Rendered Quarto website output |
requirements.txt |
Direct Python dependencies needed by the project |
requirements-lock.txt |
Snapshot of all packages installed in the active environment |
_quarto.yml |
Quarto project configuration |
A clear structure reduces confusion and makes the project easier to review, teach, publish, and extend.
Work from the Project Root
Run the commands in this lesson from the project root: the directory containing _quarto.yml and requirements.txt.
Check your current location:
pwdOn Windows PowerShell, use:
Get-LocationThe reusable scripts later in this lesson can locate the project root from their own file locations. Even so, working from the project root is a useful and consistent project habit.
Create the Project Folders
From the project root, create the core folders.
mkdir -p data/raw data/processed data/reference
mkdir -p scripts/bash scripts/python
mkdir -p notebooks models reports docsCheck that the folders were created.
find . -maxdepth 2 -type d | sortYou should see the main project directories in the output.
Create a Python Environment
A virtual environment keeps project dependencies separate from packages installed elsewhere on your computer.
Create an environment named .venv:
python -m venv .venvActivate it on macOS or Linux:
source .venv/bin/activateActivate it on Windows PowerShell:
.venv\Scripts\Activate.ps1Verify the Python interpreter.
python --version
python -c "import sys; print(sys.executable)"When the environment is active, the executable path should point inside the project’s .venv directory.
The scripts in this lesson require Python 3.10 or later. Python 3.12 is a suitable choice for this guide.
Control Project Dependencies
Dependencies should be recorded so the project can be rebuilt later.
Create requirements.txt and add the direct packages used by the guide.
pandas
numpy
scikit-learn
matplotlib
joblib
fastapi
uvicorn
jupyterlab
Install them through the Python interpreter in the active environment.
python -m pip install --upgrade pip
python -m pip install -r requirements.txtAfter installation, record the exact package versions in the current environment.
python -m pip freeze > requirements-lock.txtThe two files serve different purposes:
requirements.txtlists the direct dependencies intended for the project.requirements-lock.txtrecords a complete snapshot of packages installed in the active environment, including indirect dependencies.
pip freeze creates an environment snapshot, not a fully portable lock file. Package availability and compatibility may still vary by operating system, Python version, and hardware architecture. Record the Python version alongside the snapshot and test the environment when moving the project to another system.
Check the Quarto Project
This guide uses Quarto for reproducible reporting and publishing.
Check that Quarto is available.
quarto --versionDuring development, preview the guide with live updates.
quarto previewWhen you are ready to generate the complete output, render the project.
quarto renderIf rendering succeeds, Quarto writes the website to the output directory configured in _quarto.yml, usually docs/.
Reusable Project Scripts
The following scripts turn the manual setup steps into repeatable project commands. Save them in the paths shown and run them from the project root.
Script 01A — Create the Project Structure
Create:
scripts/bash/01a-create-project-structure.sh
Add:
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
project_root="$(cd -- "$script_dir/../.." && pwd)"
cd "$project_root"
echo "Creating Applied Data Science project structure..."
mkdir -p data/raw data/processed data/reference
mkdir -p scripts/bash scripts/python
mkdir -p notebooks models reports docs
echo "Project folders created."
echo
echo "Current project structure:"
find . -maxdepth 2 -type d | sortRun it:
bash scripts/bash/01a-create-project-structure.shThe script first identifies the project root from its own location. It therefore behaves consistently even if it is launched from another directory.
Script 01B — Check the Environment
Create:
scripts/python/01b_check_environment.py
Add:
from importlib.util import find_spec
from pathlib import Path
import shutil
import sys
PROJECT_ROOT = Path(__file__).resolve().parents[2]
MINIMUM_PYTHON = (3, 10)
REQUIRED_DIRS = [
"data/raw",
"data/processed",
"data/reference",
"scripts/bash",
"scripts/python",
"notebooks",
"models",
"reports",
"docs",
]
REQUIRED_FILES = [
"requirements.txt",
"_quarto.yml",
]
REQUIRED_PACKAGES = [
"pandas",
"numpy",
"sklearn",
"matplotlib",
"joblib",
"fastapi",
"uvicorn",
"jupyterlab",
]
def display_paths(title: str, paths: list[str]) -> None:
if paths:
print(title)
for path in paths:
print(f"- {path}")
print()
expected_venv = (PROJECT_ROOT / ".venv").resolve()
active_prefix = Path(sys.prefix).resolve()
missing_dirs = [
path for path in REQUIRED_DIRS
if not (PROJECT_ROOT / path).is_dir()
]
missing_files = [
path for path in REQUIRED_FILES
if not (PROJECT_ROOT / path).is_file()
]
missing_packages = [
package for package in REQUIRED_PACKAGES
if find_spec(package) is None
]
python_supported = sys.version_info >= MINIMUM_PYTHON
venv_active = sys.prefix != sys.base_prefix
project_venv_active = venv_active and active_prefix == expected_venv
quarto_available = shutil.which("quarto") is not None
print(f"Project root: {PROJECT_ROOT}")
print(f"Python executable: {sys.executable}")
print(f"Python version: {sys.version.split()[0]}")
print(f"Project .venv active: {'yes' if project_venv_active else 'no'}")
print(f"Quarto available: {'yes' if quarto_available else 'no'}")
print()
display_paths("Missing directories:", missing_dirs)
display_paths("Missing files:", missing_files)
display_paths("Missing Python packages:", missing_packages)
errors = []
if not python_supported:
errors.append("Python 3.10 or later is required.")
if not project_venv_active:
errors.append("Activate the project's .venv environment.")
if not quarto_available:
errors.append("Install Quarto or add it to PATH.")
if missing_dirs or missing_files or missing_packages:
errors.append("Create or install the missing project requirements listed above.")
if errors:
for error in errors:
print(f"ERROR: {error}")
raise SystemExit(1)
print("Environment check passed.")Run it:
python scripts/python/01b_check_environment.pyThis check distinguishes directories from files, verifies Python 3.10 or later, confirms that the project’s .venv is active, checks core package imports, and confirms that Quarto is available.
The import name for scikit-learn is sklearn. This is why the environment check uses sklearn even though requirements.txt uses scikit-learn.
Script 01C — Check and Build the Project
Create:
scripts/bash/01c-build-project.sh
Add:
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
project_root="$(cd -- "$script_dir/../.." && pwd)"
cd "$project_root"
if ! command -v python >/dev/null 2>&1; then
echo "ERROR: Python is not available on PATH." >&2
exit 1
fi
if ! command -v quarto >/dev/null 2>&1; then
echo "ERROR: Quarto is not available on PATH." >&2
exit 1
fi
echo "Checking the project environment..."
python scripts/python/01b_check_environment.py
echo
echo "Rendering the Quarto project..."
quarto render
echo
echo "Build complete."Run it:
bash scripts/bash/01c-build-project.shThe build stops if Python, Quarto, or any checked project requirement is unavailable. Quarto renders only after the environment check passes.
Script 01D — Record Dependency Versions
Create:
scripts/bash/01d-freeze-dependencies.sh
Add:
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
project_root="$(cd -- "$script_dir/../.." && pwd)"
cd "$project_root"
if ! command -v python >/dev/null 2>&1; then
echo "ERROR: Python is not available on PATH." >&2
exit 1
fi
expected_prefix="$project_root/.venv"
active_prefix="$(python -c 'import sys; print(sys.prefix)')"
if [[ "$active_prefix" != "$expected_prefix" ]]; then
echo "ERROR: Activate the project's .venv before freezing dependencies." >&2
echo "Expected: $expected_prefix" >&2
echo "Active: $active_prefix" >&2
exit 1
fi
echo "Recording Python dependency versions..."
python -m pip freeze > requirements-lock.txt
echo "Dependency snapshot written to requirements-lock.txt."Run it only after activating the project’s .venv:
bash scripts/bash/01d-freeze-dependencies.shThe script refuses to create the snapshot from a global environment or a different virtual environment. This prevents unrelated packages from being recorded accidentally.
Recommended Workflow
After the first setup, a typical working session becomes:
source .venv/bin/activate
python scripts/python/01b_check_environment.py
quarto previewWhen dependencies change:
python -m pip install -r requirements.txt
bash scripts/bash/01d-freeze-dependencies.shBefore publishing or sharing the complete guide:
bash scripts/bash/01c-build-project.shThis sequence separates three useful activities:
- environment validation
- interactive preview during development
- complete rendering before publication
Common Setup Problems
Python does not point inside .venv
Activate the project environment and check again.
source .venv/bin/activate
python -c "import sys; print(sys.executable)"A required package cannot be imported
Install the declared dependencies through the active Python interpreter.
python -m pip install -r requirements.txtQuarto is not found
Install Quarto using the official installer for your operating system, then open a new terminal and run:
quarto --versionThe project renders from one location but not another
Run commands from the project root and avoid hard-coded absolute paths in analytical code. Build file paths from a known project location instead.
Completion Check
Before continuing, confirm that:
- the project folders exist
.venvis created and active- Python 3.10 or later is available
requirements.txtcontains the direct dependencies- the required packages are installed
- Quarto is available
- the environment check passes
quarto previewopens the guide- the full project renders successfully
requirements-lock.txtwas created from the project environment
At this point, the project has a reproducible foundation for the applied workflow developed in the remaining chapters.
Next Step
The next chapter moves from project setup to feature engineering: transforming raw variables into useful, well-defined inputs for modeling while protecting the validity of the analytical workflow.