Python numpy数组的第一个最小值或鞍点(微积分导数)是什么?

Python numpy数组的第一个最小值或鞍点(微积分导数)是什么?,python,numpy,minimum,derivative,Python,Numpy,Minimum,Derivative,我有上面描述的numpy数组 功能如 print(arr.argsort()[:3]) 将返回三个最低值中的三个最低索引: [69 66 70] 如何返回第一个索引,其中第一个最小值或第一个鞍点(在微积分意义上),以数组中的第一个为准 在这种情况下,索引处的两个数字0.62026396 0.605666232和3是第一个鞍点(这不是一个真实的鞍点,因为斜率没有变平,但它明显打破了那里坚硬的向下斜率。也许可以添加一个“变平”的阈值)。由于函数在第一个鞍点之前从不上升,因此第一个最小值出现在鞍点

我有上面描述的
numpy
数组

功能如

print(arr.argsort()[:3])
将返回三个最低值中的三个最低索引:

[69 66 70]
如何返回第一个
索引
,其中第一个
最小值
或第一个
鞍点
(在微积分意义上),以数组中的第一个为准

在这种情况下,
索引处的两个数字
0.62026396 0.60566623
2和3是第一个
鞍点
(这不是一个真实的
鞍点
,因为斜率没有变平,但它明显打破了那里坚硬的向下斜率。也许可以添加一个“变平”的阈值)。由于函数在第一个
鞍点之前从不上升,因此第一个
最小值出现在
鞍点之后,这就是我感兴趣的索引

[1.04814804 0.90445908 0.62026396 0.60566623 0.32295758 0.26658469
 0.19059289 0.10281547 0.08582772 0.05091265 0.03391474 0.03844931
 0.03315003 0.02838656 0.03420759 0.03567401 0.038203   0.03530763
 0.04394316 0.03876966 0.04156067 0.03937291 0.03966426 0.04438747
 0.03690863 0.0363976  0.03171374 0.03644719 0.02989291 0.03166156
 0.0323875  0.03406287 0.03691943 0.02829374 0.0368121  0.02971704
 0.03427005 0.02873735 0.02843848 0.02101889 0.02114978 0.02128403
 0.0185619  0.01749904 0.01441699 0.02118773 0.02091855 0.02431763
 0.02472427 0.03186318 0.03205664 0.03135686 0.02838413 0.03206674
 0.02638371 0.02048122 0.01502128 0.0162665  0.01331485 0.01569286
 0.00901017 0.01343558 0.00908635 0.00990869 0.01041151 0.01063606
 0.00822482 0.01312368 0.0115005  0.00620334 0.0084177  0.01058152
 0.01198732 0.01451455 0.01605602 0.01823713 0.01685975 0.03161889
 0.0216687  0.03052391 0.02220871 0.02420951 0.01651778 0.02066987
 0.01999613 0.02532265 0.02589186 0.02748692 0.02191687 0.02612152
 0.02309497 0.02744753 0.02619196 0.02281516 0.0254296  0.02732746
 0.02567608 0.0199178  0.01831929 0.01776025]

您可以使用
np.gradient
np.diff
计算差异(第一个计算中心差异,第二个仅为x[1:]-x[:-1]),然后使用
np.sign
获取梯度符号,另一个
np.diff
查看符号的变化位置。然后过滤正负号变化(对应于最小值):


在环顾四周并根据给出的两个建议(到目前为止),我做了以下工作:

import scipy
from scipy import interpolate

x = np.arange(0, 100)
spl = scipy.interpolate.splrep(x,arr,k=3) # no smoothing, 3rd order spline
ddy = scipy.interpolate.splev(x,spl,der=2) # use those knots to get second derivative 
print(ddy)

asign = np.sign(ddy)
signchange = ((np.roll(asign, 1) - asign) != 0).astype(int)
print(signchange)
这给了我
二阶导数,然后我可以分析,例如,查看符号变化发生的位置:

[-0.894053   -0.14050616  0.61304067 -0.69407217  0.55458251 -0.16624336
 -0.0073225   0.12481963 -0.067218    0.03648846  0.02876712 -0.02236204
  0.00167794  0.01886512 -0.0136314   0.00953279 -0.01812436  0.03041855
 -0.03436446  0.02418512 -0.01458896  0.00429809  0.01227133 -0.02679232
  0.02168571 -0.0181437   0.02585209 -0.02876075  0.0214645  -0.00715966
  0.0009179   0.00918466 -0.03056938  0.04419937 -0.0433638   0.03557532
 -0.02904901  0.02010647 -0.0199739   0.0170648  -0.00298236 -0.00511529
  0.00630525 -0.01015011  0.02218007 -0.01945341  0.01339405 -0.01211326
  0.01710444 -0.01591092  0.00486652 -0.00891456  0.01715403 -0.01976949
  0.00573004 -0.00446743  0.01479495 -0.01448144  0.01794968 -0.02533936
  0.02904355 -0.02418628  0.01505374 -0.00499926  0.00302616 -0.00877499
  0.01625907 -0.01240068 -0.00578862  0.01351128 -0.00318733 -0.0010652
  0.0029     -0.0038062   0.0064102  -0.01799678  0.04422601 -0.0620881
  0.05587037 -0.04856099  0.03535114 -0.03094757  0.03028399 -0.01912546
  0.01726283 -0.01392421  0.00989012 -0.01948119  0.02504401 -0.02204667
  0.0197554  -0.01270022 -0.00260326  0.01038581 -0.00299247 -0.00271539
 -0.00744152  0.00784016  0.00103947 -0.00576122]

[0 0 1 1 1 1 0 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1
 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 0 1]

这就是我检测局部最大值/最小值、拐点和鞍座的方法

让我们首先定义以下函数

import numpy as np


def n_derivative(arr, degree=1):
    """Compute the n-th derivative."""
    result = arr.copy()
    for i in range(degree):
        result = np.gradient(result)
    return result


def sign_change(arr):
    """Detect sign changes."""
    sign = np.sign(arr)
    result = ((np.roll(sign, 1) - sign) != 0).astype(bool)
    result[0] = False
    return result


def zeroes(arr, threshold=1e-8):
    """Find zeroes of an array."""
    return sign_change(arr) | (abs(arr) < threshold)
如果临界点的二阶导数非零,则该点为最大值或最小值:

def maxima_minima(arr):
    return zeroes(n_derivative(arr, 1)) & ~zeroes(n_derivative(arr, 2))


def maxima(arr):
    return zeroes(n_derivative(arr, 1)) & (n_derivative(arr, 2) < 0)


def minima(arr):
    return zeroes(n_derivative(arr, 1)) & (n_derivative(arr, 2) > 0)
如果临界点的二阶导数等于零,但三阶导数非零,则这是一个鞍:

def inflections(arr):
    return zeroes(n_derivative(arr, 1)) & zeroes(n_derivative(arr, 2)) & ~zeroes(n_derivative(arr, 3))
请注意,该方法在数值上不稳定,因为一方面在某些任意阈值定义上检测到零,另一方面,不同的采样可能导致函数/数组不可微。 因此,根据这个定义,你所期望的实际上不是鞍点

为了更好地逼近连续函数,可以对大量过采样(根据代码中的
K
)函数使用三次插值,例如:

import scipy as sp
import scipy.interpolate

data = [
    1.04814804, 0.90445908, 0.62026396, 0.60566623, 0.32295758, 0.26658469, 0.19059289,
    0.10281547, 0.08582772, 0.05091265, 0.03391474, 0.03844931, 0.03315003, 0.02838656,
    0.03420759, 0.03567401, 0.038203, 0.03530763, 0.04394316, 0.03876966, 0.04156067,
    0.03937291, 0.03966426, 0.04438747, 0.03690863, 0.0363976, 0.03171374, 0.03644719,
    0.02989291, 0.03166156, 0.0323875, 0.03406287, 0.03691943, 0.02829374, 0.0368121,
    0.02971704, 0.03427005, 0.02873735, 0.02843848, 0.02101889, 0.02114978, 0.02128403,
    0.0185619, 0.01749904, 0.01441699, 0.02118773, 0.02091855, 0.02431763, 0.02472427,
    0.03186318, 0.03205664, 0.03135686, 0.02838413, 0.03206674, 0.02638371, 0.02048122,
    0.01502128, 0.0162665, 0.01331485, 0.01569286, 0.00901017, 0.01343558, 0.00908635,
    0.00990869, 0.01041151, 0.01063606, 0.00822482, 0.01312368, 0.0115005, 0.00620334,
    0.0084177, 0.01058152, 0.01198732, 0.01451455, 0.01605602, 0.01823713, 0.01685975,
    0.03161889, 0.0216687, 0.03052391, 0.02220871, 0.02420951, 0.01651778, 0.02066987,
    0.01999613, 0.02532265, 0.02589186, 0.02748692, 0.02191687, 0.02612152, 0.02309497,
    0.02744753, 0.02619196, 0.02281516, 0.0254296, 0.02732746, 0.02567608, 0.0199178,
    0.01831929, 0.01776025]
samples = np.arange(len(data))
f = sp.interpolate.interp1d(samples, data, 'cubic')

K = 10
N = len(data) * K

x = np.linspace(min(samples), max(samples), N)
y = f(x)
然后,所有这些定义都可以通过以下方式进行目视测试:

import matplotlib.pyplot as plt

plt.figure()
plt.plot(samples, data, label='data')
plt.plot(x, y, label='f')
plt.plot(x, n_derivative(y, 1), label='d1f')
plt.plot(x, n_derivative(y, 2), label='d2f')
plt.plot(x, n_derivative(y, 3), label='d3f')
plt.legend()
for w in np.where(inflections(y))[0]:
    plt.axvline(x=x[w])
plt.show()

但即使在这种情况下,这一点也不是马鞍。

你的要求有点不清楚?你能澄清一下吗?微积分意义上的最小值是多少?你是说局部极小值,一阶导数的极小值,还是别的什么?也许提供一些有效的Python复制可复制输入(以及预期的输出)会有帮助。局部极小值作为一般原则,您将使用
np.gradient()
计算导数并检查其改变符号的位置(对于极小值和鞍点,您将需要一阶导数和二阶导数),但请记住,这种方法在数值上可能不稳定。这并不完全正确(我正在寻找),但可能是我在寻找第一个鞍点或第一个最小值,以先到者为准。
0.03420759
是数据的第一个局部最小值。用这种方法我完全能理解是的,你是对的。我补充说,我可能也在寻找第一个鞍点。因为你的答案绝对正确,所以我把它加了上去。@norok2你是对的,我以为我用了两次np.diff。因此,
+2
将在局部最小值的“正后方”给您打分(而
+1
将在“正前方”给您打分)
def inflections(arr):
    return zeroes(n_derivative(arr, 1)) & zeroes(n_derivative(arr, 2)) & ~zeroes(n_derivative(arr, 3))
import scipy as sp
import scipy.interpolate

data = [
    1.04814804, 0.90445908, 0.62026396, 0.60566623, 0.32295758, 0.26658469, 0.19059289,
    0.10281547, 0.08582772, 0.05091265, 0.03391474, 0.03844931, 0.03315003, 0.02838656,
    0.03420759, 0.03567401, 0.038203, 0.03530763, 0.04394316, 0.03876966, 0.04156067,
    0.03937291, 0.03966426, 0.04438747, 0.03690863, 0.0363976, 0.03171374, 0.03644719,
    0.02989291, 0.03166156, 0.0323875, 0.03406287, 0.03691943, 0.02829374, 0.0368121,
    0.02971704, 0.03427005, 0.02873735, 0.02843848, 0.02101889, 0.02114978, 0.02128403,
    0.0185619, 0.01749904, 0.01441699, 0.02118773, 0.02091855, 0.02431763, 0.02472427,
    0.03186318, 0.03205664, 0.03135686, 0.02838413, 0.03206674, 0.02638371, 0.02048122,
    0.01502128, 0.0162665, 0.01331485, 0.01569286, 0.00901017, 0.01343558, 0.00908635,
    0.00990869, 0.01041151, 0.01063606, 0.00822482, 0.01312368, 0.0115005, 0.00620334,
    0.0084177, 0.01058152, 0.01198732, 0.01451455, 0.01605602, 0.01823713, 0.01685975,
    0.03161889, 0.0216687, 0.03052391, 0.02220871, 0.02420951, 0.01651778, 0.02066987,
    0.01999613, 0.02532265, 0.02589186, 0.02748692, 0.02191687, 0.02612152, 0.02309497,
    0.02744753, 0.02619196, 0.02281516, 0.0254296, 0.02732746, 0.02567608, 0.0199178,
    0.01831929, 0.01776025]
samples = np.arange(len(data))
f = sp.interpolate.interp1d(samples, data, 'cubic')

K = 10
N = len(data) * K

x = np.linspace(min(samples), max(samples), N)
y = f(x)
import matplotlib.pyplot as plt

plt.figure()
plt.plot(samples, data, label='data')
plt.plot(x, y, label='f')
plt.plot(x, n_derivative(y, 1), label='d1f')
plt.plot(x, n_derivative(y, 2), label='d2f')
plt.plot(x, n_derivative(y, 3), label='d3f')
plt.legend()
for w in np.where(inflections(y))[0]:
    plt.axvline(x=x[w])
plt.show()