I am still discovering the wonders of lmfit (and python...) I therefore apologise if my question is trivial or ill-formulated.I would like to make a function that would take any model (e.g. PseudoVoigt) and create an asymmetric version of it, to be able to test different profiles to fit asymmetric X-ray diffraction peaks.Creating a function generating a piecewise model using different prefixes (e.g. prefix='Left_' and prefix='Right_') through if/else is not a problem, but the two points that block me are :1. Some of the parameters are shared between the two sides (e.g. the position and maximum of the maximum) but others are side-dependent (generally width, but potentially mixing parameters in case of a pseudo-voigt for example). The number of shared and side-dependent parameters depends on the model and, potentially on the user.
2. I would like this function to be able to handle any model (i.e. having a variable number of parameters).
So my questions are :Q1. Is there a way to create such a universal "model-splitting" function ?
Q2. If no, then how can I create it for a given model, say a PseudoVoigt ? (I should then be able to derive similar scheme for the other models)
Hi Matt,Thanks for the answer. I hope you enjoyed the eclipse. You're lucky guy, the next total eclipse in the US is in not so long !I had been fidgeting with something that looked like you're first solution, but I could not make it work. Thank you for providing me with a solution !The skewed functions are indeed a good solution, thanks for pointing them out to me. As a lazy physicist, as soon as I saw the profiles I needed for my purpose, I dove into using them and not being a native speaker (nor very versed in statistics) "skewed" did not ring a bell.
Just to be sure, I'd have a question about your skewed_gaussian : your "beta" corresponds to wikipedia's "alpha" and you introduce a new parameter "gamma" that enters the definition of the asymmetry and may be subsequently fitted. Is that correct ?
As I was thinking about the Lorentzian and Pseudo-Voigt profiles, I was starting to mix up the "gamma" for the asymmetry and the one related to the HWHM of the Lorentzian profile.
In order to be coherent with your site, it might be good to have skewed versions of the Built-in models (from a selfish point of view, I would particularly be interested in a skewed Lorentzian, pseudo-voigt and Pearson VII, as they are quite useful for diffraction data) as well as an "asymmetry model" (also based on the same form) for models built by the user.
Concerning the functional to use to introduce the asymmetry, I am not aware of any particular preference for elastic scattering data. I have never come across anything but the definition a piecewise function with two hwhm's, which usually brings in more problems than solutions.
Let me know how I can help.
---- Pierre
You received this message because you are subscribed to the Google Groups "lmfit-py" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+unsubscribe@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.
To view this discussion on the web visit https://groups.google.com/d/msgid/lmfit-py/b35beac9-afd6-40c0-b13c-cc0f12560352%40googlegroups.com.
Right, that would be confusing.... Perhaps the generic asymmetry function should use 'alpha', and the skewed Gaussian definiton be changed (ater some deprecation)?
I'm not really an expert in X-ray diffraction, but I would be somewhat surprised if asymmetric peaks were needed very often, and would think that would be due to some instrumental issue. But, it's fine to want to fit asymmetric peaks.
I think that trying out "StepModel() * PeakModel()", with a choice of "form='erf'" and so on for StepModel() and any of the PeakModels you list above. If that can work for your data, a simple example would be much appreciated!
from lmfit.models import LorentzianModelfrom scipy.special import erffrom lmfit import Model
model = LorentzianModel(prefix='l_peak_')
def asym(x, center_asym=0, beta=1): return 1 + erf(beta*(x-center_asym))
def createAsymModel(x, model): return Model(asym) * model
asymModel = createAsymModel(x, model)params = model.guess(y, x=x)params += Model(asym).make_params(center_asym=x[np.argmax(y)])out = asymModel.fit(y, params, x=x)plt.plot(x, out.init_fit, 'k--', label='Intial values')plt.plot(x, out.best_fit, 'y-', linewidth=2, label='Skewed Lor. fit')print(out.fit_report(min_correl=0.25))
NameError: Two models have parameters named "center". Use distinct names.
model = LorentzianModel(prefix='l_peak_')
def AsymmetryModel(x, model, center_asym=0, beta=1): return Model(1 + erf(beta*(x-center_asym))) * model
asymModel = AsymmetryModel(x, model)params = model.guess(y, x=x) params.add('center_asym', value=x[np.argmax(y)])
Traceback (most recent call last):
File "<ipython-input-71-a3a6fce2b716>", line 1, in <module> runfile('/Users/janolin/Documents/DocumentsPro/Python/Codes/Test_asymetric.py', wdir='/Users/janolin/Documents/DocumentsPro/Python/Codes')
File "//anaconda/lib/python3.5/site-packages/spyder/utils/site/sitecustomize.py", line 688, in runfile execfile(filename, namespace)
File "//anaconda/lib/python3.5/site-packages/spyder/utils/site/sitecustomize.py", line 101, in execfile exec(compile(f.read(), filename, 'exec'), namespace)
File "/.../Codes/Test_asymetric.py", line 186, in <module> asymModel = AsymmetryModel(x, model)
File "/.../Codes/Test_asymetric.py", line 184, in AsymmetryModel return Model(1 + erf(beta*(x-center_asym))) * model
File "//anaconda/lib/python3.5/site-packages/lmfit/model.py", line 111, in __init__ self._parse_params()
File "//anaconda/lib/python3.5/site-packages/lmfit/model.py", line 157, in _parse_params argspec = inspect.getargspec(self.func)
File "//anaconda/lib/python3.5/inspect.py", line 1043, in getargspec getfullargspec(func)
File "//anaconda/lib/python3.5/inspect.py", line 1095, in getfullargspec raise TypeError('unsupported callable') from ex
TypeError: unsupported callable
import numpy as npimport matplotlib.pyplot as plt
with open('003cm.xy') as f : data = np.loadtxt(f)x = data[:,0]y = data[:,1]
from lmfit.models import LinearModelline_mod = LinearModel(prefix='bckd_') # linear backgroundslopei = (y[np.argmax(x)]-y[np.argmin(x)])/(x.max()-x.min()) # intial slopeinteri = (y[np.argmin(x)]-slopei*x.min()) # initial interceptpars_lin = line_mod.make_params(intercept=interi, slope=slopei)
bkd_mod = line_mod # as I may chose between linear orpars_bkd = pars_lin # constant (not shown here)
from lmfit.models import StepModel, ConstantModelfrom lmfit.models import LorentzianModel, VoigtModel, GaussianModelfrom lmfit import Parameter, Parameters
def AsymModel(model): """ Creates an asymmetric version of the model and initialises the parameters """ params = Parameters() params += model.guess(data=y, x=x) params += pars_bkd p1 = Parameter('Step_amplitude', 1.0) p2 = Parameter('Step_center', x[np.argmax(y)]) p3 = Parameter('Step_sigma', 1.0) params.add_many(p1, p2, p3) return model * StepModel(form='erf', prefix='Step_'), params
asym_model, params = AsymModel(GaussianModel(prefix='g_peak_'))tot_model = asym_model + bkd_modparams += pars_bkdout = tot_model.fit(y, params, x=x)plt.plot(x, y, 'bo')
plt.plot(x, out.init_fit, 'k--', label='Intial values')
plt.plot(x, out.best_fit, 'y-', linewidth=2, label='Skewed Gauss. fit')print(out.fit_report(min_correl=0.25))
---- Pierre
You received this message because you are subscribed to the Google Groups "lmfit-py" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+unsubscribe@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.
To view this discussion on the web visit https://groups.google.com/d/msgid/lmfit-py/56e31a08-0a5a-4880-bce5-abbe6aa60924%40googlegroups.com.
If you think this deserves to be included, I'd be delighted to have contributed.
About the centers this is a very valid point. Unfortunately I don't know how to make the two parameters equal.
About the line profile, you're absolutely right, but we don't have the chance to work at a synchrotron ;)
-- pierre
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+u...@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.To view this discussion on the web visit https://groups.google.com/d/msgid/lmfit-py/56e31a08-0a5a-4880-bce5-abbe6aa60924%40googlegroups.com.--Matt
--
You received this message because you are subscribed to a topic in the Google Groups "lmfit-py" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/lmfit-py/aD51ufTwawU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to lmfit-py+u...@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.
To view this discussion on the web visit https://groups.google.com/d/msgid/lmfit-py/CA%2B7ESbq8UckJjpUC5X%2BFko8WQOyMc-MBQDGMUPuuJvdo067hLA%40mail.gmail.com.
If you think this deserves to be included, I'd be delighted to have contributed.
About the centers this is a very valid point. Unfortunately I don't know how to make the two parameters equal.
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+unsubscribe@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.To view this discussion on the web visit https://groups.google.com/d/msgid/lmfit-py/56e31a08-0a5a-4880-bce5-abbe6aa60924%40googlegroups.com.----Matt
You received this message because you are subscribed to a topic in the Google Groups "lmfit-py" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/lmfit-py/aD51ufTwawU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to lmfit-py+unsubscribe@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.
To view this discussion on the web visit https://groups.google.com/d/msgid/lmfit-py/CA%2B7ESbq8UckJjpUC5X%2BFko8WQOyMc-MBQDGMUPuuJvdo067hLA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "lmfit-py" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+unsubscribe@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.
To view this discussion on the web visit https://groups.google.com/d/msgid/lmfit-py/CABg%2BS1ghfkWW6jmr5OvauC6NQuLCvouAnTOQB6%2B1qpvirPYWaA%40mail.gmail.com.
Following your example, you should be able to say:params['Step_center'].set(expr='g_peak_center')or even
p2 = Parameter('Step_center', expr='g_peak_center')
this will force 'Step_center' to have the same value as 'g_peak_center'. The expression held in a Parameter's 'expr' can be nearly any valid python expression written in terms of the other parameter names and common Python / numpy functions and values.
asym_model, params = AsymModel(GaussianModel(prefix='g_peak_')
asym_model, params = AsymModel(VoigtModel(prefix='v_peak_')
p2 = Parameter('Step_center', expr='g_peak_center')
p2 = Parameter('Step_center', expr='v_peak_center')
Matt,Sorry for the late answer, my university moved to a new campus and there are still a few glitches to fix here, like internet access...
Following your example, you should be able to say:params['Step_center'].set(expr='g_peak_center')or even
p2 = Parameter('Step_center', expr='g_peak_center')
this will force 'Step_center' to have the same value as 'g_peak_center'. The expression held in a Parameter's 'expr' can be nearly any valid python expression written in terms of the other parameter names and common Python / numpy functions and values.
Thanks for showing me how to do that. It works like a charm. In order to make it more flexible, I was wondering whether it would be possible to call for the prefix that is defined when the function is called.That is, if instead of :
asym_model, params = AsymModel(GaussianModel(prefix='g_peak_')I use a VoigtModel and therefore call this function with
asym_model, params = AsymModel(VoigtModel(prefix='v_peak_')Then I would have to change
p2 = Parameter('Step_center', expr='g_peak_center')into
p2 = Parameter('Step_center', expr='v_peak_center')within the function.
I tried to understand how to get this value, but I could not make use of "VoigtModel.prefix" as it appears to be a "property" which I don't know how to handle.A workaround would be to impose to call "AsymModel" with the prefix as a variable, to assign the prefix to the peak within the function and to use it to impose the two centers to be identical but this is not very elegant and seems a bit un-natural to me.Any hint ?
def AsymPeak(form, form_prefix):
""" Creates an asymmetric version of the model and initialises the parameters """
model_name = form+'Model' p11 = Parameter('Step_amplitude', 1.0)
p21 = Parameter('Step_center', expr=form_prefix+'_center') p31 = Parameter('Step_sigma', 1.0)
params1 = Parameters() params1 += model_name.guess(data=y, x=x) params1 += pars_bkd params1.add_many(p11, p21, p31) return model_name(prefix=form_prefix) * StepModel(form='erf', prefix='Step_'), params1
Thanks-Pierre
--
You received this message because you are subscribed to the Google Groups "lmfit-py" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+unsubscribe@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.
To view this discussion on the web visit https://groups.google.com/d/msgid/lmfit-py/c4880a8b-31ec-4147-a7d3-34b76ad090bc%40googlegroups.com.
In fact, there is a skewed Voigt function in lineshapes.py (skewed_voigt(), see https://github.com/lmfit/lmfit-py/blob/master/lmfit/lineshapes.py#L230), though it's not exposed as a builtin Model -- that could be fixed easily.
Thanks-Pierre
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+u...@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.
To view this discussion on the web visit https://groups.google.com/d/msgid/lmfit-py/c4880a8b-31ec-4147-a7d3-34b76ad090bc%40googlegroups.com.
--Matt
Hello Matt,Far up in this thread, you wrote:In fact, there is a skewed Voigt function in lineshapes.py (skewed_voigt(), see https://github.com/lmfit/lmfit-py/blob/master/lmfit/lineshapes.py#L230), though it's not exposed as a builtin Model -- that could be fixed easily.Could this be done? I would like to use a skewed Voigt model, and for the sake of easy collaboration with my coworkers (we are all new to python), it would be nice to have the builtin available.
Parameters([('t_skew', <Parameter 't_skew', 0.0, bounds=[-inf:inf]>),
('t_sigma', <Parameter 't_sigma', 1.0, bounds=[-inf:inf]>),
('t_center', <Parameter 't_center', 0.0, bounds=[-inf:inf]>),
('t_amplitude', <Parameter 't_amplitude', 1.0, bounds=[-inf:inf]>)])
Hi Matt,Thank you for the fast reply! Sadly I cannot get the ad-hoc version to work: the gamma parameter seems to be missing from the parameters dictionary.Here is what i've done:> mod = Model(skewed_voigt, prefix='t_')> modpars = mod.make_params()> modparsParameters([('t_skew', <Parameter 't_skew', 0.0, bounds=[-inf:inf]>),
('t_sigma', <Parameter 't_sigma', 1.0, bounds=[-inf:inf]>),
('t_center', <Parameter 't_center', 0.0, bounds=[-inf:inf]>),
('t_amplitude', <Parameter 't_amplitude', 1.0, bounds=[-inf:inf]>)])So, when I attempt to set an initial value and min/max for gamma, I get a KeyError.