numpy.gradient
numpy.gradient(f, *varargs, **kwargs)
Функция gradient() вычисляет дискретный градиент (конечные разности) N-мерного массива f
. Градиент вычисляется на заданной N-мерной сетке с шагом, указанным в параметре varargs
.
-
- f - массив NumPy или подобный массиву объект
- Одномерный или N-мерный массив значений.
- varargs - массив NumPy или подобный массиву объект, необязательный параметр
- В данном параметре указывается интервал между значениями массива
f
(по умолчанию равен единице). Интервал можно указать следующими способами:- одним числом, которое будет определять интервал по всем осям массива
f
; - N чисел, каждое из которых будет соответствовать каждой из N осей массива
f
, т.е. dx, dy, dz и т.д.; - N массивов, каждый из которых содержит индексы вдоль каждой из осей массива
f
, при этом длина массивов должна быть равна длине соответствующей оси; - Любой комбинацией из N чисел или массивов согласно пунктам 2 и 3.
axis
, то количество элементов вvarargs
должно соответствовать количеству элементов вaxis
. - одним числом, которое будет определять интервал по всем осям массива
- edge_order - число из {1, 2}, необязательный параметр
- Указывает какой порядок разности должен быть использован при расчете градиента на границе массива
f
(Доступно с версии 1.9.1. и выше). - axis - None, номер оси или кортеж с номерами осей, необязательный параметр
- Градиент будет вычислен только по значениям указанных осей. По умолчанию равно None, что соответствует вычислению градиента по всем осям. Номером может быть отрицательное число, что приведет к отсчету от последней оси массива
f
.
-
- результат - массив или список массивов NumPy
- Дискретная производная массива или список массивов с дискретными производными по кажой оси массива
f
. Массив или каждый массив из списка имеет ту же форму что иf
.
Замечание
В случае если шаг сетки на котором вычисляется градиент массива является неравномерным, то вычисление осуществляется на основе центральных разностей второго порядка, а так же прямой или обратной односторонней разности на краях массива. При этом предполагается, что значения массива f
, являются результатом некоторой функции (хотя, зачастую, это действительно, всего лишь предположение, например, в случае работы с изображениями). Так же предполагается, что данная функция имеет, как минимум три непрерывные производные. Имеея неоднородный шаг мы можем минимизировать
- "ошибку согласованности" между истинным градиентом (функции, которой может и не быть) и его оценкой из линейной комбинации соседних значений массива (например точек изображения):
Подстановка в функцию значений и
и дальнейшее разложение в ряд Тейлора прифодят к системе уравнений:
Тогда приближение функции будет выглядеть следующим образом:
Если данные расположены равномерно, т.е. величина шага является постоянной (), то мы получаем стандартное приближение второго порядка и вычисление сильно упрощается:
Таким же образом может быть получено как прямое так и обратное приближение, используемое для границ массива.
Примеры
>>> f = np.array([1, 2, 2, 3, 4, 6, 10, 13])
>>> np.gradient(f)
array([1. , 0.5, 0.5, 1. , 1.5, 3. , 3.5, 3. ])
По умолчанию varargs
равен единице, поэтому на границах массива вычисляется обычная разность между соседними элементами массива:
>>> f[1] - f[0], f[-1] - f[-2]
(1, 3)
Внутри массива, для каждого элемента вычисляется разность его соседних значений, но уже деленая на 2*varargs
:
>>> (f[2] - f[0])/2
0.5
>>> (f[3] - f[1])/2
0.5
>>> (f[4] - f[2])/2
1.0
>>> (f[5] - f[3])/2
1.5
>>> (f[6] - f[4])/2
3.0
>>> (f[7] - f[5])/2
3.5
Если изменить шаг, то вычисления будут происходить тем же образом, но деление будет происходить на величину шага у края и удвоенную величину шага внутри массива:
>>> np.gradient(f, 2)
array([0.5 , 0.25, 0.25, 0.5 , 0.75, 1.5 , 1.75, 1.5 ])
>>>
>>> # у краев:
... (f[1] - f[0])/2, (f[-1] - f[-2])/2
(0.5, 1.5)
>>>
>>> # внутри массива:
... for i in range(1, len(f) - 1):
... print((f[i + 1] - f[i - 1])/(2*2))
...
0.25
0.25
0.5
0.75
1.5
1.75
Параметр varargs
может быть задан с помощью массива:
>>> f = np.array([1, 2, 2, 3, 4, 6, 10, 13])
>>> # равномерный интервал:
... h = np.arange(f.size)*5
>>> h
array([ 0, 5, 10, 15, 20, 25, 30, 35])
>>>
>>> np.gradient(f, h)
array([0.2, 0.1, 0.1, 0.2, 0.3, 0.6, 0.7, 0.6])
>>>
>>>
>>> # неравномерный интервал:
... h = np.arange(f.size)**2
>>> h
array([ 0, 1, 4, 9, 16, 25, 36, 49], dtype=int32)
>>>
>>> np.gradient(f, h)
array([1. , 0.75 , 0.075 , 0.17619048, 0.17757937,
0.28585859, 0.30273893, 0.23076923])
Для двумерного массива будет возвращен список из двух массивов, первый будет соответствовать градиенту вдоль первой оси (строки), второй градиенту вдоль второй (столбцов):
>>> f = np.arange(16).reshape(4, 4)
>>> f
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
>>>
>>> np.gradient(f)
[array([[4., 4., 4., 4.],
[4., 4., 4., 4.],
[4., 4., 4., 4.],
[4., 4., 4., 4.]]),
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])]
>>>
>>>
>>> f = f**2
>>> f
array([[ 0, 1, 4, 9],
[ 16, 25, 36, 49],
[ 64, 81, 100, 121],
[144, 169, 196, 225]], dtype=int32)
>>>
>>> np.gradient(f)
[array([[ 16., 24., 32., 40.],
[ 32., 40., 48., 56.],
[ 64., 72., 80., 88.],
[ 80., 88., 96., 104.]]),
array([[ 1., 2., 4., 5.],
[ 9., 10., 12., 13.],
[17., 18., 20., 21.],
[25., 26., 28., 29.]])]
Для каждой оси массива мы можем указать свой шаг:
>>> # для первой оси укажем одинаковый шаг:
... dx = 10
>>>
>>> # для второй уси укажем
... # неравномерный шаг:
... dy_i = [1, 5, 9, 13]
>>>
>>> np.gradient(f, dx, dy_i)
[array([[ 1.6, 2.4, 3.2, 4. ],
[ 3.2, 4. , 4.8, 5.6],
[ 6.4, 7.2, 8. , 8.8],
[ 8. , 8.8, 9.6, 10.4]]),
array([[0.25, 0.5 , 1. , 1.25],
[2.25, 2.5 , 3. , 3.25],
[4.25, 4.5 , 5. , 5.25],
[6.25, 6.5 , 7. , 7.25]])]
Если необходимо вычислить градиент только вдоль определенной оси или множества осей то это можно указать в параметре axis
:
>>> np.gradient(f, axis = 0)
array([[ 16., 24., 32., 40.],
[ 32., 40., 48., 56.],
[ 64., 72., 80., 88.],
[ 80., 88., 96., 104.]])
>>>
>>> np.gradient(f, axis = 1)
array([[ 1., 2., 4., 5.],
[ 9., 10., 12., 13.],
[17., 18., 20., 21.],
[25., 26., 28., 29.]])
Параметр edge_order
позволяет указать какой порядок разности использывать для расчета на границе массива:
>>> f = np.array([1, 2, 2, 3, 4, 6, 10, 13])
>>>
>>> np.gradient(f, edge_order = 1) # равен 1 по умолчанию
array([1. , 0.5, 0.5, 1. , 1.5, 3. , 3.5, 3. ])
>>>
>>> np.gradient(f, edge_order = 2)
array([1.5, 0.5, 0.5, 1. , 1.5, 3. , 3.5, 2.5])
>>> 1/1.5