numpy.gradient

numpy.gradient(f, *varargs, **kwargs)

Функция gradient() вычисляет дискретный градиент (конечные разности) N-мерного массива f. Градиент вычисляется на заданной N-мерной сетке с шагом, указанным в параметре varargs.

Параметры:
f - массив NumPy или подобный массиву объект
Одномерный или N-мерный массив значений.
varargs - массив NumPy или подобный массиву объект, необязательный параметр
В данном параметре указывается интервал между значениями массива f (по умолчанию равен единице). Интервал можно указать следующими способами:
  1. одним числом, которое будет определять интервал по всем осям массива f;
  2. N чисел, каждое из которых будет соответствовать каждой из N осей массива f, т.е. dx, dy, dz и т.д.;
  3. N массивов, каждый из которых содержит индексы вдоль каждой из осей массива f, при этом длина массивов должна быть равна длине соответствующей оси;
  4. Любой комбинацией из N чисел или массивов согласно пунктам 2 и 3.
Если указан параметр axis, то количество элементов в varargs должно соответствовать количеству элементов в axis.
edge_order - число из {1, 2}, необязательный параметр
Указывает какой порядок разности должен быть использован при расчете градиента на границе массива f (Доступно с версии 1.9.1. и выше).
axis - None, номер оси или кортеж с номерами осей, необязательный параметр
Градиент будет вычислен только по значениям указанных осей. По умолчанию равно None, что соответствует вычислению градиента по всем осям. Номером может быть отрицательное число, что приведет к отсчету от последней оси массива f.
Возвращает:
результат - массив или список массивов NumPy
Дискретная производная массива или список массивов с дискретными производными по кажой оси массива f. Массив или каждый массив из списка имеет ту же форму что и f.

Замечание

В случае если шаг сетки на котором вычисляется градиент массива является неравномерным, то вычисление осуществляется на основе центральных разностей второго порядка, а так же прямой или обратной односторонней разности на краях массива. При этом предполагается, что значения массива f, являются результатом некоторой функции (хотя, зачастую, это действительно, всего лишь предположение, например, в случае работы с изображениями). Так же предполагается, что данная функция имеет, как минимум три непрерывные производные. Имеея неоднородный шаг h_{\ast } мы можем минимизировать h_{\ast } - "ошибку согласованности" между истинным градиентом (функции, которой может и не быть) и его оценкой из линейной комбинации соседних значений массива (например точек изображения):

\eta_{i}=f_{i}^{(1)}-\left [ \alpha f(x_{i})+\beta f(x_{i}+h_{d})+\gamma f(x_{i}+h_{s}) \right ]

Подстановка в функцию значений f(x_{i} + h_{d}) и f(x_{i} - h_{s}) и дальнейшее разложение в ряд Тейлора прифодят к системе уравнений:

\left\{
                                                                    \begin{array}{r}
                                                                        \alpha+\beta+\gamma=0 \\
                                                                        \beta h_{d}-\gamma h_{s}=1 \\
                                                                        \beta h_{d}^{2}+\gamma h_{s}^{2}=0
                                                                    \end{array}
                                                                \right.

Тогда приближение функции f_{i}^{(1)} будет выглядеть следующим образом:

\hat f_{i}^{(1)} =
    \frac{
        h_{s}^{2}f\left(x_{i} + h_{d}\right)
        + \left(h_{d}^{2} - h_{s}^{2}\right)f\left(x_{i}\right)
        - h_{d}^{2}f\left(x_{i}-h_{s}\right)}
        { h_{s}h_{d}\left(h_{d} + h_{s}\right)}
    + \mathcal{O}\left(\frac{h_{d}h_{s}^{2}
                        + h_{s}h_{d}^{2}}{h_{d}
                        + h_{s}}\right)

Если данные расположены равномерно, т.е. величина шага является постоянной (h_{s}=h_{d}), то мы получаем стандартное приближение второго порядка и вычисление сильно упрощается:

\hat f_{i}^{(1)}=
    \frac{f\left(x_{i+1}\right) - f\left(x_{i-1}\right)}{2h}
    + \mathcal{O}\left(h^{2}\right)

Таким же образом может быть получено как прямое так и обратное приближение, используемое для границ массива.

Смотрите так же: diff, ediff1d

Примеры

>>> 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