Friday, September 25, 2026

Range, IQR, and Absolute Deviations

Before reaching variance, it is worth exploring measures that are often simpler and sometimes more appropriate.


1. Range

The range is

[
R=x_{\max}-x_{\min}.
]

For

[
2,;3,;4,;5,;6
]

the range is

[
6-2=4.
]

Its greatest advantage is interpretability.

Its greatest weakness is equally obvious.

Only two observations determine it.

A dataset containing one million observations has its range determined entirely by its minimum and maximum.


2. Interquartile range

The interquartile range is

[
IQR=Q_3-Q_1.
]

It describes the width occupied by the middle half of the observations.

Because observations below (Q_1) and above (Q_3) do not directly affect the endpoints, it is much less sensitive to extremes than the range or standard deviation.

That makes the IQR especially useful for skewed distributions.

It is also the machinery behind the familiar boxplot.


3. Mean absolute deviation

Instead of squaring deviations, why not simply take their absolute values?

Around the mean:

\frac1n
\sum_{i=1}^{n}|x_i-\bar x|.
]

Or around the median:

\frac1n
\sum_{i=1}^{n}|x_i-\tilde x|.
]

These should not be confused with the median absolute deviation, which we will encounter later.

Absolute deviations have a useful property: extreme observations grow linearly rather than quadratically in influence.

Consider deviations of 2 and 20.

Under absolute loss:

[
2 \rightarrow 2,\qquad 20\rightarrow20.
]

Under squared loss:

[
2\rightarrow4,\qquad20\rightarrow400.
]

Squaring turns the second observation into a statistical megaphone.


4. Python comparison

import numpy as np
import pandas as pd

x = np.array([10, 11, 12, 13, 14, 15, 50])

mean = np.mean(x)
median = np.median(x)

results = {
    "Range": np.ptp(x),
    "IQR": np.percentile(x, 75)
           - np.percentile(x, 25),
    "Mean abs dev about mean":
        np.mean(np.abs(x - mean)),
    "Mean abs dev about median":
        np.mean(np.abs(x - median)),
    "SD": np.std(x, ddof=1)
}

print(pd.Series(results))

Now progressively increase the outlier.

outliers = np.arange(15, 101, 5)

rows = []

for o in outliers:
    z = np.array([10, 11, 12, 13, 14, 15, o])

    rows.append({
        "outlier": o,
        "range": np.ptp(z),
        "IQR": np.percentile(z, 75)
               - np.percentile(z, 25),
        "SD": np.std(z, ddof=1),
        "AAD": np.mean(
            np.abs(z - np.mean(z))
        )
    })

df = pd.DataFrame(rows)
print(df)

Plot the trajectories:

import matplotlib.pyplot as plt

plt.plot(df["outlier"], df["range"],
         label="Range")
plt.plot(df["outlier"], df["SD"],
         label="SD")
plt.plot(df["outlier"], df["IQR"],
         label="IQR")
plt.plot(df["outlier"], df["AAD"],
         label="Absolute deviation")

plt.xlabel("Extreme observation")
plt.ylabel("Dispersion")
plt.legend()
plt.show()

This plot is wonderfully revealing.

Range grows directly with the extreme observation.

SD grows rapidly.

Absolute deviation responds more gently.

IQR barely notices.


5. R version

x <- c(10, 11, 12, 13, 14, 15, 50)

aad_mean <- mean(abs(x - mean(x)))
aad_median <- mean(abs(x - median(x)))

c(
  Range = diff(range(x)),
  IQR = IQR(x),
  AAD_mean = aad_mean,
  AAD_median = aad_median,
  SD = sd(x)
)

Simulation:

outliers <- seq(15, 100, by = 5)

result <- data.frame(
  outlier = outliers,
  range = NA,
  IQR = NA,
  SD = NA,
  AAD = NA
)

for (i in seq_along(outliers)) {
  z <- c(10, 11, 12, 13, 14, 15,
         outliers[i])

  result$range[i] <- diff(range(z))
  result$IQR[i] <- IQR(z)
  result$SD[i] <- sd(z)
  result$AAD[i] <- mean(abs(z - mean(z)))
}

matplot(
  result$outlier,
  result[, c("range", "IQR", "SD", "AAD")],
  type = "l",
  lty = 1,
  xlab = "Extreme observation",
  ylab = "Dispersion"
)

legend(
  "topleft",
  legend = c("Range", "IQR", "SD", "AAD"),
  lty = 1,
  col = 1:4
)

6. Advantages and limitations

MeasureStrengthMain limitation
RangeExtremely intuitiveDetermined by two observations
IQRRobust and easy to interpretIgnores much tail information
Mean absolute deviationSame units and moderate tail sensitivityLess algebraically convenient
SDRich mathematical theoryHighly sensitive to tails

A useful habit is to report more than one.

For skewed or contamination-prone data, reporting

[
\text{median + IQR}
]

may be far more informative than

[
\text{mean + SD}.
]

No comments: