Перцентили в Python с NumPy
Как вычислять перцентили в Python с NumPy. Квартили, IQR, обнаружение выбросов, методы интерполяции и применение в ML.
Перцентиль показывает значение, ниже которого находится заданный процент наблюдений в наборе данных. Если студент набрал баллы на уровне 80-го перцентиля, значит 80 % результатов ниже его. Перцентили — краеугольный камень разведочного анализа данных; они применяются в машинном обучении для обнаружения выбросов, обрезки признаков и оценки производительности модели по подгруппам.
В этой главе рассматриваются:
- Что такое перцентиль и как он вычисляется
- Как использовать
numpy.percentile()иnumpy.quantile() - Квартили и пятичисловая сводка
- Межквартильный размах (IQR) для обнаружения выбросов
- Методы интерполяции и когда они важны
- Нахождение ранга перцентиля конкретного значения с помощью SciPy
- Практические сценарии ML: винсоризация и обрезка данных
Что такое перцентиль?
Перцентиль делит отсортированный набор данных на 100 равных частей. P-й перцентиль — это значение, при котором или ниже которого находится P процентов данных.
| Перцентиль | Общепринятое название | Значение |
|---|---|---|
| 25-й | Q1 (первый квартиль) | 25 % значений ниже этой точки |
| 50-й | Q2 / Медиана | Половина значений ниже этой точки |
| 75-й | Q3 (третий квартиль) | 75 % значений ниже этой точки |
| 90-й | — | 90 % значений ниже этой точки |
Перцентили тесно связаны со средним, медианой и модой: 50-й перцентиль — это в точности медиана.
Перцентиль и квантиль
Эти термины часто используются как синонимы. Квантиль выражает ту же идею, но через дробь от 0 до 1, а не через процент от 0 до 100:
- 25-й перцентиль = квантиль 0,25
- 75-й перцентиль = квантиль 0,75
NumPy предоставляет как np.percentile(), так и np.quantile(). Они ведут себя одинаково; выбирайте тот вариант, который делает ваш код понятнее.
Вычисление перцентиля с помощью NumPy
numpy.percentile(a, q) принимает массив a и значение перцентиля q (0–100) и возвращает интерполированное значение в этой позиции.
Вычисление 75-го перцентиля набора данных
import numpy as np
data = [10, 20, 30, 40, 50]
result = np.percentile(data, 75)
print(result) # Output: 40.0Передайте список в q, чтобы вычислить несколько перцентилей за один вызов — NumPy вернёт их в виде массива:
Вычисление 25-го, 50-го и 75-го перцентилей одновременно
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.0Использование np.quantile()
np.quantile() принимает дробь (0,0–1,0) вместо процента:
Использование np.quantile() с дробным аргументом
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.0Квартили и пятичисловая сводка
Три квартиля (Q1, Q2, Q3) вместе с минимумом и максимумом образуют пятичисловую сводку — компактный снимок разброса любого набора данных. Все пять значений можно вычислить за один вызов:
Вычисление пятичисловой сводки
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.0Пятичисловая сводка лежит в основе ящика с усами (box plot), который можно построить с помощью Matplotlib (см. Гистограммы Matplotlib для ознакомления со смежными техниками визуализации).
Межквартильный размах и обнаружение выбросов
Межквартильный размах (IQR) — это расстояние между Q1 и Q3:
IQR = Q3 − Q1Он описывает разброс средних 50 % данных. Поскольку IQR игнорирует верхний и нижний кварталы, он устойчив к экстремальным значениям — что делает его стандартным инструментом для правило-основанного обнаружения выбросов.
Правило заборов Тьюки помечает любое значение, выходящее за пределы 1,5 × IQR от любого из квартилей, как потенциальный выброс:
- Нижняя граница: Q1 − 1,5 × IQR
- Верхняя граница: Q3 + 1,5 × IQR
Обнаружение выбросов методом IQR
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]Значение 80 значительно превышает верхнюю границу 19,5, поэтому оно помечается как выброс. Метод IQR широко используется, поскольку не требует никаких предположений о лежащем в основе распределении данных.
Методы интерполяции
Когда запрашиваемый перцентиль попадает между двумя точками данных, NumPy применяет интерполяцию. Параметр method (добавлен в NumPy 1.22 на замену устаревшему параметру interpolation) определяет способ вычисления.
Сравнение методов интерполяции на 35-м перцентиле
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| Метод | Описание | Когда использовать |
|---|---|---|
linear | Взвешенное среднее двух соседних значений | По умолчанию; наиболее статистически корректный |
lower | Возвращает меньшее из двух соседних значений | Когда необходимы целочисленные индексы |
higher | Возвращает большее из двух соседних значений | Консервативные верхние границы |
midpoint | Среднее из меньшего и большего | Простые отчёты о среднем диапазоне |
nearest | Ближайшая реальная точка данных | Когда результат должен быть реальным наблюдением |
Для большинства задач анализа данных и ML метод linear по умолчанию является правильным выбором.
Нахождение перцентильного ранга значения
Иногда возникает обратная задача: для заданного значения определить его перцентиль. Используйте scipy.stats.percentileofscore():
Определение перцентильного ранга конкретного значения
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.0Значение 30 находится на 60-м перцентиле — это означает, что 60 % значений в data равны 30 или меньше его.
Практическое применение в ML: винсоризация
Винсоризация (также называемая обрезкой) ограничивает экстремальные значения на выбранной перцентильной границе вместо их удаления. Это сохраняет количество строк, одновременно ограничивая влияние выбросов на обучение модели.
Обрезка выбросов до диапазона 10-го–90-го перцентилей
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]Оба экстремальных значения — 10 (ниже нижней перцентильной границы 20) и 200 (выше верхней перцентильной границы 100) — ограничиваются граничным значением. Винсоризация — распространённый шаг предобработки перед обучением линейных моделей или нейронных сетей, особенно на финансовых данных или данных с датчиков, где экстремальные показания встречаются часто.
Подробнее о подходах к масштабированию признаков, дополняющих обрезку на основе перцентилей, читайте в главе Масштабирование.
Краткий справочник
| Задача | Функция | Пример |
|---|---|---|
| Один перцентиль | np.percentile(data, q) | np.percentile(data, 75) |
| Несколько перцентилей | np.percentile(data, [q1, q2, …]) | np.percentile(data, [25, 50, 75]) |
| Квантиль (шкала 0–1) | np.quantile(data, q) | np.quantile(data, 0.75) |
| Пятичисловая сводка | np.percentile(data, [0,25,50,75,100]) | — |
| IQR | Q3 − Q1 | np.percentile(data, 75) - np.percentile(data, 25) |
| Перцентильный ранг значения | scipy.stats.percentileofscore(data, v) | — |
| Обрезка до границ перцентиля | np.clip(data, lower, upper) | np.clip(data, p10, p90) |
Связанные темы
- Среднее, медиана и мода — меры центральной тенденции; 50-й перцентиль — это медиана.
- Стандартное отклонение — измерение разброса вокруг среднего; дополняет IQR.
- Распределение данных — понимание асимметрии и выбросов перед выбором сводных статистик.
- Масштабирование — техники масштабирования признаков для предобработки в ML.
- Руководство по NumPy — базовые навыки NumPy, используемые на протяжении всей этой главы.
Заключение
Перцентили ранжируют значения внутри набора данных и раскрывают его форму так, как не могут ни среднее, ни стандартное отклонение. С помощью numpy.percentile() вычисление Q1, Q2, Q3 и IQR занимает всего одну строку Python. В машинном обучении перцентили применяются на каждом этапе: в разведочном анализе данных для выявления выбросов, в предобработке для винсоризации экстремальных значений и при оценке для понимания точности модели на всём распределении данных. Владение ими даёт надёжный инструмент без предположений о распределении, который работает с любым набором данных.