How to Plot Physics Data in Python: Error Bars, Fits, and Residuals
pythondata visualizationlab analysismatplotlibcurve fittingphysics tutorial

How to Plot Physics Data in Python: Error Bars, Fits, and Residuals

PPhysics Direct Editorial
2026-06-11
10 min read

A reusable workflow for plotting physics data in Python with error bars, fitted curves, and residuals for clearer lab analysis.

Good physics plots do more than make a lab report look neat. They show uncertainty honestly, make trends visible, and help you decide whether a model actually fits your data. This tutorial gives you a repeatable workflow for plotting physics data in Python with error bars, fitted curves, and residuals using tools that are common in student labs and research notebooks. The goal is not just to produce one figure, but to build a reliable process you can reuse across mechanics, optics, electromagnetism, thermodynamics, and beyond.

Overview

If you are learning physics lab data analysis, the main challenge is rarely the plotting command itself. The harder part is knowing what a complete figure should include and what standard of evidence your graph is supposed to support. In many lab settings, a figure should answer at least four questions:

  • What was measured on each axis, with units?
  • How uncertain are the measurements?
  • What model is being compared to the data?
  • Where does the model systematically miss?

That is why the workflow in this article focuses on three elements together: the raw data with error bars, a fit based on a stated model, and a residual plot underneath. Used together, these give a much clearer picture than a smooth line alone.

We will use Python with NumPy, Matplotlib, and SciPy. Those tools cover a large share of what students need for plotting and curve fitting physics Python tasks. The examples here are intentionally general, so you can adapt them to a spring constant measurement, Ohm’s law, exponential decay, diffraction data, or a heat transfer experiment.

As a practical rule, think of your plotting workflow in this order:

  1. Import and inspect data.
  2. Define uncertainties.
  3. Choose a model from the physics.
  4. Fit the model carefully.
  5. Plot the data and fit clearly.
  6. Check residuals before drawing conclusions.

If you are still building your background in experimental topics, it can help to pair this article with concept refreshers such as Simple Harmonic Motion Explained: Springs, Pendulums, and Energy, Electric Fields and Electric Potential Explained with Visual Intuition, or Thermodynamics Laws Explained: Internal Energy, Heat, Work, and Entropy.

Step-by-step workflow

Here is the process to follow when you want to plot physics data Python style in a way that is defensible in a lab report or notebook.

1. Start with clean, labeled arrays

Your first job is to make the variables readable. Avoid anonymous names like a, b, and c unless they truly belong inside a short derivation. In code for analysis, prefer names tied to the experiment.

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

# Example data: extension vs force for a spring
force_N = np.array([0.5, 1.0, 1.5, 2.0, 2.5, 3.0])
extension_m = np.array([0.012, 0.025, 0.036, 0.051, 0.061, 0.074])

# Estimated uncertainties
force_unc_N = np.full_like(force_N, 0.02)
extension_unc_m = np.full_like(extension_m, 0.002)

Even in a simple analysis, this helps you avoid mistakes with units and interpretation. If your data come from CSV files, read them in and rename columns immediately. The less guessing you do later, the better.

2. Plot the raw data before fitting anything

This step is easy to skip and often where avoidable errors enter. A quick scatter plot helps you catch outliers, transcription mistakes, or unit problems before you build a model around them.

plt.figure(figsize=(6,4))
plt.errorbar(force_N, extension_m,
             xerr=force_unc_N, yerr=extension_unc_m,
             fmt='o', capsize=3, label='Measured data')
plt.xlabel('Force (N)')
plt.ylabel('Extension (m)')
plt.title('Spring extension vs force')
plt.legend()
plt.tight_layout()
plt.show()

This is the basic error bars Python Matplotlib pattern you will return to often. Note a few choices that are worth keeping:

  • capsize=3 makes error bars readable.
  • Axis labels include units.
  • The title identifies the relationship without overexplaining.

If you do only one thing better in your next lab report, make it this: always label axes with quantities and units.

3. Choose the model from the physics, not from the plotting software

In many beginner analyses, students choose a polynomial because it is easy to fit. That can be useful for interpolation, but it is often not the right scientific model. Start from the equation suggested by the experiment.

For the spring example, Hooke’s law suggests a linear relationship:

def linear_model(x, m, c):
    return m*x + c

Here, m is the slope and c is the intercept. Depending on the lab, you may have a physical reason to constrain the intercept to zero, or you may leave it free to reveal systematic offset. That choice should be stated in your method or caption.

Other common lab models include:

  • Exponential decay: radioactive decay, capacitor discharge
  • Sinusoidal functions: oscillations and waves
  • Inverse square laws: some field and intensity relationships
  • Gaussian profiles: beam spots, some spectral features

When in doubt, write down the governing physics first. If needed, review the relevant theory before fitting. For example, work on induction experiments may connect well with Magnetism and Electromagnetic Induction Explained Simply, and lens or diffraction work pairs naturally with Geometric Optics Explained: Mirrors, Lenses, and Image Formation.

4. Fit the model with uncertainties where possible

For many standard lab tasks, scipy.optimize.curve_fit is enough. If you know the uncertainty in the dependent variable, pass it in using sigma. This gives the fitter information about how much each point should influence the result.

params, covariance = curve_fit(
    linear_model,
    force_N,
    extension_m,
    sigma=extension_unc_m,
    absolute_sigma=True
)

slope, intercept = params
slope_unc, intercept_unc = np.sqrt(np.diag(covariance))

print(f"slope = {slope:.4f} ± {slope_unc:.4f}")
print(f"intercept = {intercept:.4f} ± {intercept_unc:.4f}")

A few notes matter here:

  • sigma should usually be the uncertainty in y, not x, for standard curve_fit use.
  • absolute_sigma=True tells SciPy to treat the provided uncertainties as absolute rather than relative scaling factors.
  • The diagonal of the covariance matrix gives parameter variance estimates.

If uncertainties in x are large and important, a basic least-squares approach may not be enough. In that case, methods such as orthogonal distance regression may be more appropriate. For many student labs, though, careful use of curve_fit is a solid starting point.

5. Generate a smooth fit line for plotting

Never connect sparse data points directly and call that the model. Instead, evaluate the fitted function on a fine grid.

x_fit = np.linspace(force_N.min(), force_N.max(), 300)
y_fit = linear_model(x_fit, slope, intercept)

This gives you a clean visual separation between measured points and the fitted model.

6. Build a figure with data and residuals

A physics figure becomes much more informative when the residuals are placed below the main plot. Residuals are simply the differences between measured values and the model prediction:

residuals = extension_m - linear_model(force_N, slope, intercept)

Now create a two-panel figure.

fig, (ax1, ax2) = plt.subplots(
    2, 1, figsize=(7, 6), sharex=True,
    gridspec_kw={'height_ratios': [3, 1]}
)

# Top panel: data and fit
ax1.errorbar(force_N, extension_m,
             xerr=force_unc_N, yerr=extension_unc_m,
             fmt='o', capsize=3, label='Data')
ax1.plot(x_fit, y_fit, '-', label='Linear fit')
ax1.set_ylabel('Extension (m)')
ax1.set_title('Spring extension with fit and residuals')
ax1.legend()
ax1.grid(alpha=0.3)

# Bottom panel: residuals
ax2.axhline(0, color='black', linewidth=1)
ax2.errorbar(force_N, residuals, yerr=extension_unc_m,
             fmt='o', capsize=3)
ax2.set_xlabel('Force (N)')
ax2.set_ylabel('Residual')
ax2.grid(alpha=0.3)

plt.tight_layout()
plt.show()

This is a simple and reusable pattern for residual plots Python. In lab analysis, the residual panel often tells you more than the fit line does. A fit can look visually reasonable in the top panel while still showing clear curvature or bias in the residuals.

7. Interpret the residuals before claiming success

Residuals should usually look randomly scattered around zero, without an obvious shape. Here is a useful checklist:

  • If residuals curve upward or downward, the model may be missing nonlinearity.
  • If residual spread grows with x, uncertainty may be heteroscedastic.
  • If one point dominates, check for transcription or calibration issues.
  • If all residuals sit above or below zero in one region, there may be a systematic effect.

This is the point where data analysis becomes physics again. A pattern in residuals is not just a software issue. It may indicate a real effect, a broken assumption, or an instrument limitation.

8. Add fit parameters to the figure or caption carefully

Readers should not have to search through the body text to find the key result. You can add fit values directly to a legend or annotation, but keep formatting restrained.

label_text = f'Fit: y = ({slope:.3f}±{slope_unc:.3f})x + ({intercept:.3f}±{intercept_unc:.3f})'

plt.figure(figsize=(6,4))
plt.errorbar(force_N, extension_m, yerr=extension_unc_m, fmt='o', capsize=3, label='Data')
plt.plot(x_fit, y_fit, '-', label=label_text)
plt.xlabel('Force (N)')
plt.ylabel('Extension (m)')
plt.legend()
plt.tight_layout()
plt.show()

In formal lab reports, a caption may be a better place for full detail. The plot itself should stay readable when printed or embedded in a PDF.

Tools and handoffs

The most useful workflow is one that survives beyond a single assignment. That means choosing tools and file habits that make handoff easy between raw measurement, analysis, and final reporting.

Core Python stack

  • NumPy for arrays and numerical work
  • Matplotlib for publication-style figures
  • SciPy for fitting and optimization
  • Pandas if your data start in CSV or spreadsheet form

You do not need an elaborate setup to produce strong figures. A simple Jupyter notebook or a plain Python script is enough if it is organized well.

A clean lab analysis folder often includes:

  • data_raw/ for untouched original measurements
  • data_processed/ for cleaned versions if needed
  • analysis.ipynb or analysis.py for the workflow
  • figures/ for exported PNG, PDF, or SVG files
  • README.md with a short note on variables, units, and assumptions

This structure makes it easier to revisit a project later, share it with a lab partner, or update it if the experiment changes.

Export choices that work well for reports

For most students, PNG is fine for slides and quick documents, while PDF or SVG is often better for sharp printed output. Matplotlib makes export straightforward:

plt.savefig('figures/spring_fit.png', dpi=300, bbox_inches='tight')

Use a high enough resolution for raster output, and keep filenames descriptive. A file called figure3_final_final2.png is not a workflow; it is a warning sign.

How this connects to broader study support

Plotting is not isolated from the rest of physics learning. Many students discover during analysis that they understand the coding step better than the model itself. When that happens, go back to the concept. If your graph comes from energy measurements, Work, Energy, and Power Explained: Formulas, Units, and Common Exam Traps may help tighten the interpretation. If you are looking for more learning tools to support simulations, code practice, or lab preparation, see Best Physics YouTube Channels, Simulations, and Free Learning Tools. And if you need stronger theory references behind your experiments, Best Physics Textbooks by Subject and Level is a useful place to start.

Quality checks

Before you submit a report or trust a plotted result, run through a short set of checks. These are simple, but they catch a surprising number of weak figures.

1. Axis and units check

  • Are both axes labeled with physical quantities and units?
  • Are prefixes and symbols consistent, such as mm versus m or mA versus A?
  • Are you plotting the intended independent variable on x?

2. Uncertainty check

  • Did you include error bars when uncertainty matters to the claim?
  • Are the uncertainties measured, estimated, or instrument-based?
  • If you left out x-uncertainty, is that a reasonable approximation?

3. Model check

  • Is the fitted function physically motivated?
  • Did you explain whether the intercept was fixed or free?
  • Are the fitted parameters in sensible ranges and units?

4. Residual check

  • Do residuals scatter randomly around zero?
  • Is there visible structure suggesting a missing effect?
  • Are outliers identified and treated transparently rather than hidden?

5. Presentation check

  • Are fonts readable at report size?
  • Is the legend short and useful?
  • Does the figure work in grayscale if printed?

A common trap is to over-style a weak figure. Scientific plotting is not a design contest. Clear labels, honest uncertainties, and sensible model comparisons matter more than decorative themes.

Another good habit is to compare your figure against your own physical expectation. If the fit implies an impossible sign, unrealistic magnitude, or inconsistent unit, do not assume the optimizer is right. Trace the issue back through the data, units, and model definition.

When to revisit

This workflow is worth revisiting whenever the tools change, your experiment becomes more complex, or your standards for analysis improve. A plotting setup that feels complete for a first-year mechanics lab may need revision for upper-level optics, electronics, or research data.

In practice, update your approach when:

  • You move from manual data entry to CSV or instrument exports.
  • You begin fitting nonlinear models instead of straight lines.
  • You need reproducible notebooks for collaboration or assessment.
  • You start handling larger uncertainties, correlated errors, or calibration curves.
  • Matplotlib, SciPy, or your notebook environment changes enough to affect syntax or defaults.

A practical next step is to save a personal template today. Build one notebook with these sections already in place: imports, data loading, uncertainty definitions, model function, fit, main figure, residuals, and export. Then duplicate that notebook for each new lab rather than starting from scratch. Over time, your template becomes a small but valuable piece of lab infrastructure.

If you want to deepen the scientific side of this workflow, try applying it to one new experiment in each major topic area: a spring or pendulum in mechanics, an I-V curve in electromagnetism, a cooling curve in thermodynamics, or image distance measurements in optics. The plotting code changes only slightly; the interpretation is where your physics understanding grows.

And if you later use the same habits while reading journal figures or paper supplements, that is a good sign. Careful plotting and residual analysis are part of a larger skill set: learning to inspect evidence, not just present it. For readers moving toward paper reading and research workflows, How to Read a Physics Research Paper Without Getting Lost is a useful companion.

The simplest action plan is this:

  1. Create a reusable Python plotting template.
  2. Always plot raw data with units and uncertainties first.
  3. Fit a model derived from the physics.
  4. Add residuals before making conclusions.
  5. Export clean figures and keep your analysis reproducible.

Do that consistently, and your graphs will start doing what physics graphs are supposed to do: help you think, not just decorate a report.

Related Topics

#python#data visualization#lab analysis#matplotlib#curve fitting#physics tutorial
P

Physics Direct Editorial

Senior SEO Editor

Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.

2026-06-09T23:23:01.788Z