2
$\begingroup$

I'm using Mathematica to generate some data, and I'm having some difficulty understanding how my intuition from "regular programming" should transfer over. The code that I'm writing does the following:

  • Generates a 2D plot, which is itself a function of several different parameters (call these parameters 'X').
  • Fits the plot to another function, and extracts the fit parameters (called 'A').
  • Writes both X and A to a file.

This whole process is a dozen or so lines of code (since generating the plot takes a few steps). I would like to repeat this process for many different instances of 'X'. In a more standard programming language, I would simply wrap the above three steps in a function of 'X', and then put it in a for loop that loops over different values of 'X.' My understanding of Mathematica is that for loops are discouraged, and the 'Do' function is more proper. However, all the uses of Do that I could find usually just involve repeated evaluations of a single expression, rather than several lines of code that must be executed sequentially. What is the "correct" way of accomplishing the above task in Mathematica?

EDIT: Here is a minimal example of what I want to do:

H[a_, b_, c_, x_] := a x^2 + b x + c;
a = 1;
b = 2;
c = 3;
fitData = Table[H[a, b, c, x], {x, 0, 20}];
fitParams = FindFit[fitData, d x^2 + f x + g, {d, f, g}, x];
data = {a, b, c, d, f, g} /. fitParams;
Export[data, "data.txt"]

and in this case, I would like to loop over many different values of a, b, and c.

$\endgroup$
3
  • $\begingroup$ It's always easier to answer a question that contain an actual minimal working example in Mathematica code in formatted form. $\endgroup$
    – rhermans
    Commented Jul 10, 2019 at 14:57
  • $\begingroup$ You can use CompoundExpression with semicolons anywhere in place of a single expression. $\endgroup$
    – Somos
    Commented Jul 10, 2019 at 15:02
  • $\begingroup$ You can "wrap the above three steps in a function" with findData[a_, b_, c_] := Module[{fitData, fitParams, d, f, g, x}, fitData = Table[H[a, b, c, x], {x, 0, 20}]; fitParams = FindFit[fitData, d x^2 + f x + g, {d, f, g}, x]; {a, b, c, d, f, g} /. fitParams] -- Well, your example code didn't show a 2D Plot[] per se, so I'm assuming the Table[] is what you meant. And I skipped the Export, since I think separating the data generation from the I/O makes handling the data more flexible.. $\endgroup$
    – Michael E2
    Commented Jul 11, 2019 at 15:33

2 Answers 2

5
$\begingroup$

This is one way that you might write such a loop:

list = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
i = 1;
Do[
  H[a_, b_, c_, x_] := a x^2 + b x + c;
  {a, b, c} = values;
  fitData = Table[H[a, b, c, x], {x, 0, 20}];
  fitParams = FindFit[fitData, d x^2 + f x + g, {d, f, g}, x];
  data = {a, b, c, d, f, g} /. fitParams;
  Export["~/Desktop/data" <> ToString[i] <> ".txt", data];
  i++,
  {values, list}
]

I've defined list to be 3 sets of parameters that become a, b, and c. The values variable in the Do loop will take on the value of each successive element of list as the loop repeats. In this case, it will be {1, 2, 3} and then {4, 5, 6}, etc. I've included the i as a simple counter so that the data.txt file doesn't continuously get overwritten. The other change I made was that Export expects the file location to come first, and the thing to export comes second.

Another way to write it might be:

list = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
Do[
  H[a_, b_, c_, x_] := a x^2 + b x + c;
  {a, b, c} = list[[i]];
  fitData = Table[H[a, b, c, x], {x, 0, 20}];
  fitParams = FindFit[fitData, d x^2 + f x + g, {d, f, g}, x];
  data = {a, b, c, d, f, g} /. fitParams;
  Export["~/Desktop/data" <> ToString[i] <> ".txt", data],
  {i, Length[list]}
]

They're pretty well equivalent, though. For your example, a For loop would also probably work just as well, but with a Do loop there is the potential to use ParallelDo instead.

$\endgroup$
5
$\begingroup$

The simplest way is to just use Table to iterate over all permutations of parameters. For example, here I iterate over all permutations {a, b, c} with each variable ranging from 1 to 3:

H[a_, b_, c_, x_] := a x^2 + b x + c;

paramTable = Flatten[ 
    (* 
       Table generates 1 extra dimension per parameter you iterate over, 
        so Flatten the output down to a 2D array 
    *)
  Table[
   Module[{fitData, fitParams}, (* localize the intermediate results *)
    fitData = Table[H[a, b, c, x], {x, 0, 20}];
    fitParams = FindFit[fitData, d x^2 + f x + g, {d, f, g}, x];
    {a, b, c, d, f, g} /. fitParams (* this is the return value that Table will store *)
   ],
   {a, 1, 3},
   {b, 1, 3},
   {c, 1, 3}
   ],
  2 (* because we iterate over 3 variables, we need to flatten twice to get a 2D array back *)
];

Export["data.txt", paramTable]

You can use ParallelTable instead of Table if you expect the array to become really large.

You can also replace Table with Do (or ParallelDo) and include the Export function at the end of the Module. It wasn't clear to me if you want to have lots of files or one big one that contains all results. When you use Do, you don't need Flatten either because Do has no return value.

$\endgroup$
3
  • 2
    $\begingroup$ Need to swap the arguments in Export... $\endgroup$
    – MelaGo
    Commented Jul 10, 2019 at 17:52
  • $\begingroup$ @MelaGo You're absolutely right. I still mess up the order of arguments of Export every once in a while. Thanks, I just fixed it. $\endgroup$ Commented Jul 10, 2019 at 22:27
  • $\begingroup$ A cute equivalent: Tuples[Unevaluated@findData[##], 3] & @@ Range[1, 3] where findData[a, b, c] computes the code block Module[..] of your` Table[]`. $\endgroup$
    – Michael E2
    Commented Jul 11, 2019 at 15:25

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.