StatisticsSciPy 1.18 · pandas 3.0 · statsmodels 0.15 · Python 3.12 or 3.13
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
57 small wins to finish your pathNext lesson →

Range, MAD and coefficient of variation

Range, MAD and the coefficient of variation are measures of spread: the range is the largest value minus the smallest, the MAD is a typical absolute distance from the centre, and the coefficient of variation is the standard deviation as a percentage of the mean.

Last updated: 07 Oct, 2026 · SciPy 1.18

Variance and standard deviation covers the main measures of spread, which answer one question: how far values sit from the mean, on average, after squaring. These three answer others: how wide the data is, how far a typical value is without squaring, and whether one group varies more than another when their sizes differ.

Finding the range

The range is the maximum minus the minimum. An interview use case in the statistics Q&A notes gives the recovery times, in days, of three patient groups after the same surgery:

GroupRecovery times (days)SortedRange
A5, 6, 4, 5, 7, 5, 64, 5, 5, 5, 6, 6, 77 − 4 = 3
B7, 8, 7, 9, 8, 7, 97, 7, 7, 8, 8, 9, 99 − 7 = 2
C5, 7, 6, 5, 6, 6, 55, 5, 5, 6, 6, 6, 77 − 5 = 2
Range

Groups B and C have the smallest range. The range uses only two values, so a single outlier sets it: the ten values 1, 1, 2, 2, 3, 3, 4, 5, 5, 6 from Mean, median and mode have a range of 5, and adding 100 makes it 99.

Measuring the mean absolute deviation

The mean absolute deviation is the average distance of the values from their mean, with each distance taken as an absolute value instead of being squared.

Mean absolute deviation

For 1, 2, 2, 3, 4, 5 the distances from the mean 2.83 are 1.83, 0.83, 0.83, 0.17, 1.17 and 2.17. They add up to 7, so the mean absolute deviation is 7/6 = 1.17. The population standard deviation of the same values is 1.34. The population standard deviation is never smaller than the mean absolute deviation, because squaring gives the larger distances more weight.

Measuring the median absolute deviation

The median absolute deviation is the median of the distances from the median.

Median absolute deviation

For 1, 2, 2, 3, 4, 5 the median is 2.5, the distances from it are 1.5, 0.5, 0.5, 0.5, 1.5 and 2.5, and their median is 1. Built from medians, it resists outliers the way the median does. On the ten values from the mean lesson it goes from 1.5 to 2.0 when 100 is added, while the sample standard deviation jumps from 1.75 to 29.23.

Both measures are called MAD, so name the one you mean. scipy.stats.median_abs_deviation computes the median one. With scale='normal' it is multiplied by 1.4826, which makes it an estimate of σ for normal data; Outlier detection with IQR and z-score uses it to flag outliers.

Comparing spread with the coefficient of variation

The coefficient of variation (CV) is the standard deviation divided by the mean, usually written as a percentage. It measures spread relative to the size of the values, so groups with different means can be compared.

Coefficient of variation

The Q&A notes' sales use case has the monthly sales, in thousands, of three regions: North 12, 15, 14, 13, 17, 19, 20; South 22, 21, 20, 23, 25, 26, 28; West 32, 30, 31, 29, 30, 33, 35. The question is which region sells most consistently. West has the highest mean, 31.43, and the lowest sample standard deviation, 2.07, so its CV of 6.6% is the lowest as well. West is the most consistent by either measure.

The patient groups show where the CV changes the answer. Group C has a smaller standard deviation than group B, 0.76 days against 0.90. Group B's recovery times are longer, though, and relative to its mean B varies less: a CV of 11.5% against 13.2%.

The CV needs values on a ratio scale, with a true zero and all values positive: lengths, sales, times. On an interval scale such as temperature in °C the mean can be close to zero, and the CV becomes meaningless (Measurement scales).

Computing range, MAD and CV in Python

The range and the mean absolute deviation with NumPy

np.ptp (peak to peak) returns max − min. NumPy has no mean absolute deviation function, so it is one line.

python
import numpy as np

np.ptp(x)                          # range: max - min
np.mean(np.abs(x - np.mean(x)))    # mean absolute deviation

The median absolute deviation and the CV with SciPy

stats.variation divides by the ÷N standard deviation by default (ddof=0); pass ddof=1 for the sample one.

python
from scipy import stats

stats.median_abs_deviation(x)      # median of |x - median|
stats.variation(x, ddof=1)         # sample sd / mean, as a fraction

Measuring the spread of the three patient groups

ExampleThe Q&A notes' recovery times, run on SciPy 1.18
import numpy as np
from scipy import stats

groups = {"A": [5, 6, 4, 5, 7, 5, 6],
          "B": [7, 8, 7, 9, 8, 7, 9],
          "C": [5, 7, 6, 5, 6, 6, 5]}

for name, days in groups.items():
    days = np.array(days)
    print(f"{name}: range={np.ptp(days)}"
          f"  mean abs dev={np.mean(np.abs(days - days.mean())):.3f}"
          f"  median abs dev={stats.median_abs_deviation(days)}"
          f"  mean={days.mean():.3f}  sd={days.std(ddof=1):.3f}"
          f"  cv={stats.variation(days, ddof=1):.1%}")

What the patient groups show

  • Ranges 3, 2 and 2: A is the widest, B and C tie.
  • The mean absolute deviation puts C lowest at 0.612, then B at 0.735 and A at 0.776.
  • The median absolute deviation is 1.0 for all three: the middle distances in seven whole-day values come out the same.
  • The CV ranks B lowest at 11.5%, ahead of C at 13.2%, although C's standard deviation (0.756) is smaller than B's (0.900).

Testing each measure against an outlier

ExampleThe video's ten values, run on SciPy 1.18
import numpy as np
from scipy import stats

six = np.array([1, 2, 2, 3, 4, 5])
print("1, 2, 2, 3, 4, 5:  mean abs dev", round(np.mean(np.abs(six - six.mean())), 3),
      " median abs dev", stats.median_abs_deviation(six), " population sd", round(six.std(), 3))

base = np.array([1, 1, 2, 2, 3, 3, 4, 5, 5, 6])
with_100 = np.append(base, 100)

for name, data in [("10 values", base), ("with 100", with_100)]:
    print(f"{name:9}  range={np.ptp(data):5}  sd={data.std(ddof=1):6.2f}"
          f"  mean abs dev={np.mean(np.abs(data - data.mean())):5.2f}"
          f"  median abs dev={stats.median_abs_deviation(data)}")

Which measures the outlier moves

  • For 1, 2, 2, 3, 4, 5 the mean absolute deviation is 1.167, below the population standard deviation 1.344, and the median absolute deviation is 1.0.
  • The range goes from 5 to 99: it follows the outlier all the way.
  • The standard deviation goes from 1.75 to 29.23, and the mean absolute deviation from 1.44 to 16.00.
  • The median absolute deviation goes from 1.5 to 2.0, the only measure that stays near the bulk of the data.

Comparing the three sales regions

ExampleThe Q&A notes' sales data, run on pandas 3.0
import pandas as pd

sales = pd.DataFrame({"North": [12, 15, 14, 13, 17, 19, 20],
                      "South": [22, 21, 20, 23, 25, 26, 28],
                      "West":  [32, 30, 31, 29, 30, 33, 35]})

summary = pd.DataFrame({"mean": sales.mean(),
                        "sd": sales.std(),
                        "cv %": sales.std() / sales.mean() * 100})
print(summary.round(3))

What the sales summary shows

  • The means are 15.714, 23.571 and 31.429 thousand for North, South and West.
  • The sample standard deviations are 3.039, 2.878 and 2.070: pandas divides by n − 1.
  • The CVs are 19.3%, 12.2% and 6.6%. West has the lowest standard deviation and the lowest CV, so it is the most consistent region.

Range vs IQR vs standard deviation vs MAD vs CV

MeasureBuilt fromUnitsOne outlier
Rangemax and minthe data'ssets it
IQRQ3 and Q1the data'sbarely moves it
Standard deviationevery squared deviationthe data'sinflates it
Mean absolute deviationevery absolute deviationthe data'sinflates it, less than the SD
Median absolute deviationthe median of the distancesthe data'sbarely moves it
Coefficient of variationSD ÷ meannone (a percentage)inflates it

The interquartile range, the spread of the middle half of the data, has its own lesson: Quartiles and the interquartile range.

Where you use the range, MAD and CV

  • Quality checks: the range of a small batch of measurements is a quick spread check on a factory line.
  • Resistant scaling: the median and the median absolute deviation replace the mean and the standard deviation when a column has outliers.
  • Comparing variability across scales: the CV compares the spread of prices, weights or lab results whose means differ a lot.
Watch out. "MAD" means the mean absolute deviation in some books and the median absolute deviation in others, and the two can be far apart on data with outliers (16.0 and 2.0 above). pandas removed its Series.mad() method in version 2.0; compute the one you mean explicitly.
Try it yourself
  • Add a recovery time of 30 days to group A and see which of its measures change the most.
  • Pass scale='normal' to stats.median_abs_deviation for the ten values and compare it with their standard deviation.
  • Add 100 to every sales figure of the North region and check that the standard deviation stays 3.039 while the CV drops.

This is what real progress feels like.