Python Crash Course for Ecologists
Enough Python to read the code in this course, modify it, and debug it when it breaks. You will not be asked to write programs from scratch.
0. Install everything
Before Day 1 you need a working Python environment. If the word
"environment" makes you nervous, read the
Python Environments & Conda tutorial first
— it explains why we use envs and walks through Miniforge install,
activation, and the one-time creation of the course's aiecol env in
much more detail than this section. The blocks below are the short version.
macOS
# Install Homebrew if you don't have it.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install Miniforge (conda + mamba) and a few system tools.
brew install --cask miniforge
brew install git ffmpeg
# Set up conda for your shell (one-time)
conda init zsh
# Close and reopen your terminal.
# Create the course environment (see the environments tutorial for what's happening).
mamba create -n aiecol -c conda-forge python=3.11 sox ffmpeg -y
conda activate aiecol
pip install jupyterlab numpy pandas matplotlib scikit-learn torch torchvision \
ultralytics opencv-python soundfile librosa
Windows
# Open PowerShell and install via winget.
winget install Git.Git
winget install CondaForge.Miniforge3
# Open a NEW "Miniforge Prompt" (Start Menu) — or restart PowerShell after
# `conda init powershell`. Then:
mamba create -n aiecol -c conda-forge python=3.11 sox ffmpeg -y
conda activate aiecol
pip install jupyterlab numpy pandas matplotlib scikit-learn torch torchvision `
ultralytics opencv-python soundfile librosa
torch torchvision line with the version from
pytorch.org/get-started/locally that
matches your CUDA version. On Apple Silicon the default pip install torch uses the
GPU automatically through MPS.
1. Running Python
You will use three things:
- Jupyter Lab for exploration. Run
jupyter labin a terminal with theaiecolenv active and a browser tab opens. - VS Code for editing scripts. Install the "Python" and "Jupyter" extensions from Microsoft.
- The terminal for running long scripts:
python train.py.
2. The minimum Python you need
Variables, lists, dicts
# Numbers, strings, and booleans
n_cameras = 24
species = "Odocoileus hemionus"
is_endangered = False
# Lists: ordered, can be modified
sites = ["Crater_Lake_A", "Crater_Lake_B", "Mt_Hood_01"]
sites.append("Mt_Hood_02")
print(sites[0]) # "Crater_Lake_A"
print(len(sites)) # 4
# Dicts: key-value pairs (your most useful data structure)
detection = {
"image": "IMG_0042.JPG",
"species": "deer",
"confidence": 0.87,
"bbox": [120, 230, 540, 720], # x1, y1, x2, y2
}
print(detection["species"]) # "deer"
Loops, conditions, functions
for site in sites:
print(f"Processing {site}")
# A function — wrap any logic you'll use twice
def is_high_confidence(det, threshold=0.7):
"""Return True if the detection is above the confidence threshold."""
return det["confidence"] >= threshold
if is_high_confidence(detection):
print("Keep this one")
Reading files
from pathlib import Path
# Walking a directory of camera-trap images
image_dir = Path("/Users/you/oregon_critters/images")
for img_path in image_dir.glob("**/*.JPG"):
print(img_path.name, img_path.stat().st_size)
3. NumPy — arrays of numbers
NumPy is the foundation of everything else (PyTorch, OpenCV, librosa). Arrays are like R vectors / matrices, but with dimensions explicit.
import numpy as np
counts = np.array([3, 7, 0, 12, 2, 5])
print(counts.mean()) # 4.83
print(counts.sum()) # 29
print(counts[counts > 3]) # [ 7 12 5]
# A grayscale image is just a 2-D array of pixel values 0-255
img = np.zeros((480, 640), dtype=np.uint8) # all-black 480x640 image
img[100:200, 200:300] = 255 # paint a white rectangle
4. pandas — tabular data
If you know R's data.frame, you know pandas. A DataFrame is rows and
columns with labels.
import pandas as pd
# Read a camera-trap detection CSV
df = pd.read_csv("detections.csv")
print(df.head())
print(df["species"].value_counts()) # how many of each species?
# Filter to high-confidence deer
deer = df[(df["species"] == "deer") & (df["confidence"] > 0.7)]
# Group by site and count
by_site = df.groupby("site").size().reset_index(name="n_detections")
by_site.to_csv("per_site_counts.csv", index=False)
5. matplotlib — plots
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(by_site["site"], by_site["n_detections"])
ax.set_ylabel("Number of detections")
ax.set_title("Detections per site")
plt.xticks(rotation=45, ha="right")
plt.tight_layout()
plt.savefig("detections_per_site.png", dpi=150)
plt.show()
6. Reading EXIF timestamps from a camera-trap photo
You'll need this in Lab 1 to bin detections by hour of day.
from PIL import Image, ExifTags
from datetime import datetime
def read_timestamp(path):
"""Return a datetime from a camera-trap JPEG's EXIF DateTimeOriginal field."""
img = Image.open(path)
exif = {ExifTags.TAGS.get(k): v for k, v in (img._getexif() or {}).items()}
raw = exif.get("DateTimeOriginal") # "2023:07:14 06:21:43"
if raw is None:
return None
return datetime.strptime(raw, "%Y:%m:%d %H:%M:%S")
ts = read_timestamp("IMG_0042.JPG")
print(ts.hour) # e.g. 6 → dawn
7. Common errors and what they mean
| Error | What it usually means |
|---|---|
ModuleNotFoundError |
The package isn't installed in this env. Run conda activate aiecol first,
then pip install <name>. |
IndentationError |
You mixed tabs and spaces, or your indentation is uneven. Always use 4 spaces. |
KeyError: 'species' |
The dict or DataFrame doesn't have that column. Print the keys/columns first to check. |
CUDA out of memory |
Your GPU ran out of RAM. Lower the batch size, or run with device='cpu'. |
8. Where to go for help
- Built-in: in Jupyter, type
pd.read_csv?and shift-enter to see the docstring. - RealPython for friendly long-form tutorials.
- Claude Code — ask "explain what this error means and how I'd fix it", pasting the full traceback. (See Claude Code dos & don'ts.)