Environments and paths#

A path is an address for a file or folder. Research scripts use paths to locate raw data, configuration files, saved arrays, figures, and output tables. A path that only works from one person’s Desktop is one of the most common reasons an analysis fails on another computer.

This chapter separates two questions: which Python and packages execute the script? and which files does the script read? Environments answer the first; paths answer the second.

Before continuing, make sure you have completed one of the two environment routes in Setup before the workshop: Conda or venv + pip.

Conda and venv solve the same core problem#

Conda

venv + pip

Creates isolated environments

yes

yes

Selects a Python version

yes

uses an installed Python

Installs Python packages

conda install

python -m pip install

Can manage non-Python libraries

yes

no

Common environment location

central Conda directory

.venv inside the project

Useful here

ACN/MNE and compiled dependencies

lightweight general workshop setup

Neither tool makes an analysis reproducible by itself. Reproducibility comes from recording the packages in a file such as workshop-environment.yml or workshop-requirements.txt and testing that the environment can be recreated.

Three locations to distinguish#

These locations are related but not interchangeable. The Python interpreter is an executable program. An environment is the interpreter plus its installed packages. The working directory is the folder Python treats as the starting point for a relative file address.

Python interpreter  → which Python runs?
Environment         → which packages are installed?
Working directory   → where do relative paths begin?

Inspect them:

import sys
from pathlib import Path

print("Interpreter:", sys.executable)       # Python executable in use
print("Working directory:", Path.cwd())     # base for relative paths

Path.cwd() returns the current working directory as a Path object. It may change depending on whether code is launched from a terminal, VS Code’s Run button, or a notebook. This is why a relative path can work in one context and fail in another.

Building a path with Path#

Path represents a filesystem address as an object. The / operator joins path components; it does not divide numbers here because Path defines a path-specific meaning for that operator.

Read the next example one line at a time:

  • Path.cwd() supplies the starting folder.

  • project / "data" appends a folder named data.

  • the f-string inserts the participant ID into a filename.

  • the final / appends that filename to the data folder.

No folder or file is created by these expressions. They only construct an address.

from pathlib import Path

project = Path.cwd()                         # start at the project folder
data_dir = project / "data"                  # append the data folder
participant_id = "P07"                       # identify one participant
epochs_file = data_dir / f"{participant_id}_epochs.npy"  # build the filename

print(epochs_file)                            # display the constructed path
print(epochs_file.exists())                   # check whether the file exists

If the working directory is /Users/name/project, the resulting value is equivalent to /Users/name/project/data/P07_epochs.npy on macOS. On Windows, Path uses the appropriate drive and separators automatically.

Avoid string assembly such as:

epochs_file = str(project) + "/data/" + participant_id + "_epochs.npy"

That version mixes path logic with string formatting and assumes a separator. Keeping the value as a Path also gives access to filesystem methods.

Useful attributes and methods:

epochs_file.name                           # filename with extension
epochs_file.stem                           # filename without extension
epochs_file.suffix                         # extension, such as .npy
epochs_file.parent                         # containing folder
epochs_file.with_suffix(".csv")            # new path, original unchanged

Check with Python: edit the participant ID or suffix and inspect the resulting path.

Output will appear here.

Attributes such as .name, .stem, .suffix, and .parent describe the address and do not use parentheses. Methods such as .exists() and .with_suffix() perform an operation and therefore use parentheses. .with_suffix(".csv") returns a new path; it does not rename the original file.

Find collections of files#

glob searches a folder for names matching a pattern. In *_epochs.npy, * means “any sequence of characters,” so the pattern matches P01_epochs.npy and P07_epochs.npy, but not participants.csv. glob returns an iterable of Path objects; sorted makes their order deterministic.

epoch_files = sorted(data_dir.glob("*_epochs.npy"))  # find matching files

for path in epoch_files:                           # process each path
    print(path.stem)                                # show the participant name

Exercise 6 (Robust project path)

Suppose analysis.py lives inside src/, while data lives in data/ beside src/. Construct data/trials.csv relative to the script, not relative to wherever the user launched Python.

Solution to Exercise 6 (Robust project path)

from pathlib import Path

project_dir = Path(__file__).resolve().parent.parent
trials_path = project_dir / "data" / "trials.csv"

__file__ is the path of the running script. .resolve() makes it absolute, the first .parent moves from analysis.py to src, and the second moves from src to the project folder. Notebooks do not define __file__; in a notebook, use a known project root or locate it from Path.cwd() after checking the current directory.