3
$\begingroup$

Imagine I'm running a loop that's manipulating and changing elements in an array. Is it possible for me to create a ListPlot output in my notebook that updates on command?

I'm imagining taking something like this:

testArray = Array[{0, 0} &, 10^3];
For[i = 1, i <= 10^3, i++,

  testArray[[i]] = {i, i};

  If[Mod[i, 10] == 0,
   Print[ListPlot[testArray, PlotRange -> All]];
  ];

];

And transforming it into something like this (with the imaginary command fTriggerUpdateForPreviouslyExistingListPlot):

testArray = Array[{0, 0} &, 10^3];
For[i = 1, i <= 10^3, i++,

  testArray[[i]] = {i, i};

  If[Mod[i, 10] == 0,
   fTriggerUpdateForPreviouslyExistingListPlot[{testArray}];
  ];

];

Here, fTriggerUpdateForPreviouslyExistingListPlot is some kind of switch that takes a listplot, say, in another notebook (within the context of the same kernel) or the current one, and plots the new data.

Is this possible?


The function Monitor comes close to what I'm looking for:

testArray = Array[{0, 0} &, 10^3];

Monitor[
   For[i = 1, i <= 10^3, i++,
     Pause[0.1];
     testArray[[i]] = {i, i};
   ];
,
ListPlot[testArray, PlotRange -> All]];

ListPlot[testArray, PlotRange -> All]

However, this updates whenever testArray changes, and not on a trigger. I could create a separate testArrayForMonitor and move data from testArray to testArrayForMonitor to trigger an update, but this seems a bit of an odd thing to do, and incurs a significant overhead.

$\endgroup$
  • $\begingroup$ I wouldn't recommend this, but you may try Dynamic[ListPlot[testArray, PlotRange -> All], TrackedSymbols -> p] testArray = Array[{0, 0} &, 10^3]; p = 0; For[i = 1, i <= 10^3, i++, testArray[[i]] = {i, i}; Pause[.01]; p = Mod[i, 10];]; $\endgroup$ – Dr. belisarius Sep 23 '13 at 3:43
  • $\begingroup$ The tracked symbol change triggers the update $\endgroup$ – Dr. belisarius Sep 23 '13 at 4:01
  • $\begingroup$ @belisarius Yup, I just realized that after I posted my comment. =) $\endgroup$ – RM1618 Sep 23 '13 at 4:09
  • $\begingroup$ You could also use PrintTemporary[ListPlot[...]] function in your loop $\endgroup$ – Vahagn Poghosyan Sep 23 '13 at 4:15
3
$\begingroup$

It is not entirely clear to me whether my solution is OK, because I don't know what you mean by

Is it possible for me [..] that updates on command?

Clearly, if the Kernel is busy evaluating one expression then other evaluations are usually queued. You can use a DynamicModule to update a graphic through a button or in a specific time interval. A simple example would be the following

testArray = Array[{0, 0} &, 10^3];
DynamicModule[{gr, update},
 update[] := gr = ListPlot[testArray, PlotRange -> All];
 update[];
 Panel[Column[{
    Button["Refresh", update[]],
    Dynamic[gr]
    }]
 ]
]

And then start you computation

For[i = 1, i <= 10^3, i++,
 Pause[.05];
 testArray[[i]] = {i, Sin[.01 i]*i};
]

Mathematica graphics

This should work with different kinds of triggers.

Edit:

To trigger it from inside the For loop, you can simply set a fake trigger variable and update the plot accordingly:

trigger = 0;
testArray = Array[{0, 0} &, 10^3];

Dynamic[(trigger; 
  ListPlot[testArray, PlotRange -> {{0, 1000}, {-500, 500}}]), 
 TrackedSymbols :> {trigger}]

For[i = 1, i <= 10^3, i++,
 Pause[.01];
 testArray[[i]] = {i, Sin[.01 i]*i};
 If[Mod[i, 50] === 0, trigger++]
 ]
$\endgroup$
  • $\begingroup$ Oh terrific - how can I trigger the refresh with a line of code in the For loop? I'd like to refresh for Mod[i,10]==0 values, for example. $\endgroup$ – RM1618 Sep 23 '13 at 7:54
  • $\begingroup$ @RM1618 See my latest edit in the answer for this. $\endgroup$ – halirutan Sep 23 '13 at 8:10
  • $\begingroup$ Just what I was looking for. Why is it trigger++ instead of something like trigger+=1? $\endgroup$ – RM1618 Sep 23 '13 at 8:22
  • $\begingroup$ @RM1618 It really doesn't matter. trigger needs to be changed; that is the important thing. You can also use trigger=i. $\endgroup$ – halirutan Sep 23 '13 at 8:27
1
$\begingroup$

I like halirutan's answer, and its performance is superior because it avoids excessive refreshing, but I find it valuable to explore other approaches since there almost always is another in Mathematica, so here is a method that does not require modifying your loop, working much as Monitor does:

SetAttributes[lagDynamic, HoldAll]

lagDynamic[expr_, cond_] :=
  DynamicModule[{n, out = "waiting..."},
    Dynamic @ If[n != (n = cond), out = expr, out]
  ]

Example of use, borrowing halirutan's code:

lagDynamic[
  ListPlot[testArray, PlotRange -> {{0, 1000}, {-500, 500}}],
  Quotient[i, 50] (* refresh when this value changes *)
]

testArray = ConstantArray[0, {10^3, 2}];
Do[Pause[0.01]; testArray[[i]] = {i, Sin[.01 i]*i};, {i, 10^3}]

I think this is more convenient than the syntax you requested.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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