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:
| Group | Recovery times (days) | Sorted | Range |
|---|---|---|---|
| A | 5, 6, 4, 5, 7, 5, 6 | 4, 5, 5, 5, 6, 6, 7 | 7 − 4 = 3 |
| B | 7, 8, 7, 9, 8, 7, 9 | 7, 7, 7, 8, 8, 9, 9 | 9 − 7 = 2 |
| C | 5, 7, 6, 5, 6, 6, 5 | 5, 5, 5, 6, 6, 6, 7 | 7 − 5 = 2 |
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.
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.
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.
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.
import numpy as np
np.ptp(x) # range: max - min
np.mean(np.abs(x - np.mean(x))) # mean absolute deviationThe 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.
from scipy import stats
stats.median_abs_deviation(x) # median of |x - median|
stats.variation(x, ddof=1) # sample sd / mean, as a fractionMeasuring the spread of the three patient groups
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%}")A: range=3 mean abs dev=0.776 median abs dev=1.0 mean=5.429 sd=0.976 cv=18.0% B: range=2 mean abs dev=0.735 median abs dev=1.0 mean=7.857 sd=0.900 cv=11.5% C: range=2 mean abs dev=0.612 median abs dev=1.0 mean=5.714 sd=0.756 cv=13.2%
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
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)}")1, 2, 2, 3, 4, 5: mean abs dev 1.167 median abs dev 1.0 population sd 1.344 10 values range= 5 sd= 1.75 mean abs dev= 1.44 median abs dev=1.5 with 100 range= 99 sd= 29.23 mean abs dev=16.00 median abs dev=2.0
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
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))mean sd cv % North 15.714 3.039 19.342 South 23.571 2.878 12.212 West 31.429 2.070 6.587
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
| Measure | Built from | Units | One outlier |
|---|---|---|---|
| Range | max and min | the data's | sets it |
| IQR | Q3 and Q1 | the data's | barely moves it |
| Standard deviation | every squared deviation | the data's | inflates it |
| Mean absolute deviation | every absolute deviation | the data's | inflates it, less than the SD |
| Median absolute deviation | the median of the distances | the data's | barely moves it |
| Coefficient of variation | SD ÷ mean | none (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.
Series.mad() method in version 2.0; compute the one you mean explicitly.Related
- Previous: Sample variance and why n − 1
- Next: Skewness and kurtosis
- See also: Quartiles and the interquartile range
- Add a recovery time of 30 days to group A and see which of its measures change the most.
- Pass
scale='normal'tostats.median_abs_deviationfor 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.