← Lab 4

Where am I?

In Colab every file lands in the same place, on a machine you never see. On your own laptop nothing lands anywhere predictable — and today you have 99 files to find.

Three commands, in the terminal
  • ▸pwd — print working directory. Where am I right now?
  • ▸ls — list what is in this folder. dir on Windows.
  • ▸cd sequences — go in. cd .. — go back up.
Why any of this matters

Your download went somewhere. Not the same somewhere as the person next to you — different username, different folder, and on Windows a \ instead of a /.

That is the entire reason Python has Path objects instead of just gluing strings together.

Open a terminal now — in VS Code it is ⌃` — and use those three commands to find the folder you unzipped.

Terminal → Python

Everything you just did by hand, pathlib does in code. Same ideas, same order — you already know what these mean.

in the terminal
in Python
what you get back
pwd
Path.cwd()
the folder you are in
ls
list(p.iterdir())
everything here, as Paths
ls *.fasta
p.glob("*.fasta")
only the ones you want
cd sequences
p / "sequences"
a path one level in
cd ..
p.parent
a path one level up
mkdir results
p.mkdir()
a new folder

One import gets you all of it: from pathlib import Path

A path is an object, not a string

Once it is a Path, you can ask it questions — and it answers the same way on every operating system.

pathlib
from pathlib import Path

data_dir = Path("/Users/you/Downloads/sequences")
bee = data_dir / "Bombus_terrestris.fasta"

bee.name      # 'Bombus_terrestris.fasta'
bee.stem      # 'Bombus_terrestris'  <- the species, free
bee.suffix    # '.fasta'
bee.exists()  # True — or a problem you can catch early
The slash builds paths

data_dir / "file.fasta" reads like a path and behaves like one. Python inserts the right separator, so the same code works on macOS and Windows.

.stem is doing real work today

Your 99 files are named Genus_species.fasta. That means .stem hands you the species name without a single line of string parsing — the filename is data.

Opening a file with with

Every file you open has to be closed again. Forget, and your results can sit in a buffer and never reach the disk. with closes it for you — including when your code crashes half way through.

reading
with open(bee) as f:
    text = f.read()

# ...and the file is closed here,
# even if something went wrong above.
writing — note the "w"
with open(out_file, "w") as f:
    f.write("species,dna,protein\n")
    for name, d, p in rows:
        f.write(f"{name},{d},{p}\n")

⚠ "w" empties the file first, without asking. Use "a" to add to the end instead.

💡 For a whole small file in one go, Path has shortcuts: p.read_text() and p.write_text(s).

The bug that catches everyone

Your script works. You run it from one folder up, and it cannot find anything. Nothing changed in the code — you did.

Path.cwd()

The folder your terminal was in when you pressed Enter. Move, and it is a different answer.

Path(__file__).parent

The folder the script is saved in. Same answer from anywhere — this is the one you usually want.

the safe version
# where you happen to be standing — this MOVES
Path.cwd()

# where this script lives — this does not
Path(__file__).parent

results_dir = Path(__file__).parent / "results"
results_dir.mkdir(parents=True, exist_ok=True)

exist_ok=True means running it a second time is not an error — and you will run it a second time.

Your turn — 99 bees

The job
  • 1Download bee_coi_sequences.zip and unzip it. Remember where it went.
  • 2Open drift.py in VS Code.
  • 3Fill in the five TODOs — every one of them is about finding, reading or writing files.
  • 4uv run drift.py — then read the error, fix, run again.
  • 5Open results/drift.csv. That file did not exist when you started.

The biology is already written — the codon table, translate(), the comparison. You are not here to do genetics today. You are here to point a program at a folder.

what a finished run looks like
Read 99 sequences from sequences/
Wrote 98 rows to .../results/drift.csv

species                        DNA  protein  silent
Bombus_cryptarum                36        1      35
Bombus_lucorum                  43        2      41
Bombus_ignitus                  48        5      43
Nomioides_minutissimus         152       61      91
Ceylalictus_variegatus         156       58      98
Andrena_agilissima             173       53     120
Read that first row again

Thirty-six differences in the DNA. One in the protein. The gene drifts; the enzyme it builds barely moves — because natural selection can only see the protein.

Everyone's first run fails, and everyone's fails differently — that is TODO 1 doing its job.

Python for Biologists