Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Analysis in this course is done in Python. You are not expected to arrive knowing it; you are expected to leave able to load a CSV, fit a model, and make a publication-quality figure with error bars.

Setup

Any of these works. Pick one and stay with it.

Recommended — a local environment.

python -m venv phys320
source phys320/bin/activate        # Windows: phys320\Scripts\activate
pip install numpy scipy matplotlib pandas jupyterlab uncertainties imageio pyserial
jupyter lab

Alternative — Google Colab. Run in a browser. Check which packages are available in the current runtime and install missing ones in the first cell. Save the notebook and raw data to storage you control; the runtime is temporary.

Alternative — Anaconda. Create an environment and add any packages the experiment needs. Record the environment so someone else can rerun the work.

Two packages beyond the standard scientific stack are worth having:

The five things you will do every week

1. Load data

import numpy as np
import pandas as pd

df = pd.read_csv("week06_led_data.csv")     # header row with units in the name
print(df.head())
V, sV = df["V_on_V"].to_numpy(), df["sigma_V_V"].to_numpy()

Keep the raw file exactly as the instrument wrote it, and do the cleaning in the notebook. A CSV that has been hand-edited in a spreadsheet cannot be regenerated, and when the fit misbehaves you will want to know whether the data or the cleaning is at fault.

2. Fit

See the template in Uncertainty and Curve Fitting. When you have defensible standard uncertainties for each data point, pass sigma= and set absolute_sigma=True. Examine residuals before trusting parameter errors.

3. Propagate

from uncertainties import ufloat, umath

L  = ufloat(2.143, 0.002)      # m
dt = ufloat(14.3e-9, 0.4e-9)   # s
c  = 2 * L / dt                # round trip
print(f"c = {c:.4g} m/s")      # uncertainty carried through automatically

uncertainties tracks correlations, so x - x is exactly zero with zero uncertainty, as it should be — something the quadrature formula gets wrong if applied blindly.

4. Plot

import matplotlib.pyplot as plt
plt.rcParams.update({
    "figure.dpi": 130, "font.size": 11,
    "axes.grid": True, "grid.alpha": 0.3,
    "errorbar.capsize": 3,
})

Data are markers with error bars; models are lines without markers; axes carry a quantity and a unit; the caption says what the reader should notice. Save as PDF or SVG, not PNG, for anything that goes in a report.

5. Report the number

def quote(value, err, unit=""):
    """Round uncertainty to one significant digit (two if it starts with 1)."""
    from math import floor, isfinite, log10
    if not isfinite(err) or err <= 0:
        raise ValueError("err must be a positive finite standard uncertainty")
    power = floor(log10(err))
    leading = err / 10**power
    digits = 2 if leading < 2 else 1
    rounded_err = float(f"{err:.{digits}g}")
    places = digits - 1 - floor(log10(rounded_err))
    rounded_value = round(value, places)
    fmt = f".{max(places, 0)}f"
    return f"{rounded_value:{fmt}} ± {rounded_err:{fmt}} {unit}".strip()

print(quote(633.4271, 0.8113, "nm"))    # -> 633.4 ± 0.8 nm

Reading data out of the instruments

Getting numbers off the bench and into Python

Instrument

Export path

Notes

Tektronix / Rigol oscilloscope

USB stick → CSV

Two columns, time and volts, with a multi-line header. pd.read_csv(f, skiprows=N); check N against the file.

Thorlabs spectrometer kit (EDU-SPEA1/SPEB1)

Kit software → CSV

Wavelength in nm, intensity in counts. Take a dark spectrum and subtract it.

GM counter with serial output

pyserial → text

See the logging snippet below.

Arduino / Raspberry Pi Pico

Serial print → captured text

Print CSV lines directly; do not invent a binary format.

Phone or webcam image

PNG/JPEG → imageio or PIL

Shoot RAW or turn off auto-exposure and HDR, or the intensities are not linear.

Legacy PASCO .cap files

Capstone → File ▸ Export Data → CSV

Only needed for the archived data in old/laboratoryHandouts/data/.

Legacy Vernier .cmbl files

Logger Pro → File ▸ Export As ▸ CSV

Same.

Logging a serial stream

import serial, time, csv

with serial.Serial("/dev/ttyACM0", 115200, timeout=2) as ser, \
     open("counts.csv", "w", newline="") as f:
    w = csv.writer(f)
    w.writerow(["t_s", "counts"])
    t0 = time.time()
    while time.time() - t0 < 3600:            # one hour
        line = ser.readline().decode().strip()
        if line:
            w.writerow([round(time.time() - t0, 3), line])
            f.flush()                          # flush Python's output buffer

The f.flush() call sends each line from Python’s buffer to the operating system, so a Python process crash is less likely to lose the last readings. It does not guarantee that data reached the storage device; copy the raw file to durable storage after acquisition.

Images to intensity profiles

Experiments 4 and 5 photograph a fringe pattern and extract a profile.

import numpy as np, imageio.v3 as iio

img = iio.imread("fringes.png").astype(float)
if img.ndim == 3:
    img = img[..., :3].mean(axis=2)          # to grayscale; see the caution below
row  = slice(540, 560)                        # a band through the pattern
prof = img[row, :].mean(axis=0)               # averaging the band kills noise
x_px = np.arange(prof.size)

Notebook hygiene