简体   繁体   中英

Applying bounds to scipy.optimize.curve_fit() leads to "ValueError: `x0` must have at most 1 dimension."

Somehow the following code raises the error "ValueError: x0 must have at most 1 dimension." as soon as I add bounds to my Fit. I have absolutely no idea what I'm doing wrong here. The Goal is to restrain the fit of the 8 Lorentzian Curves to the given bounds. However, the presented code propably won't lead to a fit, but this is a problem I should be able to solve.

import matplotlib.pyplot as plt
import numpy as np
import scipy as scipy
from scipy.signal import find_peaks, peak_widths
import time

# Functions needed for Fitting model
def lorentzian(x, amp, cen, wid):
    return amp*wid**2/((x-cen)**2+wid**2)

def multi_lorentzian(x, params, *args):
    if args:
        params = [params] + [x for x in args]
        try:
            params = np.array(params).reshape(len(params)//3, 3)
        except:
            raise ValueError("Parameter dimensions don't fit the model!")
    total_curve = 0
    for amp, cen, wid in params:
        total_curve += lorentzian(x, amp, cen, wid)
    return total_curve

##############################################################################
# create data
samples = 200
start = 2.75
stop = 3
x_incr = (stop-start)/samples
x_array = np.linspace(start, stop, samples) # frequency in GHz
amp_array = np.random.uniform(0.03, 0.1, 8) # 3 bis 10% Kontrast
cen_array = [2.81, 2.829, 2.831, 2.848, 2.897, 2.914, 2.9165, 2.932]
# cen_array =  np.random.uniform(start, stop, 8)
wid_array = [0.003, 0.003, 0.003,0.003, 0.003, 0.003, 0.003, 0.003]


y_array = 1-multi_lorentzian(x_array,
                                    np.array([amp_array, cen_array, wid_array]).T)  
y_noise = y_array + np.random.normal(0, 1, samples)*1e-3

# mirroring to get maxima instead of minima
y_noise_inv = -y_noise+1
##############################################################################
# prepare guessing of start values

heights= np.random.uniform(0.03, 0.1, 8)
widths = np.random.uniform(0.002, 0.004, 8)
center_guess = cen_array+np.random.normal(0, 1, 8)*1e-3
p0_array =np.array([heights,center_guess, widths]).T 
bounds_array = ([0., 2.75, 0.], [1., 3., 0.5])


popt_y, pcov_y = scipy.optimize.curve_fit(multi_lorentzian, x_array, y_noise_inv,
                                              p0=p0_array, bounds= bounds_array)

popt_y = popt_y.reshape(len(popt_y)//3, 3)
single_peaks = [lorentzian(x_array, i, j, k) for i,j,k in popt_y]
perr_y = np.sqrt(np.diag(pcov_y))
residual_y = y_noise_inv - multi_lorentzian(x_array, popt_y)
ss_res = np.sum(residual_y**2)
ss_tot = np.sum((y_noise_inv-np.mean(y_noise_inv))**2)
r_squared = 1 - (ss_res / ss_tot)

Ok, after some digging, the issue was quite simple. p0 is supposed to be flat, not a 2D array that you supplied. I only had to change two lines to make things work.

1st, the bounds array. You're supposed to have as many minimum and maximum values as you have parameters, and since you have 3*8 params, then I just multiplied them as shown here.

bounds_array = ([0., 2.75, 0.]*8, [1., 3., 0.5]*8)

2nd, I flattened p0 when calling curve_fit .

popt_y, pcov_y = scipy.optimize.curve_fit(multi_lorentzian, x_array, y_noise_inv, p0=p0_array.flatten(), bounds= bounds_array)

And this is the fit: 在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM