Percentiles in Python with NumPy
Learn how to calculate percentiles in Python using NumPy. Covers quartiles, IQR outlier detection, interpolation methods, and practical ML use cases.
A percentile tells you the value below which a given percentage of observations in your dataset fall. If a student scores at the 80th percentile on an exam, 80 % of scores are below theirs. Percentiles are a cornerstone of exploratory data analysis and are used in machine learning for outlier detection, feature clipping, and reporting model performance across subgroups.
This chapter covers:
- What a percentile is and how it is calculated
- How to use
numpy.percentile()andnumpy.quantile() - Quartiles and the five-number summary
- The interquartile range (IQR) for outlier detection
- Interpolation methods and when they matter
- Finding the percentile rank of a specific value with SciPy
- Practical ML use cases: winsorization and data clipping
What Is a Percentile?
A percentile divides a sorted dataset into 100 equal parts. The P-th percentile is the value at or below which P percent of the data falls.
| Percentile | Common name | Meaning |
|---|---|---|
| 25th | Q1 (first quartile) | 25 % of values are below this point |
| 50th | Q2 / Median | Half the values are below this point |
| 75th | Q3 (third quartile) | 75 % of values are below this point |
| 90th | — | 90 % of values are below this point |
Percentiles are closely related to mean, median, and mode: the 50th percentile is exactly the median.
Percentile vs. quantile
The terms are often used interchangeably. A quantile expresses the same idea using a fraction from 0 to 1 instead of a percentage from 0 to 100:
- The 25th percentile = the 0.25 quantile
- The 75th percentile = the 0.75 quantile
NumPy provides both np.percentile() and np.quantile(). They behave identically; choose whichever makes your code clearer.
Calculating a Percentile with NumPy
numpy.percentile(a, q) takes an array-like a and a percentile q (0–100) and returns the interpolated value at that position.
Calculate the 75th percentile of a dataset
import numpy as np
data = [10, 20, 30, 40, 50]
result = np.percentile(data, 75)
print(result) # Output: 40.0Pass a list to q to calculate multiple percentiles in one call — NumPy returns them in an array:
Calculate the 25th, 50th, and 75th percentiles at once
import numpy as np
data = [10, 20, 30, 40, 50]
p25, p50, p75 = np.percentile(data, [25, 50, 75])
print(f'Q1 = {p25}') # Output: Q1 = 20.0
print(f'Q2 = {p50}') # Output: Q2 = 30.0
print(f'Q3 = {p75}') # Output: Q3 = 40.0Using np.quantile()
np.quantile() accepts a fraction (0.0–1.0) instead of a percentage:
Use np.quantile() with a fraction argument
import numpy as np
data = [10, 20, 30, 40, 50]
q1 = np.quantile(data, 0.25)
q3 = np.quantile(data, 0.75)
print(f'Q1 = {q1}') # Output: Q1 = 20.0
print(f'Q3 = {q3}') # Output: Q3 = 40.0Quartiles and the Five-Number Summary
The three quartiles (Q1, Q2, Q3) together with the minimum and maximum form the five-number summary — a compact snapshot of any dataset's spread. You can compute all five values in a single call:
Compute the five-number summary
import numpy as np
data = [10, 20, 30, 40, 50]
minimum, q1, median, q3, maximum = np.percentile(data, [0, 25, 50, 75, 100])
print(f'Min = {minimum}') # Output: Min = 10.0
print(f'Q1 = {q1}') # Output: Q1 = 20.0
print(f'Median = {median}') # Output: Median = 30.0
print(f'Q3 = {q3}') # Output: Q3 = 40.0
print(f'Max = {maximum}') # Output: Max = 50.0The five-number summary is the foundation of a box plot, which you can draw with Matplotlib (see Matplotlib Histograms for related visualization techniques).
The Interquartile Range and Outlier Detection
The interquartile range (IQR) is the distance between Q1 and Q3:
IQR = Q3 − Q1It describes the spread of the middle 50 % of the data. Because it ignores the top and bottom quarters, the IQR is robust to extreme values — making it the standard tool for rule-based outlier detection.
The Tukey fence rule flags any value more than 1.5 × IQR beyond either quartile as a potential outlier:
- Lower fence: Q1 − 1.5 × IQR
- Upper fence: Q3 + 1.5 × IQR
Detect outliers using the IQR method
import numpy as np
data = [5, 7, 8, 9, 10, 11, 12, 13, 14, 80]
q1 = np.percentile(data, 25)
q3 = np.percentile(data, 75)
iqr = q3 - q1
lower_fence = q1 - 1.5 * iqr
upper_fence = q3 + 1.5 * iqr
outliers = [x for x in data if x < lower_fence or x > upper_fence]
print(f'Q1 = {q1}, Q3 = {q3}, IQR = {iqr}')
# Output: Q1 = 8.25, Q3 = 12.75, IQR = 4.5
print(f'Lower fence = {lower_fence}, Upper fence = {upper_fence}')
# Output: Lower fence = 1.5, Upper fence = 19.5
print(f'Outliers: {outliers}')
# Output: Outliers: [80]The value 80 is well beyond the upper fence of 19.5, so it is flagged as an outlier. The IQR method is widely used because it requires no assumptions about the underlying data distribution.
Interpolation Methods
When the requested percentile falls between two data points, NumPy interpolates. The method parameter (added in NumPy 1.22, replacing the older interpolation parameter) controls how this is done.
Compare interpolation methods at the 35th percentile
import numpy as np
data = [10, 20, 30, 40, 50]
# The 35th percentile falls between 20 (index 1) and 30 (index 2)
print(np.percentile(data, 35, method='linear')) # Output: 24.0 (default)
print(np.percentile(data, 35, method='lower')) # Output: 20
print(np.percentile(data, 35, method='higher')) # Output: 30
print(np.percentile(data, 35, method='midpoint')) # Output: 25.0
print(np.percentile(data, 35, method='nearest')) # Output: 20| Method | Description | When to use |
|---|---|---|
linear | Weighted average of the two surrounding values | Default; most statistically correct |
lower | Returns the lower of the two surrounding values | Integer indices required |
higher | Returns the higher of the two surrounding values | Conservative upper bounds |
midpoint | Average of lower and higher | Simple mid-range reporting |
nearest | Nearest actual data point | When the result must be a real observation |
For most data analysis and ML work, the default linear method is the right choice.
Finding the Percentile Rank of a Value
Sometimes you have the reverse question: given a value, what percentile is it at? Use scipy.stats.percentileofscore():
Find what percentile rank a specific value occupies
from scipy import stats
data = [10, 20, 30, 40, 50]
rank = stats.percentileofscore(data, 30)
print(rank) # Output: 60.0
rank_of_25 = stats.percentileofscore(data, 25)
print(rank_of_25) # Output: 40.0A score of 30 is at the 60th percentile — meaning 60 % of the values in data are at or below 30.
Practical ML Use Case: Winsorization
Winsorization (also called clipping) caps extreme values at a chosen percentile boundary instead of removing them. This preserves the row count while limiting the influence of outliers on model training.
Clip outliers to the 10th–90th percentile range
import numpy as np
data = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200]
lower = np.percentile(data, 10)
upper = np.percentile(data, 90)
clipped = np.clip(data, lower, upper)
print(f'Lower bound (10th): {lower}') # Output: Lower bound (10th): 20.0
print(f'Upper bound (90th): {upper}') # Output: Upper bound (90th): 100.0
print('Clipped:', list(clipped))
# Output: Clipped: [20.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 100.0]Both extreme values — 10 (below the 10th percentile boundary of 20) and 200 (above the 90th percentile boundary of 100) — are capped at the fence value. Winsorization is a common preprocessing step before training linear models or neural networks, especially on financial or sensor data where extreme readings are common.
For more on feature scaling approaches that complement percentile-based clipping, see the Scale chapter.
Quick Reference
| Task | Function | Example |
|---|---|---|
| Single percentile | np.percentile(data, q) | np.percentile(data, 75) |
| Multiple percentiles | np.percentile(data, [q1, q2, …]) | np.percentile(data, [25, 50, 75]) |
| Quantile (0–1 scale) | np.quantile(data, q) | np.quantile(data, 0.75) |
| Five-number summary | np.percentile(data, [0,25,50,75,100]) | — |
| IQR | Q3 − Q1 | np.percentile(data, 75) - np.percentile(data, 25) |
| Percentile rank of a value | scipy.stats.percentileofscore(data, v) | — |
| Clip to percentile bounds | np.clip(data, lower, upper) | np.clip(data, p10, p90) |
Related Topics
- Mean, Median, and Mode — central tendency measures; the 50th percentile is the median.
- Standard Deviation — measure spread around the mean; complements the IQR.
- Data Distribution — understand skew and outliers before choosing summary statistics.
- Scale — feature scaling techniques for ML preprocessing.
- NumPy Tutorial — foundational NumPy skills used throughout this chapter.
Conclusion
Percentiles rank values within a dataset and expose its shape in ways that mean and standard deviation cannot. With numpy.percentile(), computing Q1, Q2, Q3, and IQR takes a single line of Python. In machine learning, percentiles appear at every stage: in exploratory data analysis to spot outliers, in preprocessing to winsorize extreme values, and in evaluation to understand model accuracy across the distribution of your data. Mastering them gives you a reliable, assumption-free tool that works on any dataset regardless of its distribution.