Аппроксимация экспериментальных точек нелинейной функцией

In [1]:
import numpy as np
import pandas as pd
import random
from scipy.optimize import basinhopping
from scipy.optimize import minimize
import matplotlib.pyplot as plt

Формирование выборки "экспериментальных" точек

In [2]:
# Независимая переменная
t = np.linspace(0.0, 100.0, num=101, endpoint=True)
In [3]:
# Амплитуда, период и фаза для трех синусоид
a1 = 10.9
T1 = 20.1
phi1 = 5.4
a2 = 10.2
T2 = 10.5
phi2 = 4.3
a3 = 9.3
T3 = 5.8
phi3 = -3.9
In [4]:
# Сумма 3-х синусоид. Требуется, что-бы аппроксимация была близка к этой функции
y = a1 * np.sin(2 * np.pi * t / T1 + phi1) \
  + a2 * np.sin(2 * np.pi * t / T2 + phi2) \
  + a3 * np.sin(2 * np.pi * t / T3 + phi3)
In [5]:
# График исходной функции
plt.plot(t, y, 'b.')
plt.plot(t, y, 'b')
plt.grid()
In [6]:
# Добавляется шум и формируется выборка "экспериментальных" точек
nois = [random.gauss(0, 5) for _ in range(101)]
z = [y[i] + nois[i] for i in range(101)]
data = pd.DataFrame({'t': t, 'y': y, 'z': z,})
In [7]:
# График "экспериментальных" точек
plt.plot(data.t, data.z, 'm.')
plt.grid()

Аппроксимация

Формирование функции для оценки ошибки минимизации

In [8]:
def function_test(b):
    
    # Идентификаторы для 3-х амплитуд, перидов и фаз
    a1 = b[0]
    T1 = b[1]
    phi1 = b[2]
    a2 = b[3]
    T2 = b[4]
    phi2 = b[5]
    a3 = b[6]
    T3 = b[7]
    phi3 = b[8]
    
    # Расчет значения функции
    data['h'] = a1 * np.sin(2 * np.pi * data['t'] / T1 + phi1) \
      + a2 * np.sin(2 * np.pi * data['t'] / T2 + phi2) \
      + a3 * np.sin(2 * np.pi * data['t'] / T3 + phi3)
    
    # Оценка коэффициента детерминации      
    data['dres2'] = (data.h  - data.z) ** 2
    SSres = data.dres2.sum()
    z_mean = data.z.mean()
    data['dtot2'] = (z_mean - data.z) ** 2
    SStot = data['dtot2'].sum()
    R2 = 1. - SSres / SStot
    # При необходимости можно распечатать изменение коэффициента детерминации - приближении его к единице
    # print(R2)
    criterion = abs(SSres / SStot)
    
    return criterion

Начальные значения (далекие от значений исходной функции)

In [9]:
b0 = [
20.,
30.,
0.,
20.,
15.,
0.,
20.,
8.,
0.]

Применение функции basinhopping

In [10]:
minimizer_kwargs = {"method": "BFGS"}
In [11]:
 res = basinhopping(function_test, b0, minimizer_kwargs=minimizer_kwargs, niter=100)
In [12]:
 res  
Out[12]:
                        fun: 0.3830165124006993
 lowest_optimization_result:       fun: 0.3830165124006993
 hess_inv: array([[ 1.44981942e+02, -1.66919141e-02, -1.26781688e-01,
        -1.58877684e+00,  1.71727520e+00,  5.48885053e-01,
        -2.06614715e+00, -1.61051030e+00, -7.08392890e+00],
       [-1.66919141e-02,  5.14887814e-02,  4.66373165e-01,
        -6.86132517e-02, -6.70821074e-02, -4.75311832e-02,
        -1.13637563e-01, -5.31833096e-02, -7.27568003e-02],
       [-1.26781688e-01,  4.66373165e-01,  5.86938737e+00,
        -3.85703718e-01, -6.69370050e-01, -4.60792059e-01,
        -9.52578505e-01, -3.95491727e-01, -7.52200468e-01],
       [-1.58877684e+00, -6.86132517e-02, -3.85703718e-01,
         1.36008179e+02,  1.26828636e+00,  1.01107746e+00,
         3.46875967e+01,  2.30636806e-01,  4.11886837e-01],
       [ 1.71727520e+00, -6.70821074e-02, -6.69370050e-01,
         1.26828636e+00,  6.72718086e+00,  5.24298683e+00,
         5.31985191e+00, -5.52921898e-01,  2.98769132e-01],
       [ 5.48885053e-01, -4.75311832e-02, -4.60792059e-01,
         1.01107746e+00,  5.24298683e+00,  5.78767058e+00,
         4.07787715e+00, -4.04404023e-01,  1.10741956e-01],
       [-2.06614715e+00, -1.13637563e-01, -9.52578505e-01,
         3.46875967e+01,  5.31985191e+00,  4.07787715e+00,
         1.65843366e+02,  1.27512109e+00,  2.06297815e+00],
       [-1.61051030e+00, -5.31833096e-02, -3.95491727e-01,
         2.30636806e-01, -5.52921898e-01, -4.04404023e-01,
         1.27512109e+00,  6.98964117e+00,  9.22555458e+00],
       [-7.08392890e+00, -7.27568003e-02, -7.52200468e-01,
         4.11886837e-01,  2.98769132e-01,  1.10741956e-01,
         2.06297815e+00,  9.22555458e+00,  3.17155408e+01]])
      jac: array([-2.79396772e-07, -8.62404704e-06, -7.63684511e-07, -2.66358256e-06,
        1.64657831e-06, -1.72853470e-06,  1.76578760e-06, -1.83656812e-06,
       -1.12876296e-06])
  message: 'Optimization terminated successfully.'
     nfev: 520
      nit: 48
     njev: 52
   status: 0
  success: True
        x: array([-10.10249755,  -5.81208311, -27.60129138,  10.54287894,
        19.79783755,  42.88124011,  -3.02225826,  12.31952126,
        52.61478713])
                    message: ['requested number of basinhopping iterations completed successfully']
      minimization_failures: 13
                       nfev: 62860
                        nit: 100
                       njev: 6271
                          x: array([-10.10249755,  -5.81208311, -27.60129138,  10.54287894,
        19.79783755,  42.88124011,  -3.02225826,  12.31952126,
        52.61478713])
In [13]:
res.x
Out[13]:
array([-10.10249755,  -5.81208311, -27.60129138,  10.54287894,
        19.79783755,  42.88124011,  -3.02225826,  12.31952126,
        52.61478713])

Полученные параметры искомой функции

In [14]:
a1 = res.x[0]
T1 = res.x[1]
phi1 = res.x[2]
a2 = res.x[3]
T2 = res.x[4]
phi2 = res.x[5]
a3 = res.x[6]
T3 = res.x[7]
phi3 = res.x[8]
In [15]:
# Расчет аппроксимации
y_calc = a1 * np.sin(2 * np.pi * t / T1 + phi1) \
  + a2 * np.sin(2 * np.pi * t / T2 + phi2) \
  + a3 * np.sin(2 * np.pi * t / T3 + phi3)
In [16]:
# График аппроксимации на фоне "экспериментальных" точек
plt.plot(data.t, data.z, 'mo')
plt.plot(t, y_calc, 'g')
plt.grid()
In [17]:
# График аппроксимации (зеленая кривая) на фоне исходной функции (синяя кривая)
plt.plot(data.t, data.y, 'b')
plt.plot(t, y_calc, 'g')
plt.grid()

Применение функции minimize

In [30]:
res = minimize(function_test, b0, method='BFGS')
In [31]:
res
Out[31]:
      fun: 0.651313720765629
 hess_inv: array([[ 1.43369222e+02, -2.35790981e+00, -1.53264857e+00,
        -8.32878143e+00, -1.62960981e+00,  1.72593920e-01,
         6.23349200e+00, -1.42811481e+00, -2.12160685e+00],
       [-2.35790981e+00,  7.44231441e+00,  5.83517707e+00,
         6.21503522e+00, -1.29790427e+00, -1.72234764e+00,
        -1.37154670e+00,  4.78504027e-02, -5.26199316e-02],
       [-1.53264857e+00,  5.83517707e+00,  6.29672140e+00,
         4.10637213e+00, -7.90451442e-01, -1.22948857e+00,
        -6.31360727e-01, -1.18728671e-02, -5.00067876e-01],
       [-8.32878143e+00,  6.21503522e+00,  4.10637213e+00,
         1.78638024e+02, -1.36477725e+00, -2.75879841e+00,
        -5.97834145e+00,  1.13817034e+00,  7.75063910e+00],
       [-1.62960981e+00, -1.29790427e+00, -7.90451442e-01,
        -1.36477725e+00,  7.79784959e+00,  1.14598467e+01,
        -5.25455208e+00,  1.65384128e-01, -4.20405319e-01],
       [ 1.72593920e-01, -1.72234764e+00, -1.22948857e+00,
        -2.75879841e+00,  1.14598467e+01,  4.22280612e+01,
        -1.11509028e+01,  2.84865348e-01, -2.64286729e+00],
       [ 6.23349200e+00, -1.37154670e+00, -6.31360727e-01,
        -5.97834145e+00, -5.25455208e+00, -1.11509028e+01,
         1.90278949e+02,  6.45050874e-01,  7.15897581e+00],
       [-1.42811481e+00,  4.78504027e-02, -1.18728671e-02,
         1.13817034e+00,  1.65384128e-01,  2.84865348e-01,
         6.45050874e-01,  2.12427536e+00,  9.65596408e+00],
       [-2.12160685e+00, -5.26199316e-02, -5.00067876e-01,
         7.75063910e+00, -4.20405319e-01, -2.64286729e+00,
         7.15897581e+00,  9.65596408e+00,  7.86007966e+01]])
      jac: array([ 4.17232513e-07,  2.68965960e-06, -1.31875277e-06, -7.45058060e-09,
        2.31713057e-06, -2.62260437e-06,  1.49011612e-07,  9.53674316e-07,
       -4.47034836e-08])
  message: 'Optimization terminated successfully.'
     nfev: 1140
      nit: 94
     njev: 114
   status: 0
  success: True
        x: array([-10.48987532,  19.79189752,  -4.25226464,  -2.91501636,
        12.30501536, -10.25270792,  -2.13254635,   8.32456858,
        12.51058332])

Полученные параметры искомой функции

In [32]:
a1 = res.x[0]
T1 = res.x[1]
phi1 = res.x[2]
a2 = res.x[3]
T2 = res.x[4]
phi2 = res.x[5]
a3 = res.x[6]
T3 = res.x[7]
phi3 = res.x[8]
In [33]:
# Расчет аппроксимации
y_calc = a1 * np.sin(2 * np.pi * t / T1 + phi1) \
  + a2 * np.sin(2 * np.pi * t / T2 + phi2) \
  + a3 * np.sin(2 * np.pi * t / T3 + phi3)
In [34]:
# График аппроксимации на фоне "экспериментальных" точек
plt.plot(data.t, data.z, 'mo')
plt.plot(t, y_calc, 'g')
plt.grid()
In [35]:
# График аппроксимации (зеленая кривая) на фоне исходной функции (синяя кривая)
plt.plot(data.t, data.y, 'b')
plt.plot(t, y_calc, 'g')
plt.grid()
In [ ]: