代码之家  ›  专栏  ›  技术社区  ›  Ivan

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

  •  -1
  • Ivan  · 技术社区  · 6 年前

    enter image description here

    我有以下内容 numpy 上面描述的数组。

    函数类

    print(arr.argsort()[:3])
    

    将返回三个最低值的三个最低下标:

    [69 66 70]
    

    我怎么还 第一 index 第一个地方 minimum 或首先 saddle point (在微积分的意义上)哪一个是数组的第一个?

    在这种情况下,这两个数字 0.62026396 0.60566623 指数 2和3是第一个 鞍点 (这不是真的 鞍点 因为斜坡没有变平,但是它明显地打破了那里坚硬的向下斜坡。或许可以添加一个“平坦”的阈值)。因为函数永远不会在第一个函数之前上升 鞍点 因此第一个 mimimum 在以下时间之后发生 鞍点 ,这是我感兴趣的索引。

    [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]
    
    3 回复  |  直到 6 年前
        1
  •  1
  •   norok2    6 年前

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

    首先定义以下函数

    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)
    

    我们现在可以利用 derivative test

    临界点的一阶导数等于零。

    def critical_points(arr):
        return zeroes(n_derivative(arr, 1))
    

    如果临界点具有非零的二阶导数,则该点为最大值或最小值:

    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, 2)) & ~zeroes(n_derivative(arr, 3))
    

    如果临界点的二阶导数等于零,但三阶导数非零,则这是一个鞍:

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

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

        2
  •  1
  •   Tarifazo    6 年前

    你可以使用 np.gradient np.diff 要评估差异(第一个计算中心差异,第二个仅为x[1:]-x[:-1]),然后使用 np.sign 得到梯度符号和另一个 NP.DIFF 看看标志在哪里改变。然后过滤正符号变化(对应于最小值):

     np.where(np.diff(np.sign(np.gradient(x))) > 0)[0][0]+2   #add 2 as each time you call np.gradient or np.diff you are substracting 1 in size, the first [0] is to get the positions, the second [0] is to get the "first" element
    >> 8
    x[np.where(np.diff(np.sign(np.gradient(x))) > 0)[0][0]+2]
    >> 0.03420759
    
        3
  •  0
  •   Ivan    6 年前

    我环顾了一下周围,并从给出的两个建议(到目前为止)中得出以下结论:

    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)
    

    这给了我 second derivative ,然后我可以分析,例如,查看符号更改发生的位置:

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