В мире анализа данных и научных вычислений NumPy является краеугольным камнем, предоставляя мощные инструменты для работы с многомерными массивами. Среди множества его функций, numpy.percentile и numpy.quantile занимают особое место, являясь незаменимыми для статистического анализа, обнаружения выбросов и понимания распределения данных. Они позволяют быстро получить ключевые статистические показатели, такие как медиана (50-й перцентиль) или квартили, что критически важно для принятия обоснованных решений.
Несмотря на их широкое применение, внутренняя механика этих функций часто остается за кадром для большинства пользователей. Как именно NumPy вычисляет перцентили? Какие алгоритмы лежат в основе? Как обрабатываются различные методы интерполяции и граничные случаи? Понимание этих деталей не только углубляет знания о библиотеке, но и позволяет более эффективно использовать ее, а также способствует разработке более надежного и производительного кода.
В этой статье мы совершим глубокое погружение в реализацию numpy.percentile и numpy.quantile. Мы рассмотрим их математические основы, изучим внутренние алгоритмы сортировки и интерполяции, проанализируем структуру исходного кода и обсудим ключевые оптимизации. Цель — демистифицировать эти мощные инструменты, предоставив разработчикам, исследователям и студентам всестороннее понимание того, как они работают «под капотом», и, возможно, вдохновить на собственный вклад в развитие NumPy.
Основы numpy.percentile и numpy.quantile
numpy.percentile и numpy.quantile являются краеугольными камнями статистического анализа в экосистеме NumPy, предоставляя эффективные средства для определения значений, ниже которых находится заданный процент или доля данных в массиве. Эти функции тесно связаны: percentile принимает значения q в диапазоне [0, 100], тогда как quantile оперирует долями в диапазоне [0, 1].
Обзор функций и их основные параметры
Основные параметры, общие для обеих функций, включают:
-
q: Процент или доля (или последовательность процентов/долей), которые необходимо вычислить. -
axis: Ось или оси, вдоль которых вычисляется перцентиль/квантиль. По умолчанию вычисление производится по всему сглаженному массиву. -
out: Альтернативный выходной массив для размещения результата. -
overwrite_input: ЕслиTrue, входной массив может быть изменен (отсортирован) для оптимизации. -
interpolation: Метод интерполяции, используемый при вычислении перцентиля, когда требуемое значение находится между двумя элементами. Это критически важный параметр, который мы подробно рассмотрим далее. -
keepdims: ЕслиTrue, результирующий массив будет иметь те же измерения, что и входной, с размером 1 по осям, вдоль которых производилось вычисление.
Математические основы перцентилей и квантилей
Математически, p-й перцентиль (или q-й квантиль, где q = p/100) — это значение, ниже которого находится p процентов наблюдений в наборе данных. Например, 50-й перцентиль (или 0.5 квантиль) соответствует медиане, разделяющей данные на две равные половины. Вычисление перцентилей требует упорядочивания данных, после чего определяется позиция и, при необходимости, интерполируется значение. Различные методы интерполяции позволяют обрабатывать ситуации, когда позиция перцентиля не является целым числом, что является ключевым аспектом их реализации.
Обзор функций и их основные параметры
Функции numpy.percentile и numpy.quantile являются основными инструментами в NumPy для вычисления перцентилей и квантилей соответственно. Они предоставляют гибкий интерфейс для статистического анализа данных, позволяя пользователям точно определять пороговые значения в распределениях. Несмотря на схожесть, numpy.percentile принимает значения q в диапазоне от 0 до 100, тогда как numpy.quantile работает с долями от 0 до 1, что делает последнюю более универсальной для статистических расчетов.
Рассмотрим ключевые параметры, которые определяют поведение этих функций:
-
q(илиpercentile): Это основной параметр, задающий перцентиль(и) или квантиль(и) для вычисления. Он может быть скалярным значением (например,50для медианы) или последовательностью (например,[25, 50, 75]для квартилей). Дляnumpy.percentileзначения должны быть в диапазоне[0, 100], а дляnumpy.quantile—[0, 1]. -
axis: Определяет ось или оси, вдоль которых будут вычисляться перцентили. Еслиaxis=None(по умолчанию), перцентили вычисляются для всего сглаженного массива. Если указано целое число, вычисление происходит вдоль этой оси. Для многомерных массивов можно передать кортеж осей. -
out: Позволяет указать альтернативный выходной массив, в который будут помещены результаты. Это может быть полезно для оптимизации памяти, избегая создания нового массива. -
overwrite_input: Логический флаг, указывающий, можно ли перезаписывать входной массив во время вычислений. УстановкаTrueможет повысить производительность за счет экономии памяти, но изменяет исходный массив. По умолчаниюFalse. -
interpolation: Этот параметр определяет метод интерполяции, используемый, когда желаемый перцентиль находится между двумя точками данных. NumPy поддерживает несколько методов, таких как'linear','lower','higher','midpoint'и'nearest'. Детальный разбор этих методов и их реализации будет представлен в следующих разделах.
Математические основы перцентилей и квантилей
После обзора основных параметров функций numpy.percentile и numpy.quantile, углубимся в их математические основы. Перцентили и квантили являются ключевыми статистическими мерами, используемыми для описания распределения данных. Они позволяют понять, как значения в наборе данных распределены относительно друг друга, а не только их центральную тенденцию или разброс.
Перцентиль (от англ. percentile) — это значение, ниже которого находится определённый процент наблюдений в наборе данных. Например, 25-й перцентиль (или первый квартиль) — это значение, ниже которого лежит 25% данных. Медиана, в свою очередь, является 50-м перцентилем.
Квантиль (от англ. quantile) — это обобщение перцентиля. Квантили делят упорядоченный набор данных на равные, смежные подгруппы. Перцентили являются частным случаем квантилей, где q выражается в процентах (от 0 до 100). Например, 0.25-квантиль эквивалентен 25-му перцентилю. Другие известные квантили включают:
-
Квартили (quartiles): делят данные на четыре равные части (0.25, 0.50, 0.75 квантили).
-
Децили (deciles): делят данные на десять равных частей (0.1, 0.2, …, 0.9 квантили).
Математически, для упорядоченного набора данных $X = {x_1, x_2, …, x_N}$, $p$-й квантиль (или $100p$-й перцентиль) — это значение $x_k$ такое, что доля элементов, меньших или равных $x_k$, составляет не менее $p$, а доля элементов, больших или равных $x_k$, составляет не менее $1-p$. Однако, поскольку данные часто дискретны, а желаемый квантиль может не соответствовать точному элементу в отсортированном массиве, возникает необходимость в интерполяции. Именно методы интерполяции определяют, как вычисляется перцентиль, когда его позиция в отсортированном массиве не является целым числом. NumPy предлагает несколько таких методов, которые мы рассмотрим далее.
Внутренние алгоритмы вычисления перцентилей в NumPy
Как было отмечено ранее, вычисление перцентилей требует упорядочивания данных и, зачастую, интерполяции. В основе numpy.percentile лежат высокооптимизированные алгоритмы для этих двух задач, обеспечивающие точность и производительность.
Сортировка и выборка элементов: стратегии оптимизации
Первым критически важным шагом в вычислении перцентилей является сортировка входного массива. NumPy использует высокоэффективные алгоритмы сортировки, такие как Introsort (гибрид быстрой сортировки, пирамидальной сортировки и сортировки вставками), реализованные на C или Fortran. Это позволяет обрабатывать большие массивы данных с минимальными накладными расходами. Для сохранения целостности исходного массива numpy.percentile по умолчанию работает с его копией, если только параметр overwrite_input не установлен в True, что позволяет выполнять сортировку на месте и экономить память.
Методы интерполяции: детальный разбор и их реализация
После определения позиции перцентиля p в отсортированном массиве (индекс i = (p / 100) * (N - 1), где N — количество элементов), применяется один из пяти методов интерполяции, заданных параметром interpolation. Эти методы определяют, как обрабатывается дробная часть индекса:
-
linear(по умолчанию): Вычисляет взвешенное среднее между двумя ближайшими точками данных. Если индексiравенj + f, гдеj— целая часть, аf— дробная, результат будет(1 - f) * arr[j] + f * arr[j+1]. Это наиболее распространенный метод. -
lower: Возвращает значение элемента с индексомfloor(i). -
higher: Возвращает значение элемента с индексомceil(i). -
nearest: Возвращает значение элемента с индексом, ближайшим кi(округляетiдо ближайшего целого). -
midpoint: Возвращает среднее арифметическое значенийlowerиhigher, то есть(arr[floor(i)] + arr[ceil(i)]) / 2.
Эти методы реализованы в низкоуровневом коде NumPy, обеспечивая точное и предсказуемое поведение функции percentile в зависимости от выбранной стратегии интерполяции.
Сортировка и выборка элементов: стратегии оптимизации
Хотя numpy.percentile может использовать полную сортировку массива как базовый подход, для оптимизации производительности, особенно на больших наборах данных, применяются более продвинутые стратегии. Полная сортировка (например, с помощью np.sort или np.argsort) имеет временную сложность O(N log N), где N — количество элементов. Однако для вычисления одного или нескольких перцентилей часто не требуется полная упорядоченность всего массива.
Ключевой стратегией является использование алгоритмов частичной сортировки или алгоритмов выбора (selection algorithms). Эти алгоритмы способны найти k-й наименьший элемент в массиве в среднем за O(N) времени. NumPy использует оптимизированные внутренние реализации, которые могут быть эквивалентны алгоритму nth_element из стандартных библиотек C++. Это позволяет значительно сократить вычислительные затраты, когда требуется найти лишь несколько конкретных перцентилей.
-
Оптимизация на уровне C/Fortran: Основные операции сортировки и выбора в NumPy реализованы на низкоуровневых языках (C и Fortran), что обеспечивает максимальную производительность. Эти реализации эффективно используют кэш процессора и векторные инструкции.
-
Параметр
overwrite_input: Этот параметр напрямую влияет на стратегию выборки. Еслиoverwrite_input=True, функция может изменять входной массив на месте для выполнения сортировки или частичной сортировки. Это позволяет избежать создания копии массива, что экономит память и может улучшить производительность, особенно для очень больших массивов. Однако это означает, что исходный массив будет изменен. -
Обработка
axis: Когда указан параметрaxis, операции сортировки или частичной сортировки применяются независимо к срезам массива вдоль указанной оси. Это может привести к многократному вызову алгоритмов выбора, но каждый вызов будет работать с меньшим подмассивом, что также оптимизировано внутренними механизмами NumPy.
Методы интерполяции: детальный разбор и их реализация
После того как алгоритмы сортировки или частичного выбора определили элементы, окружающие искомый перцентиль, вступает в игру интерполяция. Она необходима, когда желаемый перцентиль не соответствует точному значению в отсортированном массиве, а находится между двумя элементами. NumPy предлагает несколько методов интерполяции, управляемых параметром interpolation:
-
'linear'(по умолчанию): Вычисляет взвешенное среднее между двумя ближайшими элементами. Веса определяются расстоянием до искомого перцентиля. Еслиi— индекс, аf— дробная часть, то результат(1 - f) * array[i] + f * array[i+1]. Это наиболее часто используемый метод. -
'lower': Возвращает значение элемента с индексомi, то есть ближайшее значение, которое меньше или равно искомому перцентилю. -
'higher': Возвращает значение элемента с индексомi+1, то есть ближайшее значение, которое больше или равно искомому перцентилю. -
'nearest': Выбирает ближайшее значение изarray[i]илиarray[i+1]на основе округления дробной частиfдо ближайшего целого. Еслиfравно 0.5, округление происходит вверх. -
'midpoint': Вычисляет среднее арифметическое междуarray[i]иarray[i+1], то есть(array[i] + array[i+1]) / 2.
Реализация этих методов в NumPy происходит на низком уровне, часто с использованием C-расширений, что обеспечивает высокую производительность. Выбор метода интерполяции существенно влияет на конечный результат, особенно для дискретных распределений или небольших наборов данных.
Структура исходного кода numpy.percentile
После детального рассмотрения методов интерполяции, логично перейти к тому, как эти методы и общая функциональность numpy.percentile реализованы на уровне исходного кода. Основная точка входа для функций numpy.percentile и numpy.quantile находится в файле numpy/lib/function_base.py.
Навигация по файлам и ключевые компоненты реализации
В function_base.py вы найдете обертки для percentile и quantile, которые, в свою очередь, делегируют основную логику внутренней функции _quantile_internal. Эта функция является сердцем реализации, где происходит выбор алгоритма сортировки/выборки и применение метода интерполяции. Для повышения производительности, особенно при работе с большими массивами, _quantile_internal часто вызывает оптимизированные C-реализации, доступные через numpy.core._multiarray_umath или аналогичные модули.
Обработка параметров axis, out и overwrite_input
-
axis: Обработка параметраaxisявляется критически важной для многомерных массивов. Внутри_quantile_internalили ее вспомогательных функций происходит итерация по указанной оси или соответствующее изменение формы (reshape) массива, чтобы применить вычисление перцентиля к каждому "слайсу" вдоль этой оси. Еслиaxis=None, массив сначала выравнивается (flattened). -
out: Параметрoutпозволяет пользователю предоставить предварительно выделенный массив для записи результатов. Это значительно повышает производительность, избегая лишних выделений памяти. Код проверяет наличие и совместимостьoutи, если он предоставлен, направляет результаты непосредственно в него. -
overwrite_input: Этот булевый флаг указывает, может ли функция изменять входной массив на месте. ЕслиTrue,numpy.percentileможет использовать сортировку на месте (например,np.partitionилиnp.sort), что экономит память и может ускорить процесс, поскольку не требуется создание копии массива для сортировки. Однако это изменяет исходный массив, что требует осторожности.
Навигация по файлам и ключевые компоненты реализации
Как было упомянуто, основной точкой входа для numpy.percentile и numpy.quantile является файл numpy/lib/function_base.py. Однако, для понимания полной картины, необходимо углубиться в иерархию вызовов и взаимодействие с другими модулями NumPy.
-
function_base.py: Этот файл содержит высокоуровневые оберткиpercentileиquantile. Обе функции, после начальной валидации входных данных, делегируют основную логику внутренней вспомогательной функции_quantile_internal. -
_quantile_internal: Эта функция является центральным диспетчером. Она отвечает за:-
Обработку параметра
q(процентили/квантили), включая его преобразование в массив, если это необходимо. -
Валидацию входных данных и параметров, таких как
interpolation. -
Подготовку данных для вычислений, включая обработку
axis. -
Вызов низкоуровневых функций для фактического вычисления.
-
-
Обработка
axisи_ureduce: Для эффективной работы с многомерными массивами и параметромaxis,_quantile_internalчасто использует функцию_ureduce(или аналогичные механизмы)._ureduceпозволяет применять скалярную или одномерную операцию к подмассивам вдоль указанной оси, значительно упрощая логику основной функции и оптимизируя производительность за счет минимизации копирования данных. -
Низкоуровневые вычисления: Самые ресурсоемкие части, такие как сортировка, частичная сортировка (partitioning) и выборка элементов, а также применение методов интерполяции, делегируются оптимизированным C-реализациям. Эти функции находятся в модулях
numpy.core._multiarray_umathили других внутренних C-расширениях NumPy. Именно здесь достигается высокая производительность при работе с большими массивами, поскольку операции выполняются на уровне C, минуя накладные расходы Python.Реклама
Обработка параметров axis, out и overwrite_input
Параметр axis играет ключевую роль в определении того, вдоль какой оси многомерного массива должны быть вычислены перцентили. Как было упомянуто, _ureduce является одним из внутренних механизмов, который подготавливает данные. Внутри _quantile_internal, перед вызовом низкоуровневых функций, массив может быть соответствующим образом переформатирован или итерирован. Если axis не указан (или None), массив рассматривается как одномерный. В противном случае, _ureduce эффективно "сворачивает" массив по указанной оси, позволяя базовым алгоритмам работать с одномерными срезами данных, а затем корректно восстанавливает форму результата. Это обеспечивает гибкость и эффективность при работе с данными любой размерности.
Обработка параметра out направлена на оптимизацию использования памяти. Если пользователь предоставляет предварительно выделенный массив через out, NumPy стремится записать результат непосредственно в него, избегая создания нового массива. Это особенно полезно в сценариях, где функция percentile вызывается многократно, например, в циклах или при обработке больших потоков данных. Внутренний код проверяет совместимость типа данных и формы предоставленного out массива с ожидаемым результатом, и в случае успеха делегирует запись результата соответствующим низкоуровневым функциям.
Параметр overwrite_input является флагом производительности. Когда он установлен в True, NumPy разрешает модифицировать входной массив на месте, что может быть использовано для оптимизации сортировки. Например, если для вычисления перцентиля требуется полная или частичная сортировка (как в случае np.partition), установка overwrite_input=True позволяет алгоритму сортировать исходный массив напрямую, экономя время и память, которые иначе были бы затрачены на создание копии. Однако это означает, что исходный массив будет изменен после выполнения функции, что требует осторожности со стороны пользователя. По умолчанию overwrite_input равен False, обеспечивая безопасность данных.
Глубокое погружение в реализацию: особенности и оптимизации
Продолжая наше погружение в архитектуру numpy.percentile, важно рассмотреть, как функция справляется с потенциально проблемными сценариями, такими как граничные случаи и некорректные входные данные, а также как достигается её высокая производительность.
Особенности обработки граничных случаев и некорректных входных данных
NumPy разработан с учетом надежности, и percentile не исключение. Обработка граничных случаев и ошибок критически важна:
-
Пустые массивы: Если входной массив пуст,
numpy.percentileобычно возвращаетNaN(Not a Number) или вызывает ошибкуValueError, в зависимости от контекста и версии NumPy. Это предотвращает некорректные вычисления. -
Значения
q: Параметрq(процентили) должен находиться в диапазоне [0, 100]. Еслиqвыходит за эти пределы, функция вызываетValueError, явно указывая на некорректность входных данных. -
NaNво входных данных: По умолчаниюnumpy.percentileобрабатываетNaNкак обычные значения, что может привести кNaNв результате, еслиNaNприсутствует в данных. Однако, как и многие другие агрегирующие функции NumPy,percentileимеет соответствующуюnanpercentileверсию, которая игнорируетNaNзначения, обеспечивая более гибкую обработку неполных данных. -
Некорректные методы интерполяции: Передача несуществующего строкового значения для параметра
interpolationприведет кValueError, что помогает разработчикам быстро выявлять ошибки в коде.
Производительность и C-расширения для больших массивов
Высокая производительность numpy.percentile для больших массивов достигается за счет использования низкоуровневых C-реализаций. Вместо того чтобы выполнять все операции на чистом Python, NumPy делегирует ресурсоемкие задачи, такие как сортировка или частичная сортировка (partitioning), оптимизированным C-функциям.
-
Частичная сортировка (Partitioning): Для вычисления перцентилей не всегда требуется полная сортировка всего массива. Часто достаточно найти k-й наименьший элемент. NumPy использует алгоритмы частичной сортировки (например,
np.partitionили аналогичные внутренние C-реализации), которые значительно быстрее полной сортировки, особенно для больших массивов и когдаqне является крайним значением (0 или 100). -
Векторизация: Операции применяются ко всему массиву или его подмножествам эффективно, минимизируя накладные расходы Python-циклов.
-
Управление памятью: Параметр
overwrite_inputпозволяет функции изменять входной массив на месте, что может снизить потребление памяти и ускорить операции, избегая создания копий данных, особенно для очень больших массивов. Однако это требует осторожности, так как исходные данные будут изменены.
Особенности обработки граничных случаев и некорректных входных данных
Функция numpy.percentile спроектирована с учетом надежной обработки различных граничных и некорректных входных данных, что критически важно для стабильности научных вычислений.
-
Пустые массивы: При передаче пустого массива (
arr.size == 0) функцияpercentileобычно возвращаетNaN(Not a Number), еслиqне является пустым. Это логичное поведение, поскольку перцентиль для пустого набора данных не определен. Внутренние проверки в коде_quantile_uncheckedили_compute_qth_percentileявно обрабатывают этот сценарий. -
Некорректные значения
q: Параметрqдолжен находиться в диапазоне [0, 100] дляpercentileили [0, 1] дляquantile. Еслиqвыходит за эти пределы, NumPy генерирует ошибкуValueError. Это предотвращает некорректные вычисления и сигнализирует пользователю о неверном использовании. -
Значения
NaNво входных данных: ОбработкаNaNв массиве данных регулируется параметромnan_policy.-
nan_policy='propagate'(по умолчанию): Если в данных естьNaN, результат также будетNaN. -
nan_policy='omit':NaNзначения игнорируются при вычислении перцентиля. Это эквивалентно использованиюnumpy.nanpercentile. -
nan_policy='raise': Генерируется ошибкаValueError, если в данных присутствуютNaN. Эта гибкость позволяет пользователю контролировать поведение функции в зависимости от требований к обработке отсутствующих данных.
-
-
Типы данных: Функция также проверяет совместимость типов данных. Например, для целочисленных массивов результат интерполяции может быть преобразован в тип с плавающей точкой, чтобы корректно представить дробные перцентили.
Эти механизмы обеспечивают, что numpy.percentile не только эффективно вычисляет перцентили, но и делает это предсказуемо и безопасно даже при наличии неидеальных входных данных.
Производительность и C-расширения для больших массивов
Для достижения высокой производительности при работе с большими массивами данных, numpy.percentile, как и многие другие функции NumPy, активно использует низкоуровневые C-расширения. Это критически важно, поскольку Python сам по себе неэффективен для интенсивных числовых вычислений из-за накладных расходов интерпретатора и Global Interpreter Lock (GIL).
В основе оптимизации лежит реализация ключевых алгоритмов, таких как сортировка или частичная сортировка (например, с использованием алгоритмов выбора k-го элемента, подобных Quickselect), непосредственно на C. Когда вы вызываете np.percentile, NumPy делегирует эти ресурсоемкие операции с данными в скомпилированный C-код, который находится в таких модулях, как numpy.core._multiarray_umath. Это позволяет:
-
Минимизировать накладные расходы Python: Избегаются циклы Python и вызовы функций, что значительно ускоряет обработку.
-
Эффективно использовать память: C-реализации могут более тонко управлять памятью, что важно для больших массивов, предотвращая избыточное копирование данных.
-
Оптимизация на уровне CPU: Компиляторы C/C++ могут применять специфические для архитектуры процессора оптимизации (например, SIMD-инструкции), что еще больше повышает скорость выполнения.
Таким образом, производительность numpy.percentile для больших массивов достигается за счет тщательно оптимизированных C-реализаций базовых алгоритмов. Эти низкоуровневые компоненты позволяют NumPy обрабатывать терабайты данных с эффективностью, недостижимой для чистого Python, делая его незаменимым инструментом для научных вычислений и анализа данных.
Сравнение, практическое применение и вклад в NumPy
Функции numpy.percentile и numpy.quantile часто используются взаимозаменяемо, и их внутренняя реализация в значительной степени совпадает, опираясь на те же оптимизированные алгоритмы сортировки и интерполяции, которые мы подробно рассматривали. Ключевое различие заключается в масштабе входного параметра q:
-
numpy.percentileпринимает значенияqв диапазоне от 0 до 100, что соответствует традиционному определению перцентилей (например, 95-й перцентиль). -
numpy.quantileпринимает значенияqв диапазоне от 0 до 1, что более точно соответствует математическому определению квантилей как долей распределения. Функцияnumpy.quantileбыла введена в NumPy версии 1.15 для лучшего соответствия статистической терминологии и обеспечения большей гибкости при работе с вероятностями. Выбор между ними часто сводится к предпочтениям и контексту:percentileудобен для отчетности,quantile— для более абстрактных статистических и вероятностных расчетов.
Изучение внутренней реализации numpy.percentile дает ценные знания, которые могут стать отправной точкой для внесения собственного вклада в NumPy. Библиотека активно развивается, и сообщество всегда приветствует новых контрибьюторов. Если вы обнаружили потенциальную оптимизацию, нашли ошибку или хотите предложить новый метод интерполяции, ваши знания о структуре кода и алгоритмах percentile будут неоценимы.
Процесс внесения вклада обычно включает следующие шаги:
-
Ознакомление с руководством по внесению вклада на официальном сайте NumPy.
-
Форк репозитория NumPy на GitHub и создание новой ветки для ваших изменений.
-
Реализация изменений, написание соответствующих тестов для проверки новой функциональности или исправления.
-
Отправка Pull Request (PR) в основной репозиторий NumPy для рассмотрения. Ваш вклад, будь то улучшение производительности, исправление ошибки или добавление новой функции, помогает поддерживать NumPy в качестве ведущего инструмента для научных вычислений.
percentile vs quantile: тонкости и сценарии использования
Хотя numpy.percentile и numpy.quantile выполняют схожую задачу по вычислению значений, разделяющих отсортированные данные на равные части, их существование как отдельных функций обусловлено как историческими причинами, так и удобством использования в различных контекстах.
Исторически numpy.percentile появилась раньше и стала стандартом для вычисления процентных точек (от 0 до 100) в NumPy. Однако с развитием экосистемы научных вычислений и появлением других библиотек, таких как R и pandas, где квантили (от 0 до 1) являются более распространенным представлением, возникла потребность в функции, напрямую работающей с этим масштабом. Так появился numpy.quantile.
На уровне внутренней реализации numpy.percentile фактически является оберткой для numpy.quantile. Это означает, что вызов np.percentile(data, q) эквивалентен np.quantile(data, q / 100.0). Такое архитектурное решение позволяет избежать дублирования кода и обеспечивает единую логику вычислений, используя один и тот же базовый алгоритм интерполяции.
Сценарии использования:
-
numpy.percentile: Идеально подходит для отчетности и представления результатов, ориентированных на человека, где процентные значения интуитивно понятны. Например, "90-й перцентиль времени отклика" или "25-й перцентиль дохода". -
numpy.quantile: Предпочтителен в статистическом моделировании, машинном обучении и при работе с вероятностями или кумулятивными функциями распределения. Он более удобен, когда значенияqгенерируются программно (например,np.linspace(0, 1, N)для создания равномерно распределенных квантилей) или когда требуется согласованность с другими библиотеками, использующими шкалу от 0 до 1.
Выбор между ними часто сводится к вопросу читаемости кода и соответствия предметной области. Для большинства задач, где требуется гибкость и программное управление квантилями, numpy.quantile является более современным и предпочтительным выбором.
Расширение функциональности: как внести вклад в исходный код NumPy
После детального изучения numpy.percentile и numpy.quantile, их алгоритмов и внутренней реализации, логичным следующим шагом для глубоко заинтересованных разработчиков становится вклад в развитие самой библиотеки NumPy. Понимание того, как эти функции работают «под капотом», является отличной отправной точкой для внесения значимых изменений и улучшений.
NumPy — это проект с открытым исходным кодом, который активно развивается благодаря сообществу. Возможности для вклада разнообразны:
-
Исправление ошибок (Bug Fixes): Даже в таких зрелых функциях, как
percentile, могут быть обнаружены редкие граничные случаи или неточности. Изучение отчетов об ошибках на GitHub-репозитории NumPy может выявить такие возможности. -
Улучшение производительности: Для больших массивов и сложных сценариев всегда есть потенциал для оптимизации, особенно в C-расширениях, которые лежат в основе многих операций NumPy. Это может включать более эффективные алгоритмы сортировки или выборки, а также оптимизацию использования памяти.
-
Расширение функциональности: Возможно, существуют новые методы интерполяции, которые могли бы быть полезны, или способы обработки специфических типов данных, которые еще не полностью поддерживаются.
-
Улучшение документации: Четкая и полная документация критически важна. Обновление docstrings, добавление примеров или улучшение руководств пользователя всегда приветствуется.
Как внести вклад:
-
Начните с малого: Изучите раздел
good first issueна трекере задач NumPy. Это поможет освоиться с процессом. -
Настройте среду разработки: Клонируйте репозиторий NumPy и настройте среду для сборки и тестирования. Это включает компиляторы C/Fortran, так как многие критические части NumPy написаны на этих языках.
-
Изучите код: Для
percentileиquantileосновные Python-обертки находятся вnumpy/lib/function_base.py, а низкоуровневые, производительные реализации — в C-файлах, таких какnumpy/core/src/multiarray/methods.c.srcилиnumpy/core/src/npysort/quicksort.c.src(для сортировки). -
Пишите тесты: Любое изменение должно сопровождаться соответствующими тестами, которые подтверждают корректность нового поведения и предотвращают регрессии. NumPy имеет строгие требования к покрытию тестами.
-
Создайте Pull Request (PR): После реализации изменений и прохождения всех тестов, отправьте PR в основной репозиторий. Будьте готовы к конструктивной критике и обсуждению с мейнтейнерами и другими участниками сообщества.
Вклад в NumPy — это не только способ улучшить библиотеку, но и отличная возможность углубить свои знания в области высокопроизводительных вычислений и стать частью глобального сообщества разработчиков.
Заключение
На протяжении этой статьи мы совершили глубокое погружение в мир numpy.percentile и numpy.quantile, раскрыв их внутреннюю механику от базовых математических принципов до тонкостей реализации в исходном коде NumPy. Мы детально изучили различные методы интерполяции, такие как ‘linear’, ‘lower’, ‘higher’, ‘midpoint’ и ‘nearest’, понимая, как каждый из них влияет на конечный результат и почему выбор метода критичен для статистического анализа.
Мы проанализировали структуру исходного кода, навигацию по файлам и ключевые компоненты, отвечающие за обработку параметров axis, out и overwrite_input. Особое внимание было уделено оптимизациям, граничным случаям и использованию C-расширений, которые обеспечивают высокую производительность NumPy при работе с большими массивами данных.
Понимание этих внутренних механизмов не только позволяет более эффективно использовать функции percentile и quantile, но и открывает двери для активного участия в развитии библиотеки. Как мы обсуждали, глубокое знание архитектуры и алгоритмов является фундаментом для внесения значимого вклада — будь то исправление ошибок, предложение новых функций или оптимизация существующих.
Таким образом, numpy.percentile — это не просто функция, а сложный, тщательно спроектированный инструмент, демонстрирующий мощь и гибкость NumPy. Надеемся, что это исследование вдохновит вас на дальнейшее изучение и активное участие в сообществе открытого исходного кода.