2
$\begingroup$

I am having a go at writing my first package and would like to do something similar to the following. Its a basically a very thin wrapper around NonlinearModelFit.

BeginPackage["TestPackage`"]

gaussianFit::usage="Fits to a gaussian and returns list of best fit parameters.";

Begin["`Private`"]

  gaussianFit[data_,Aest_, x0est_, σest_,y0est_]:= NonlinearModelFit[
    data
  , A E^(-(1/2) ((x-x0)/σ)^2)+y0
  , { {A,Aest},{x0, x0est},{σ,σest},{y0, y0est} }
  , x
  ]["BestFitParameters"]

End[]
EndPackage[]

I would like the function to return a list of rules such as

{A->1, x0->1, σ->1, y0->1}

but what actually gets returned is

 { TestPackage`Private`A -> -5.09821,  TestPackage`Private`x0 -> 4.1642, 
   TestPackage`Private`σ -> 1.29237,  TestPackage`Private`y0 -> 2.25453}

I've read various things about fixing this by making the variables global but that doesn't seem like the right thing to do since I don't want to be able to globally overwrite them to something else.

Is there a way to achieve what I want?

$\endgroup$
5
  • 3
    $\begingroup$ I'd say the stable way is to accept {symbol, symbolEst}... pairs instead of only the value. See: Derivative from my package function returns 0. Or you can return Values @ ... and associate them with symbols later or not at all. $\endgroup$
    – Kuba
    Jan 25, 2018 at 10:05
  • 1
    $\begingroup$ I agree with Kuba. The best solution is to pass these symbols into your function in some way, not to have them predefined. If you really want predefined symbol names, then you should make them public by mentioning them after BeginPackage[...] (and probably also make them Protected), and accept that you won't be able to use these symbols for something else after loading the package. $\endgroup$
    – Szabolcs
    Jan 25, 2018 at 10:59
  • $\begingroup$ I was steering away from just returning a list of the best fit parameters as my real case is more complex (more parameters) and keeping track of what each parameter is in a long list is quite difficult. Accepting the symbols is possible but I actually use the data to make intelligent guesses for some parameters so the user doesn’t supply them which again makes it feel a bit awkward. $\endgroup$
    – lac
    Jan 25, 2018 at 13:22
  • $\begingroup$ Is there any reason for returning these labels as symbols? My suggestion would be to return them as strings which get rid of the namespace issues and seem to provide any functionality you are after. If you don't need to support version < 10 you could then also return an Association instead of a list of rules which might make some opperations more elegant and efficient... $\endgroup$ Feb 24, 2018 at 22:12
  • $\begingroup$ Association is what I have actually gone with in the end. It works quite nicely and as you say makes some operations work more smoothly $\endgroup$
    – lac
    Feb 24, 2018 at 23:05

1 Answer 1

1
$\begingroup$

This might not be satisfactory to you because it requires an extra step in the Global` context after the package function gaussianFit returns its result, but I think you should consider it.

BeginPackage["TestPackage`"];
gaussianFit::usage = 
  "Fits to a gaussian and returns list of best fit parameters.";
Begin["`Private`"];
gaussianFit[data_, Aest_, x0est_, σest_, y0est_] :=
  Module[{params}, 
    params = 
      NonlinearModelFit[
        data, 
        A E^(-(1/2) ((x - x0)/σ)^2) + y0, 
        {{A, Aest}, {x0, x0est}, {σ, σest}, {y0, y0est}}, x]["BestFitParameters"];
    StringDelete[ToString[params], "TestPackage`Private`"]]
End[];
EndPackage[];

data =
  Table[
    {x, E^(-((x - μ)^2/(2. σ^2))) + 2.)},
    {x, Subdivide[μ - 3 σ, μ + 3 σ, 50]}] /. {μ -> 1., σ -> 0.5};

rules = (gaussianFit[data, .95, 1.1, .55, 2.] // ToExpression)

{A -> 1., x0 -> 1., σ -> 0.5, y0 -> 2.}

Plot[A E^(-(1/2) ((x - x0)/σ)^2) + y0 /. rules, {x, -1, 3},
  Epilog -> {Red, AbsolutePointSize[5], Point[data]}]

plot

$\endgroup$
4
  • $\begingroup$ Let's say you call Needs["whateverElse`"]; inside your package because you need it. If that package introduces e.g. whateverElse`A it will now be on context path. And your output will be: {whateverElse`A -> 1.`, x0 -> 1.`, \[Sigma] -> 0.5`, y0 -> 2.`}. Packages should not export dummy names but you never know. A way to simulate it is to start with: $\endgroup$
    – Kuba
    Jan 25, 2018 at 13:20
  • $\begingroup$ BeginPackage["TestPackage`"]; gaussianFit; Needs["GeneralUtilities`"]; GeneralUtilities`A; Begin["`Private`"];... $\endgroup$
    – Kuba
    Jan 25, 2018 at 13:21
  • $\begingroup$ So it is a trade off I guess, flexibility for edge cases problems. $\endgroup$
    – Kuba
    Jan 25, 2018 at 13:25
  • $\begingroup$ @Kuba. Yes, far from a perfect solution. But there are often tradeoffs. The OP must decide whether or not it works in his/her paticular case. $\endgroup$
    – m_goldberg
    Jan 25, 2018 at 18:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.