Spec-Zone.ru › NumPy 2.0

Использование удобных классов

Удобные классы, предоставляемые пакетом полиномов, это:

Имя

Обеспечивает

Polynomial

Степенной ряд

Chebyshev

Ряд Чебышева

Legendre

Ряд Лежандра

Laguerre

Ряд Лагуэра

Hermite

Ряд Эрмита

HermiteE

Ряд Эрмита E

Ряды в данном контексте — конечные суммы соответствующих полиномиальных базисных функций, умноженных на коэффициенты. Например, степенной ряд выглядит так

\[p(x) = 1 + 2x + 3x^2\]

и имеет коэффициенты \([1, 2, 3]\). Ряд Чебышева с теми же коэффициентами выглядит так

\[p(x) = 1 T_0(x) + 2 T_1(x) + 3 T_2(x)\]

и, более общо,

\[p(x) = \sum_{i=0}^n c_i T_i(x)\]

где в данном случае \(T_n\) — функции Чебышева степени \(n\), но они также могут быть базисными функциями любого из других классов. Согласно соглашению для всех классов, коэффициент \(c[i]\) соответствует базисной функции степени i.

Все классы неизменяемы и имеют одинаковые методы, и особенно реализуют арифметические операторы Python: +, -, *, //, %, divmod, **, == и !=. Последние два могут быть немного проблематичными из-за погрешностей округления с плавающей запятой. Сейчас мы продемонстрируем различные операции, используя NumPy версии 1.7.0.

Основы

Сначала нам нужен класс полинома и экземпляр полинома для работы. Классы можно импортировать непосредственно из пакета полиномов или из модуля соответствующего типа. Здесь мы импортируем из пакета и используем традиционный класс Polynomial из-за его привычности:

>>> from numpy.polynomial import Polynomial as P
>>> p = P([1,2,3])
>>> p
Polynomial([1., 2., 3.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Обратите внимание, что в длинном варианте вывода есть три части. Первая — коэффициенты, вторая — область определения, третья — окно:

>>> p.coef
array([1., 2., 3.])
>>> p.domain
array([-1.,  1.])
>>> p.window
array([-1.,  1.])

Вывод полинома отображает выражение полинома в более привычном формате:

>>> print(p)
1.0 + 2.0·x + 3.0·x²

Обратите внимание, что строковое представление полиномов по умолчанию использует символы Юникода (за исключением Windows) для выражения степеней и индексов. Также доступно представление на основе ASCII (по умолчанию на Windows). Формат строки полинома можно переключать на уровне пакета с помощью функции set_default_printstyle:

>>> np.polynomial.set_default_printstyle('ascii')
>>> print(p)
1.0 + 2.0 x + 3.0 x**2

или управлять им для отдельных экземпляров полиномов с помощью форматирования строк:

>>> print(f"{p:unicode}")
1.0 + 2.0·x + 3.0·x²

Мы будем разбираться с областью определения и окном, когда дойдём до подгонки, а пока мы их игнорируем и пройдёмся по основным алгебраическим и арифметическим операциям.

Сложение и вычитание:

>>> p + p
Polynomial([2., 4., 6.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')
>>> p - p
Polynomial([0.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Умножение:

>>> p * p
Polynomial([ 1.,   4.,  10.,  12.,   9.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Степень:

>>> p**2
Polynomial([ 1.,   4., 10., 12.,  9.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Деление:

Оператор целочисленного деления '//' — это оператор деления для классов полиномов; полиномы в этом отношении рассматриваются как целые числа. Для версий Python < 3.x оператор '/' отображается на '//', как и в Python, для более поздних версий оператор '/' будет работать только для деления на скаляры. В какой-то момент он будет устаревшим:

>>> p // P([-1, 1])
Polynomial([5.,  3.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Остаток:

>>> p % P([-1, 1])
Polynomial([6.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Divmod:

>>> quo, rem = divmod(p, P([-1, 1]))
>>> quo
Polynomial([5.,  3.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')
>>> rem
Polynomial([6.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Вычисление:

>>> x = np.arange(5)
>>> p(x)
array([  1.,   6.,  17.,  34.,  57.])
>>> x = np.arange(6).reshape(3,2)
>>> p(x)
array([[ 1.,   6.],
       [17.,  34.],
       [57.,  86.]])

Подстановка:

Подставить полином для x и разложить результат. Здесь мы подставляем p в себя, что приводит к новому полиному степени 4 после разложения. Если полиномы рассматривать как функции, это композиция функций:

>>> p(p)
Polynomial([ 6., 16., 36., 36., 27.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Корни:

>>> p.roots()
array([-0.33333333-0.47140452j, -0.33333333+0.47140452j])

Иногда неудобно явно использовать экземпляры Polynomial, поэтому кортежи, списки, массивы и скаляры автоматически преобразуются в арифметических операциях:

>>> p + [1, 2, 3]
Polynomial([2., 4., 6.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')
>>> [1, 2, 3] * p
Polynomial([ 1.,  4., 10., 12.,  9.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')
>>> p / 2
Polynomial([0.5, 1. , 1.5], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Полиномы, отличающиеся областью определения, окном или классом, не могут быть смешаны в арифметике:

>>> from numpy.polynomial import Chebyshev as T
>>> p + P([1], domain=[0,1])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 213, in __add__
TypeError: Domains differ
>>> p + P([1], window=[0,1])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 215, in __add__
TypeError: Windows differ
>>> p + T([1])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 211, in __add__
TypeError: Polynomial types differ

Но разные типы могут использоваться для подстановки. Фактически, таким образом выполняется преобразование классов Polynomial друг в друга для преобразования типа, области определения и окна:

>>> p(T([0, 1]))
Chebyshev([2.5, 2. , 1.5], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Что даёт полином p в форме Чебышева. Это работает, потому что \(T_1(x) = x\), а замена \(x\) на \(x\) не изменяет исходный полином. Однако все умножения и деления будут выполняться с использованием рядов Чебышева, поэтому тип результата.

Предполагается, что все экземпляры полиномов неизменяемы, поэтому операции с расширением (+=, -=, и т. д.) и любая другая функциональность, которая нарушала бы неизменяемость экземпляра полинома, намеренно не реализована.

Математический анализ

Экземпляры полинома можно интегрировать и дифференцировать:

>>> from numpy.polynomial import Polynomial as P
>>> p = P([2, 6])
>>> p.integ()
Polynomial([0., 2., 3.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')
>>> p.integ(2)
Polynomial([0., 0., 1., 1.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Первый пример интегрирует p один раз, второй пример интегрирует его дважды. По умолчанию нижний предел интегрирования и константа интегрирования равны 0, но оба могут быть указаны:

>>> p.integ(lbnd=-1)
Polynomial([-1.,  2.,  3.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')
>>> p.integ(lbnd=-1, k=1)
Polynomial([0., 2., 3.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

В первом случае нижний предел интегрирования установлен в -1, а константа интегрирования — 0. Во втором случае константа интегрирования также установлена в 1. Дифференцирование проще, так как единственный параметр — количество раз, которое полином дифференцируется:

>>> p = P([1, 2, 3])
>>> p.deriv(1)
Polynomial([2., 6.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')
>>> p.deriv(2)
Polynomial([6.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Другие конструкторы полиномов

Создание полиномов, задавая коэффициенты, — всего лишь один способ получения экземпляра полинома; они также могут быть созданы, задавая их корни, преобразуя из других типов полиномов и по наименьшим квадратам. Подгонка обсуждается в отдельном разделе, другие методы продемонстрированы ниже:

>>> from numpy.polynomial import Polynomial as P
>>> from numpy.polynomial import Chebyshev as T
>>> p = P.fromroots([1, 2, 3])
>>> p
Polynomial([-6., 11., -6.,  1.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')
>>> p.convert(kind=T)
Chebyshev([-9.  , 11.75, -3.  ,  0.25], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Метод convert также может преобразовывать область определения и окно:

>>> p.convert(kind=T, domain=[0, 1])
Chebyshev([-2.4375 ,  2.96875, -0.5625 ,  0.03125], domain=[0.,  1.], window=[-1.,  1.], symbol='x')
>>> p.convert(kind=P, domain=[0, 1])
Polynomial([-1.875,  2.875, -1.125,  0.125], domain=[0.,  1.], window=[-1.,  1.], symbol='x')

В версиях numpy >= 1.7.0 также доступны методы класса basis и cast. Метод cast работает как метод convert, а метод basis возвращает базисный полином заданной степени:

>>> P.basis(3)
Polynomial([0., 0., 0., 1.], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')
>>> T.cast(p)
Chebyshev([-9.  , 11.75, -3. ,  0.25], domain=[-1.,  1.], window=[-1.,  1.], symbol='x')

Преобразования между типами могут быть полезны, но не рекомендуется для рутинного использования. Потеря точности в вычислениях при переходе от ряда Чебышева степени 50 к ряду Polynomial той же степени может привести к тому, что результаты численного вычисления будут по существу случайными.

Подгонка

Подгонка — это причина, по которой атрибуты domain и window являются частью удобных классов. Чтобы проиллюстрировать проблему, ниже показаны значения полиномов Чебышева до степени 5.

>>> import matplotlib.pyplot as plt
>>> from numpy.polynomial import Chebyshev as T
>>> x = np.linspace(-1, 1, 100)
>>> for i in range(6):
...     ax = plt.plot(x, T.basis(i)(x), lw=2, label=f"$T_{i}$")
...
>>> plt.legend(loc="upper left")
>>> plt.show()
../_images/routines-polynomials-classes-1.png

В диапазоне -1 <= x <= 1 они являются хорошими функциями с равномерным распределением, лежащими в пределах +/- 1. Те же графики в диапазоне -2 <= x <= 2 выглядят совершенно иначе:

>>> import matplotlib.pyplot as plt
>>> from numpy.polynomial import Chebyshev as T
>>> x = np.linspace(-2, 2, 100)
>>> for i in range(6):
...     ax = plt.plot(x, T.basis(i)(x), lw=2, label=f"$T_{i}$")
...
>>> plt.legend(loc="lower right")
>>> plt.show()
../_images/routines-polynomials-classes-2.png

Как видно, «хорошие» участки стали незначительными. При использовании полиномов Чебышева для подгонки мы хотим использовать область, где x находится между -1 и 1, и именно это задаёт window. Однако маловероятно, что данные, подлежащие подгонке, имеют все точки данных в этом интервале, поэтому мы используем domain для указания интервала, где находятся точки данных. Когда подгонка выполняется, область определения сначала отображается в окно с помощью линейного преобразования, и выполняется обычная подгонка по наименьшим квадратам с использованием сопоставленных точек данных. Окно и область определения подгонки являются частью возвращаемого ряда и автоматически используются при вычислении значений, производных и т. д. Если они не указаны в вызове, процедура подгонки будет использовать окно по умолчанию и наименьшую область определения, содержащую все точки данных. Это проиллюстрировано ниже для подгонки к шумовой синусоидальной кривой.

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> from numpy.polynomial import Chebyshev as T
>>> np.random.seed(11)
>>> x = np.linspace(0, 2*np.pi, 20)
>>> y = np.sin(x) + np.random.normal(scale=.1, size=x.shape)
>>> p = T.fit(x, y, 5)
>>> plt.plot(x, y, 'o')
>>> xx, yy = p.linspace()
>>> plt.plot(xx, yy, lw=2)
>>> p.domain
array([0.        ,  6.28318531])
>>> p.window
array([-1.,  1.])
>>> plt.show()
../_images/routines-polynomials-classes-3.png

© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/reference/routines.polynomials.classes.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API