import numpy as np
import pandas as pd
import scipy as scp
import copy as cp
import plotly.graph_objects as go
import pydiana.tools.graph_tools as gt
from pydiana.tools import pyroot_fitting as prf
from pydiana.tools import dataframe_manip as dfman
from pydiana.tools import string_manip as strman
import pydiana.tools.math_tools as mts
from pydiana.shell.plotly_theme import plotly_color_palette
import plotly.subplots as psb
#############################
# #
# Histogram Manipulation #
# #
#############################
[docs]
def overlay_numpy_hists(*args):
"""
Analogue to gt.overlay_plots but for numpy hists (i.e. dict with data for root fit)
Parameters
----------
args : arbitrary quantity of dicts with multiple/single numpy hists (at least 1, otherwise an error is raised)
Returns
----------
result (dict) : dict with all hists appended
"""
result = {}
if len(args)<1 : raise Exception("Pass at least one hist, aborting...")
for i in range(len(args)):
hists = args[i]
if not isinstance(hists,dict): raise TypeError("Not single dicts are passed as argument, aborting... ")
result = {**result, **hists}
return result
#######################################################################################################################
[docs]
def peak_finder(hists,
hists2plot,
distance = 5,
prominence = 5,
height = 25,
poissonSigmas:bool = False,
single_std:float = None,
plotSigmas:bool=True,
useThreshold:bool=False,
showplot:bool=False):
"""
Find multiple peaks in multiple (or single) data go.Figure.
Add a scatter plot with the found peaks to the given Figure.
There are 2 ways to determine sigmas:
1) Assuming Poisson statistic, so sigma = sqrt(mean)
2) Using the parameter t to change the height till which consider bins around the peak, than using FWHM
Parameters
----------
hists (dict) : dict with {description : numpy figure instance}
hists2plot : go.Figure instance with multiple plots
distance (float) : parameter of Scipy find_peaks
prominence (float) : parameter of Scipy find_peaks
height (float) : parameter of Scipy find_peaks
poissonSigmas (bool) : if True choose the 1) method for sigmas, if False choose 2)
t (float) : factor to estimate Std dev for 2) method (see description); if None and poissonSigmas is False, there is an error
single_std (float) : user estimate of the single peak gaussian Std dev
plotSigmas (bool) : if True plot Sigmas of gaussians on final plots as rectangles around mean values
useThreshold (bool): if true uses np.where > height to find peaks otherwise uses scipy findpeaks
showplot (bool) : if True the plot is shown
Returns
----------
final_hists : final plots made by given hists + scatter plot with found peaks
init_params (array-like): dict of 2D array { description : [[amp,mean,std],[....],....],[[amp,mean,std],[....],....] }
(first index = dict key, second index = peak choose, third index = [amp,mean,std] choose )
"""
peak_params = {}
final_hists = go.Figure()
for i,(key,plot_data) in enumerate(zip(hists,hists2plot["data"])):
data = hists[key]
h = data["counts"]
b = (data["bin_edges"][:-1]+data["bin_edges"][1:])/2
bin_width = b[1]-b[0]
# peak finder algorithm from Scipy
peaks =[]
if useThreshold:
peaks = np.where(h>height)[0]
else:
peaks = scp.signal.find_peaks(h,distance=distance,prominence=prominence,height=height)[0]
peaks_number = len(peaks)
if peaks_number == 0:
print(f"No peaks found for {key}; try to change distance, prominence or/and height.")
else:
scatter_plot = go.Scatter(y=h[peaks],x=b[peaks],mode='markers',
name = f"{plot_data['name']}: Found {peaks_number} Peak"+('s' if peaks_number>1 else ''))
scatter_plot["marker"] = dict(
color=plotly_color_palette[i%len(plotly_color_palette)],
size=8,
line_width=0.5,
line_color="black",
symbol="x"
)
final_hists.add_trace(scatter_plot)
if peaks_number == 1:
amp = h[peaks][0]
mean = b[peaks][0]
if single_std is None:
std =np.sqrt(np.sum(h*(b-mean)**2)/(np.sum(h)-1))
else:
std=single_std
std = max((std,1.5*np.diff(b)[1]))
peak_params[key] = [[amp,mean,std]]
if peaks_number > 1:
pars = []
amps = h[peaks]
means = b[peaks]
if poissonSigmas:
stds = [np.sqrt(m)/2 for m in means] #the factor 2 make the things better, don't know why
stds = [max((s,1.5*bin_width)) for s in stds]
elif not poissonSigmas:
stds = []
bdist = np.diff(means)
bdist = np.concatenate(([bdist[0]],bdist))/2
for i in range(peaks_number):
consrangemask = np.where((b >means[i]-bdist[i]) & (b<means[i]+bdist[i]))[0]
b2cons = b[consrangemask]
h2cons = h[consrangemask]
m2cons = np.where((h2cons<=amps[i]/2))[0]
b2cons = np.array([x
for _, x in sorted(zip(h2cons[m2cons], b2cons[m2cons]))])
FWHM =np.abs(np.diff(b2cons[-2:]))[0] if len(b2cons)>2 else 0
if FWHM<=1.5*np.diff(b)[1]:
FWHM =np.sqrt(np.sum(h2cons*(b[consrangemask]-means[i])**2)/(np.sum(h2cons)-1))*2.35
std = max((FWHM/2.35,1.5*np.diff(b)[1]))
stds.append(std)
else:
raise Exception("You must specify a way to compute sigmas, either via Poisson or using t, aborting...")
for a,m,s in zip(amps,means,stds):
pars.append([a,m,s])
peak_params[key] = pars
final_hists = gt.overlay_plots((hists2plot,final_hists))
if plotSigmas == True:
for i,parname in enumerate(peak_params):
for pars in peak_params[parname]:
final_hists.add_vrect(x0=pars[1]-pars[2],
x1=pars[1]+pars[2],
opacity=0.25,
line_width=3,
line_color=plotly_color_palette[i%len(plotly_color_palette)],
fillcolor=plotly_color_palette[i%len(plotly_color_palette)])
if showplot:
final_hists.show()
return peak_params , final_hists
##################################
# #
# Advanced Gaussian Fitting #
# #
##################################
[docs]
def multigaus(data,init_params,figin=None,
parfix:dict=None,unit:str='mV',
degree:int=None,singledegree:int=None,
a = 3,xrange:list=None,
return_fit_matrices:bool=False,
polType:str="Chebyshev",mode:str='multi',
cumulative_results:bool=True,
fields= None,
same_background:bool=False,
showplot:bool=True,
fit_options:str = 'QSL',
plot_dimensions=None,
cl:float=None,
):
"""
Wrapper for fitting multiple to data.
You can restrict the fit range to ( min(means)-a*std_dev(min(means)) , max(means)+a*std_dev(max(means)) ) by using a not None parameter a
Parameters
----------
data : single numpy histogram object
init_params : 2D array-like containing the initial parameters ((constant,mean.stddev),...)
parfix (dict) : Dictionary containing the parameters to fix, for the gaussians use integer keys starting from one, the parameters inside the gaussian to fix are indicated as : [0=constant,1=mean,2=stddev]. For the background fit use the key "Background" and the parameters start from 0 as the degree of the parameter. If -1 fix all parameters.
unit (str) : unit of mean/stddev
degree (int) : use a pol(degree) to fit the background [if None, only gaussian sum is fitted]
a (float or array) : factor used to restrict the fit range [if None, no restriction is applied]
return_fit_matrices (bool) : boolean for adding the covariance and correlation matrices of the fit to the return quantities
polType (str) : choose type of polinomial to be fitted ("pol" or "Chebyshev")
mode (str) : mode that chooses the fitting procedure. If 'multi' a single fit is performed with a function that is the sum of all gaussians, if 'single' then several fits of 1 Gaussian are performed on data, if 'both' the 'multi' mode is performed to initialize the 'single' mode which is then performed and returned (background is refitted for every gaussian), if 'bothfixed' then the both mode is run but the background parameters are fixed from the 'multi' mode, if 'bothshared' the background is initialized by the multi mode but is then fitted as if the same_background flag is True.
Returns
----------
Pandas dataframe with fit results, fitted function and if return_fit_matrices is True also the covariance and correlation matrices
"""
if fields is None:
fields = ['Constant','Mean','StdDev','Reduced Chi2','MinX','MaxX']
if singledegree is None:
singledegree=degree
elif singledegree<0:
singledegree=None
if 'multi' in mode or 'both' in mode:
res=multi_gaussians_fit(data=data,init_params=init_params,parfix=parfix,
unit=unit,degree=degree, a=a,xrange=xrange,
return_fit_matrices=return_fit_matrices, polType=polType,
figin = figin if 'multi' in mode else None,
showplot=showplot if 'multi' in mode else False,
fit_options=fit_options,
plot_dimensions=plot_dimensions,
cl=cl,
)
if 'both' in mode:
fit = res[0]
func = res[1]
bkgparams=None
if degree is not None and singledegree is not None:
bkgparams = [f'p{i}' for i in range(degree+1)]
bkgerrors = dfman.extract_fit_dataframe_columns(fit,
*bkgparams,
getError=True)
if mode=='both' and singledegree!=degree:
bkgparams = [0 for _ in range(singledegree)]
else:
bkgparams = [list(bkgerrors[cc])[0] for cc in bkgerrors.columns if 'err' not in cc]
bkgerrors = [list(bkgerrors[cc])[0] for cc in bkgerrors.columns if 'err' in cc]
init_params = np.array(mts.sortmultivec(*dfman.extract_fit_dataframe_columns(fit,
'Constant',
'Mean',
'StdDev',
getError=False).values.T.tolist(),
lead=1
)).T.tolist()
if bkgparams is not None and degree is not None:
for i in range(len(init_params)):
init_params[i] = list(init_params[i])+list(bkgparams)
if 'shared' in mode:
if not same_background: print('Used option shared, overriding same_background to True')
same_background=True
if 'fixed' in mode:
singledegree=degree
same_background=False
if parfix is not None:
parfix['Background']=[i for i in range(degree+1)]
else:
parfix = {'Background':[i for i in range(degree+1)]}
if 'single' in mode or 'both' in mode:
res=separate_gaussians_fit(data=data,init_params=init_params,parfix=parfix,
unit=unit,degree=singledegree, a=a,xrange=xrange,
return_fit_matrices=return_fit_matrices, polType=polType,
cumulative_results=cumulative_results,
fields=fields,
same_background=same_background,
figin=figin,
showplot=showplot,
fit_options=fit_options,
plot_dimensions=plot_dimensions,
cl=cl
)
return res
[docs]
def separate_gaussians_fit(data,init_params,parfix:dict=None,unit:str='mV',
degree:int=None, a = 3,xrange:list=None,
return_fit_matrices:bool=False,
polType:str="Chebyshev",
cumulative_results:bool=False,
fields= ['Constant','Mean','StdDev','Reduced Chi2','MinX','MaxX'],
same_background:bool=False,
figin=None,
showplot:bool=True,
fit_options:str='QSL',
plot_dimensions=None,
cl:float=None,
):
"""
Fitting multiple separated gaussians to data.
You can restrict the fit range to ( min(means)-a*std_dev(min(means)) , max(means)+a*std_dev(max(means)) ) by using a not None parameter a
Parameters
----------
data : single numpy histogram object
init_params : 2D array-like containing the initial parameters ((constant,mean.stddev),...)
parfix (dict) : Dictionary containing the parameters to fix, for the gaussians use integer keys starting from one, the parameters inside the gaussian to fix are indicated as : [0=constant,1=mean,2=stddev]. For the background fit use the key "Background" and the parameters start from 0 as the degree of the parameter. If -1 fix all parameters.
unit (str) : unit of mean/stddev
degree (int) : use a pol(degree) to fit the background [if None, only gaussian sum is fitted]
a (float) : factor used to restrict the fit range [if None, no restriction is applied]
return_fit_matrices (bool) : boolean for adding the covariance and correlation matrices of the fit to the return quantities
polType (str) : choose type of polinomial to be fitted ("pol" or "Chebyshev")
cumulative_results: bool for squeezing together the results of all the fits
fields : list containing which fields to include in the cumulative_result
same_background (bool) : If True the spacing between the gaussians is used to fit the background. and the background parameters are fixed for all gaussians. If False, the background is fitted in the gaussian range every time.
figin: plotly figure to which to append the gaussians.
showplot: bool for enabling plot showing
Returns
----------
Pandas dataframe with fit results, fitted function and if return_fit_matrices is True also the covariance and correlation matrices
"""
if isinstance(a,(int,float)):
a = np.ones(len(init_params))*a
data = cp.deepcopy(data)
if parfix is None:
parfix = {i+1:None for i in range(len(init_params))}
elif 'Background' in list(parfix.keys()):
for gg in range(len(init_params)):
if (gg+1) in (parfix.keys()):
parfix[gg+1] = np.unique(list(parfix[gg+1])+list(parfix['Background'])).tolist()
else:
parfix[gg+1] =[j+3 for j in parfix['Background']]
init_params = np.array(init_params)
parnames = {}
parvals = {}
parlims = {}
ngaus = len(init_params)
if xrange is None and a is None:
start = np.min(data["bin_edges"])
end = np.max(data["bin_edges"])
xrange = (start, end)
elif xrange is None and a is not None:
start = min(init_params[:,1]-a[0]*init_params[:,2])
end = max(init_params[:,1]+a[-1]*init_params[:,2])
xrange = (start, end)
else:
xrange=(min(xrange),max(xrange))
bins2consider=(data['bin_edges']>(xrange[0]))&(data['bin_edges']<(xrange[1]))
data['counts']= data['counts'][bins2consider[:-1]]
data['bin_edges']=data['bin_edges'][bins2consider]
#data separation
res = {}
width =np.mean( np.diff(data['bin_edges']))
bkg_params=None
if same_background and degree is not None:
hh={'counts':data['counts'],'bin_edges':data['bin_edges']}
bkg = prf.make_background_function(degree=degree,start_idx=0,type =polType)
for i,(pp,pf) in enumerate(zip(init_params,parfix)):
distance = a[i]*pp[2]
bins2consider=list(np.invert((np.abs(hh['bin_edges']-pp[1])<(distance+0.5*width))))
hh['counts']= hh['counts'][bins2consider[:-1]]
hh['bin_edges']=hh['bin_edges'][bins2consider]
if len(hh['counts']>0):
bkg = prf.root_func(expr = bkg,
name = 'Background',
xrange = (min(hh['bin_edges']),max(hh['bin_edges'])),
)
bkg,_ = prf.root_hist_fit(heights=hh['counts'],
bin_edges=hh['bin_edges'],
func=bkg,
get_detail_matrices=False,
options = fit_options,
)
bkg = bkg[:dfman.find_row(bkg,'Chi2')]['Values'].values
bkg_params = {3+i:bkg[i] for i in range(degree+1)}
for i,pp in enumerate(init_params):
distance = a[i]*pp[2]
bins2consider=np.where(np.abs(data['bin_edges']-pp[1])<distance+0.5*width)[0]
hh = {'counts':data['counts'][bins2consider[:-1]],'bin_edges':data['bin_edges'][bins2consider]}
# calling previous function
pparams=pp
pparfix = parfix[i+1] if i+1 in list(parfix.keys()) else None
if same_background and bkg_params is not None and degree is not None:
pparams=np.concatenate([pparams,list(bkg_params.values())])
if pparfix is None:
pparfix = list(bkg_params.keys())
else:
for i in bkg_params:
pparfix.append(i)
parlims = {}
parlims[0]=(0,2*pp[0])
parlims[1]=(pp[1]-a[i]*pp[2],pp[1]+a[i]*pp[2])
parlims[2]=(pp[2]/10,pp[2]*2)
res[i+1] = prf.gaussian_fit(histogram=hh,
params = pparams,
parfix = pparfix,
name=f"Gaus {i+1}",
xrange=(pp[1]-distance-0.5*width,
pp[1]+distance+0.5*width),
unit=unit,
degree = degree,
type=polType,
get_detail_matrices=True,
fit_options=fit_options,
parlims = parlims,
)
if figin is not None:
figin = gt.overlay_fit_func(figin=figin,
fit=res[i+1][0],
func=res[i+1][1],
funlab = f'Gaus {i+1}',
cl=cl,
showplot=False
)
fit_results = {gg: res[gg][0] for gg in res}
functions = {gg: res[gg][1] for gg in res}
detail_matrices = {gg: res[gg][2] for gg in res}
#ADDING RESIDUALS AND CHI2 to plot
fig = None
if figin is not None:
fig = psb.make_subplots(rows=3, cols=1,
row_heights=[0.8, 0.4,0.2],
shared_xaxes=True,
vertical_spacing=0.05)
[fig.add_trace(tr,row=1,col=1) for tr in figin['data']]
## ADD RESIDUALS and CHI2 Scatter
chi22plot = dfman.extract_fit_dataframe_columns(fit_results,'Mean','Reduced Chi2',getError=True)
chi22plot = pd.concat([chi22plot[gg] for gg in chi22plot]).reset_index(drop=True)
for i,gg in enumerate(fit_results):
bins2consider=np.where((data['bin_edges']<=fit_results[gg]['Values']['MaxX']) & (data['bin_edges']>=fit_results[gg]['Values']['MinX']) )[0]
hh = {'counts':data['counts'][bins2consider[:-1]],'bin_edges':data['bin_edges'][bins2consider]}
hist = prf.root_hist(height=hh['counts'],bin_edges=hh['bin_edges'])
x=(hh['bin_edges'][1:] +hh['bin_edges'][:-1]) * 0.5
err_x=np.diff(hh['bin_edges'])/np.sqrt(12)
err_res=[hist.GetBinError(b)
for b in range(1,int(hist.GetEntries())-1)]
del hist
residuals=hh['counts']-prf.eval_root_func(x=x,
func=functions[gg],
params=fit_results[gg]['Values'][:-6]
)
color = plotly_color_palette[(i+1)%len(plotly_color_palette)]
for tr in fig['data']:
if tr['name']==f'Gaus {gg}':
color = tr['marker_color']
fig.add_trace(go.Scatter(
x=x,
y=residuals,
error_y = dict(type="data",
array=err_res,
width=0,
),
mode = 'markers',
marker = dict(color=color,
size=3,
symbol = 'cross',),
showlegend=False,
), row=2, col=1)
fig.add_trace(go.Scatter(
x=chi22plot['Mean'].values[i:i+1],
y=chi22plot['Reduced Chi2'].values[i:i+1],
error_x = dict(type="data",
array=chi22plot['err_Mean'].values[i:i+1],
),
mode = 'markers',
marker = dict(color=color,
size=10),
showlegend=False,
), row=3, col=1)
fig['layout']['yaxis']= figin['layout']['yaxis']
fig['layout']['yaxis']['anchor']='x'
fig['layout']['yaxis']['domain']=[0.5, 1.0]
fig['layout']['yaxis2']= {'anchor': 'x2',
'domain': [0.20, 0.49],
'title': {'text': f'Residuals<br> [({unit})<sup>-1</sup>]','font':{'size':15}}}
fig['layout']['yaxis3']= {'anchor': 'x3',
'domain': [0.0, 0.19],
'title': {'text': 'Reduced<br>χ<sup>2</sup>','font':{'size':15}}}
try:
fig.update_xaxes(title_text=figin['layout']['xaxis']['title']['text'], row=3,col=1)
except:
fig.update_xaxes(title='X',row=3,col=1)
try:
fig.update_layout(title=figin['layout']['title']['text'])
except:
fig.update_layout(title="")
fig.add_hline(y=0,col=1,row=2, line=dict(dash="dash", color="black"))
fig.add_hline(y=1,col=1,row=3, line=dict(dash="dash", color="black"))
if plot_dimensions is not None:
fig.update_layout(height=plot_dimensions[0],width=plot_dimensions[1],autosize=False)
if cumulative_results:
if degree is not None:
fields += [f'p{i}' for i in range(degree+1)]
else:
fields = [f for f in fields if f[0]!='p']
units=[fit_results[list(fit_results.keys())[0]]['Units'][f] for f in fields]
fit_results = dfman.extract_fit_dataframe_columns(fit_results,*fields)
total_res = pd.DataFrame(columns = ['Variables','Values','Errors','Units'])
total_res = total_res.set_index('Variables')
for ff in fit_results:
cumres = pd.DataFrame()
cumres['Variables']= [f'Gaussian_{ff:02d}_{f}' for f in fields]
cumres['Values']=[fit_results[ff][f].values[0] for f in fields]
cumres['Errors']=[fit_results[ff]['err_'+f].values[0] for f in fields]
cumres['Units']=units
cumres = cumres.set_index('Variables')
total_res = pd.concat((total_res,cumres))
fit_results = total_res
res = [fit_results,functions,detail_matrices]
if not return_fit_matrices:
res = [fit_results,functions]
if fig is not None:
res = [r for r in res]+[fig]
if showplot:
fig.show()
return res
[docs]
def multi_gaussians_fit(data,
init_params,
parfix:dict=None,
unit:str='mV',
degree:int=None,
a = None,
xrange:list=None,
return_fit_matrices:bool=False,
polType:str="Chebyshev",
figin=None,
showplot:bool=True,
fit_options:str='QSL',
plot_dimensions=None,
cl:float=None,
):
"""
Fitting multiple separated gaussians to data.
You can restrict the fit range to ( min(means)-a*std_dev(min(means)) , max(means)+a*std_dev(max(means)) ) by using a not None parameter a
Parameters
----------
data : single numpy histogram object
init_params : 2D array-like containing the initial parameters ((constant,mean.stddev),...)
parfix (dict) : Dictionary containing the parameters to fix, for the gaussians use integer keys starting from one, the parameters inside the gaussian to fix are indicated as : [0=constant,1=mean,2=stddev]. For the background fit use the key "Background" and the parameters start from 0 as the degree of the parameter. If -1 fix all parameters.
unit (str) : unit of mean/stddev
degree (int) : use a pol(degree) to fit the background [if None, only gaussian sum is fitted]
a (float) : factor used to restrict the fit range [if None, no restriction is applied]
return_fit_matrices (bool) : boolean for adding the covariance and correlation matrices of the fit to the return quantities
polType (str) : choose type of polinomial to be fitted ("pol" or "Chebyshev")
Returns
----------
Pandas dataframe with fit results, fitted function and if return_fit_matrices is True also the covariance and correlation matrices
"""
if isinstance(a,(int,float)):
a = np.ones(len(init_params))*a
data = cp.deepcopy(data)
init_params = np.array(init_params)
parnames = {}
parvals = {}
parlims = {}
ngaus = len(init_params)
if xrange is None and a is None:
start = np.min(data["bin_edges"])
end = np.max(data["bin_edges"])
xrange = (start, end)
elif xrange is None and a is not None:
start = min(init_params[:,1]-a[0]*init_params[:,2])
end = max(init_params[:,1]+a[-1]*init_params[:,2])
xrange = (start, end)
else:
xrange=(min(xrange),max(xrange))
expr=[]
fparfix =[]
total_params = ngaus*3
for i in range(ngaus):
expr.append(f"gaus({i*3})")
parnames[3*i] = "Gaussian_"+str(i+1)+"_Constant"
parnames[3*i+1] = "Gaussian_"+str(i+1)+f"_Mean[{unit}]"
parnames[3*i+2] = "Gaussian_"+str(i+1)+f"_StdDev[{unit}]"
parvals[3*i] = init_params[i][0]
parvals[3*i+1] = init_params[i][1]
parvals[3*i+2] = init_params[i][2]
parlims[3*i] = (0,parvals[3*i]*2)
parlims[3*i+1] = (init_params[i][1]-a[i]*init_params[i][2],init_params[i][1]+a[i]*init_params[i][2])
parlims[3*i+2] = (init_params[i][2]/10,init_params[i][2]*2)
if parfix is not None and parfix !=-1:
for k in parfix:
if isinstance(k,int):
pars = parfix[k]
[fparfix.append((k-1)*3+p) for p in pars]
expr = '+'.join(expr)
if degree is not None:
expr+=prf.make_background_function(degree=degree,start_idx=total_params,type=polType)
total_params += degree+1 if degree>=0 else 0
for j in range(degree+1):
parnames[ngaus*3+j] = f"p{j}"
parvals[ngaus*3+j] = 0
if parfix is not None and parfix!=-1:
if 'Background' in list(parfix.keys()):
[fparfix.append(ngaus*3+p) for p in parfix['Background']]
if parfix ==-1:
fparfix = range(total_params)[:]
multi_function = prf.root_func(expr,
name='my_multi_gaus',
parnames=parnames,
parvals=parvals,
parlims=parlims,
parfix=fparfix,
xrange=xrange)
result = prf.root_hist_fit(heights = data["counts"],
bin_edges = data["bin_edges"],
func=multi_function,
get_detail_matrices=return_fit_matrices,
options = fit_options,
)
if figin is not None:
figin = gt.overlay_fit_func(figin,result[0],result[1],showplot=False,cl=cl)
hist = prf.root_hist(height = data["counts"],
bin_edges = data["bin_edges"])
figin = gt.make_hist_residuals(hist=hist,
figin=figin,
func = result[1],
fit_result=result[0],
showplot=False,
errorbars={'width':0},
marker={'symbol':'cross','size':3},
)
if plot_dimensions is not None:
figin.update_layout(height=plot_dimensions[0],width=plot_dimensions[1],autosize=False)
if showplot:
figin.show()
result = [r for r in result]+[figin]
return result
[docs]
def gaussian_fit(df=None,
xvar=None,
func=None,
hist2fit:str=None,
xlabel:str='',
ylabel:str='',
title:str='',
bins=None,
binwidth=None,
sep_field:str='',
logx=False,
logy=False,
xrange:list=None,
get_detail_matrices:bool=True,
options:str='QLS',
summary:bool=True,
residuals:bool=True,
location_x:float=0.8,
location_y:float=0.7,
style:str='bar',
show_errx:bool=False,
show_erry:bool=False,
showplot = True,
cl:float=None
):
unit_x=strman.unitinstr(xlabel)
if df is not None:
x = df[xvar]
if unit_x is None:
unit_x=strman.unitinstr(xvar)
if xrange is None:
xrange=(np.min(x),np.max(x))
else:
x=xvar
if xrange is None:
xrange=(np.min(x),np.max(x))
if unit_x is None:
unit_x=""
parvals = {0:len(x),
1:np.median(x),
2:np.std(x),
}
func = prf.root_func(expr='gaus',
name = 'MyGaus',
parnames={0:f'Constant[{unit_x}^-1]',
1:f'Mean[{unit_x}]',
2:f'StdDev[{unit_x}]'},
parvals=parvals,
parlims = {0:(0,2*len(x)),
1:xrange,
2:(0,5*np.std(x)),
}
)
return gt.make_hist_fit( df=df,
xvar=xvar,
func=func,
xlabel=xlabel,
ylabel=ylabel,
title=title,
bins=bins,
binwidth=binwidth,
logx=logx,
logy=logy,
xrange=xrange,
get_detail_matrices=get_detail_matrices,
options=options,
summary=summary,
residuals=residuals,
location_x=location_x,
location_y=location_y,
style=style,
show_errx=show_errx,
show_erry=show_erry,
showplot = showplot,
cl=cl,
)