Skip to main content
Back to module
Scientific Python · Plotting Scientific Data

Log scales and fits

Reading44mLesson 2 of 3 in this module

Use log-log and semilog axes to turn power laws and exponentials into straight lines, then read the exponent or rate from a linear fit.

Why this matters

Many physical laws are not straight lines. A pendulum's period grows with the square root of its length. Radioactive activity, capacitor voltage during discharge and first-order reaction concentrations fall exponentially. Fatigue life, sound intensity and earthquake energy span many powers of ten. Plotted on ordinary axes, these curves hide their structure and squash small values into a corner. Logarithmic axes spread them out and, for the right kind of law, turn the curve into a straight line whose slope you can read off and fit.

Two shapes, two kinds of axis

Power law: y = a x^b. Take logs of both sides:

log y = log a + b log x

On log-log axes (both axes logarithmic) this is a straight line with slope b (the exponent) and intercept log a.

Exponential: y = A e^(k t) (decay when k < 0). Take the natural log of y only:

ln y = ln A + k t

On semilog axes (log y, linear t) this is a straight line with slope k.

A quick diagnostic: plot your data both ways. Whichever plot is straight tells you the type of law.

Plotting with log axes

import numpy as np
import matplotlib.pyplot as plt

L = np.array([0.10, 0.20, 0.40, 0.80, 1.60])      # pendulum length, m
T = np.array([0.636, 0.898, 1.268, 1.795, 2.538])  # period, s

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(7, 3))
ax1.plot(L, T, "o")
ax1.set_xlabel("Length (m)"); ax1.set_ylabel("Period (s)")

ax2.loglog(L, T, "o")          # same as ax.set_xscale("log"); ax.set_yscale("log")
ax2.set_xlabel("Length (m)"); ax2.set_ylabel("Period (s)")

ax.semilogy(t, y) sets only the y-axis to log. A log axis keeps the original values on its tick labels (0.1, 1, 10), which is easier to read than plotting np.log10(y) on a linear axis.

Fitting on the linearised data

To get numbers, fit a straight line to the transformed data with np.polyfit(x, y, 1), which returns [slope, intercept].

Worked example 1: pendulum power law

b, log_a = np.polyfit(np.log(L), np.log(T), 1)
a = np.exp(log_a)
print(round(b, 3), round(a, 3))     # 0.5 2.006

The exponent is 0.5, matching T = 2π sqrt(L / g). The prefactor a = 2π / sqrt(g) gives g = (2π / a)^2 ≈ 9.81 m/s².

Which log base you use does not matter for the exponent, as long as you use the same base on both axes; the prefactor must be converted back with the matching inverse (np.exp for np.log, 10** for np.log10).

Worked example 2: capacitor discharge

A capacitor discharges through a resistor: V(t) = V0 e^(-t / τ).

t = np.array([0, 1, 2, 3, 4, 5], dtype=float)       # ms
V = np.array([9.00, 5.46, 3.31, 2.01, 1.22, 0.74])  # volts

k, ln_V0 = np.polyfit(t, np.log(V), 1)
tau = -1 / k
print(round(tau, 2), round(np.exp(ln_V0), 2))       # 2.0 ms, 9.0 V

The half-life is τ ln 2 ≈ 1.39 ms: the voltage halves every 1.39 ms, whatever its starting value. That constant-ratio behaviour is the signature of an exponential.

Caveats of fitting in log space

Linearised fits are quick, robust and usually good enough. Know their limits:

  • Zero and negative values have no logarithm. Remove them (and say so) or use a model that handles an offset, such as y = A e^(kt) + C, fitted with non-linear least squares (next module).
  • Noise is reshaped. A least-squares fit to log y weights small values more heavily, because a fixed error is a bigger fraction of a small number. If your noise is constant in absolute terms, the log fit gives slightly biased parameters; a non-linear fit on the original scale is more correct.
  • Offsets break straightness. A background level (dark current, baseline absorbance) makes a semilog plot curve at long times. Subtract the background first if you know it.
  • Range matters. A power law that is straight over two decades may bend outside them. Do not extrapolate beyond the data.

Common mistakes

  • Fitting y against log x when you meant log-log, or forgetting to transform y for an exponential.
  • Reporting log a as a. Convert the intercept back with exp or 10**.
  • Mixing bases: fitting with np.log10 and converting back with np.exp.
  • Taking the log of data containing zeros, which gives -inf and a RuntimeWarning, then a fit full of nan.
  • Reading slopes off a log axis by eye using tick distances instead of fitting the transformed values.

Quick recap

  • Power law y = a x^b: straight on log-log; slope = b.
  • Exponential y = A e^(kt): straight on semilog-y; slope = k, time constant τ = -1/k.
  • ax.loglog, ax.semilogy for plotting; np.polyfit on the logged data for numbers.
  • Watch for zeros, offsets and noise reshaping; use a non-linear fit when they matter.

Try it

In the Do tab you will write power-law and exponential fitting functions and test them on noisy, seeded data.

Also in this lesson: