Re: Suggestion: Allow notebook scripts to access replot.PlotResult.widget.axes

0 views
Skip to first unread message

Owen Taylor

unread,
Aug 11, 2008, 7:57:15 AM8/11/08
to reint...@googlegroups.com
jonkuhn writes:

> I have found this patch useful for data analysis using Reinteract so
> that I can modify plots by changing the x and y scales, adding
> gridlines, adding titles and axis lables, etc. Basically, it just
> creates the plot in the PlotResult constructor, and just returned by
> create_widget (not actually created within it) when it needs to be
> displayed in the notebook.
>
> So in the notebook file it looks like this:
>
> w=plot(x, cos(x), 'r--', # red dashed lines
> x, sin(x), 'bo') # blue circles
> a=w.widget.axes.legend(['cos(x)','sin(x)'], pad=0.1)
> a=w.widget.axes.set_title('Y vs X')
> a=w.widget.axes.set_xlabel('X Axis Label')
> a=w.widget.axes.set_ylabel('Y Axis Label')
> a=w.widget.axes.set_position((0.1,0.1,0.85,0.85))
> a=w.widget.axes.set_xlim(0,7)
> a=w.widget.axes.set_ylim(-1,1)
> a=w.widget.axes.set_xticks(linspace(0,7,8))
> a=w.widget.axes.set_yticks(linspace(-1,1,5))
> a=w.widget.axes.grid(True)
> w
> #The plot doesn't display until here

The problem with the above is that it counts on side effects ... we
are continually modifying the axes object. Normally reinteract would
try to make a copy of the 'w' object in this case, which would blow up
since it's not a "pure data" object that can be easily copied. You've
worked around that by using the dummy assignments.. reinteract guesses
that a=b.foo() doesn't modify b, but there are still problems ... if
you, say, deleted one of those lines, then the effect of the deleted
line would still remain.

I've thought about this some in this particular context.

1. We could extend the plot command to allow setting more stuff in a
single command:

plot(x, cos(x), 'r--',
x, sin(x), 'bo',
legend=['cos(x)', 'sin(x)'],
title='Y vs X'
...)

This is no longer strictly compatible with the matplotlib plot()
command (in matplotlib, kwargsfor plot set line parameters), but I
don't think that matters much. The cool thing about this is that it is
easily extensible to allowing interactive manipulation of the result
widget. The result object could be provided a handle that points back
to the statement that created it. Then as the user interactively, say,
changed the title, the creation statement would be updated to match.

2. We could add setters on the custom result object for these things.
Since the custom result object *can* be copied, there is no longer a
problem with side effects.

r = plot(x, cos(x), 'r--', x , sin(x), 'bo')
r.set_legend(['cos(x)', 'sin(x)')
r.set_title('Y vs. X')
...
r

3. You could group all the side effects into a single chunk like:

if True:
w=plot(x, cos(x), 'r--', # red dashed lines
x, sin(x), 'bo') # blue circles
w.widget.axes.legend(['cos(x)','sin(x)'], pad=0.1)
w.widget.axes.set_title('Y vs X')
w

Ugly, and requires the user to understand a lot about how reinteract
works internally.

4. reinteract could be smarter about non-copyable objects. If it knows
that an object is being mutated that it can't make a copy of, it could
automatically consider the whole series of statements from the
creation to the last mutator as a single logical group, so if you edit
one line in it, it re-exectutes all the lines. (See also
http://www.reinteract.org/trac/ticket/38)

Some work on 4. is going to be necesary in the long run, but the
ability to extend to interactive editing I think makes 1. a more
interesting approach for this particular problem. Does this make any
sense for the other types of result widgets you have created?

- Owen

jonkuhn

unread,
Aug 12, 2008, 1:46:14 PM8/12/08
to Reinteract
> 1. We could extend the plot command to allow setting more stuff in a
> single command:
>
>   plot(x, cos(x), 'r--',
>          x, sin(x), 'bo',
>          legend=['cos(x)', 'sin(x)'],
>          title='Y vs X'
>          ...)
>
This is basically my current solution I defined a plotEx function that
takes
arguments like this. Internally the plotEx function does depend on my
hack
of being able to reach inside PlotResult and modify widget.axes.
However,
if the handling of these arguments was moved into
PlotResult.__init__(),
plot() could just take these arguments directly.

> 2. We could add setters on the custom result object for these things.
> Since the custom result object *can* be copied, there is no longer a
> problem with side effects.
>
>  r = plot(x, cos(x), 'r--', x , sin(x), 'bo')
>  r.set_legend(['cos(x)', 'sin(x)')
>  r.set_title('Y vs. X')
>  ...
>  r

> 4. reinteract could be smarter about non-copyable objects. If it knows
> that an object is being mutated that it can't make a copy of, it could
> automatically consider the whole series of statements from the
> creation to the last mutator as a single logical group, so if you edit
> one line in it, it re-exectutes all the lines. (See alsohttp://www.reinteract.org/trac/ticket/38)

I think this is a feature that is eventually required. With the
DataSet
data analysis class I created I have methods like appendColumn() that
can return errors if the user goes back an edits some like above the
call to that method but not above the creation of the DataSet object
it
is being called on, since the column it is trying to append will
already
exist. But this may be just that Reinteract doesn't know that the
appendColumn method is modifying the DataSet object, and thus
doesn't copy it, because the DataSet objects can be copied by
copy.deepcopy, and such.

> Some work on 4. is going to be necesary in the long run, but the
> ability to extend to interactive editing I think makes 1. a more
> interesting approach for this particular problem. Does this make any
> sense for the other types of result widgets you have created?

The only other result widget I created was one to display one of my
DataSet objects in a table. Currently this table is read only, and
it
is a little counter intuitive to the user since sorting by columns of
the table does not really modify the DataSet itself.

The tables are a little different from the case of plotting in that my
table() function takes a DataSet object as a parameter then builds
a GTK TreeView based on the data within it and displays that by
returning a TableResult that contains a TableWidget. So in this
case if you interactively modified the table it is not clear if the
DataSet itself should be modified or if the call to table() should
be modified to *display* the table in the way the user has
interactively sorted it. If I were to allow the table's data to be
edited, I think that would only really make sense if these changes
propagated to the DataSet object. Although, my DataSet
object currently does not support modifying individual cells anyway.

If you are interested in seeing my table or DataSet code, let me know
and I can send you the latest versions.

-Jon

Owen Taylor

unread,
Aug 13, 2008, 8:09:57 PM8/13/08
to reint...@googlegroups.com
On Tue, Aug 12, 2008 at 1:46 PM, jonkuhn <jon...@gmail.com> wrote:

>> 4. reinteract could be smarter about non-copyable objects. If it knows
>> that an object is being mutated that it can't make a copy of, it could
>> automatically consider the whole series of statements from the
>> creation to the last mutator as a single logical group, so if you edit
>> one line in it, it re-exectutes all the lines. (See alsohttp://www.reinteract.org/trac/ticket/38)
>
> I think this is a feature that is eventually required. With the
> DataSet data analysis class I created I have methods like appendColumn()
> that can return errors if the user goes back an edits some like above the
> call to that method but not above the creation of the DataSet object
> it is being called on, since the column it is trying to append will
> already exist. But this may be just that Reinteract doesn't know that the
> appendColumn method is modifying the DataSet object, and thus
> doesn't copy it, because the DataSet objects can be copied by
> copy.deepcopy, and such.

Reinteract uses a shallow copy on the objects. While a deep copy is
more correct,
if the mutation is a "shallow" ... something like Array.append() then
you are paying
a big penalty in memory usage and time unnecessarily.

My thought here is that (at least for modules we control), we could us
decorators
on methods:

@reinteract.const
define get_something(self): ...

@reinteract.shallow_copy
define set_member(self): ...

@reinteract.deep_copy
define change_stuff(self):...

You could also imagine specifying a particular copy member function to use for a
particular method.

>> Some work on 4. is going to be necesary in the long run, but the
>> ability to extend to interactive editing I think makes 1. a more
>> interesting approach for this particular problem. Does this make any
>> sense for the other types of result widgets you have created?
>
> The only other result widget I created was one to display one of my
> DataSet objects in a table. Currently this table is read only, and
> it is a little counter intuitive to the user since sorting by columns of
> the table does not really modify the DataSet itself.

Hmm, non-modifcation much what I'd expect, but maybe I'm just too much
of programmer :-)

> The tables are a little different from the case of plotting in that my
> table() function takes a DataSet object as a parameter then builds
> a GTK TreeView based on the data within it and displays that by
> returning a TableResult that contains a TableWidget. So in this
> case if you interactively modified the table it is not clear if the
> DataSet itself should be modified or if the call to table() should
> be modified to *display* the table in the way the user has
> interactively sorted it. If I were to allow the table's data to be
> edited, I think that would only really make sense if these changes
> propagated to the DataSet object. Although, my DataSet
> object currently does not support modifying individual cells anyway.

Again I'd be surprised if editing was possible or if it changed the dataset.
But I think there is some room for "interactive inputs". For example, I was
creating some worksheets earlier to analyze sound files. For a worksheet
like that, it would be neat to be able to pick different sound files with a
file chooser, then interactively select the segment of sound you wanted
to analyze.

I'm not exactly sure how that would work. It could be something in the
code like:

sound = load_sound('foo.wav', 0.3, 0.512);

where the loud_sound statement both a) loads the sound b) knows how to find the
currently executing reinteract statement and add a control underneath it

[ Foo.wav ] [ Browse ]
Start [ 5.3 ] seconds End [ 11.4 ] seconds [ Play ]

Where as you adjusted the controls the text of the statement chnaged.
Or maybe it's
split out so that you declare the dependencies for a reinteract
worksheet in some sort
of magic comment at the top of the worksheet and controls are added above the
worksheet to load the data for the worksheet before the start of the code of the
worksheet.

> If you are interested in seeing my table or DataSet code, let me know
> and I can send you the latest versions.

I took a brief look earlier at the versions you posted to
http://www.reinteract.org/trac/ticket/33, but it wasn't immediately clear to me
offhand how they were intended to be used. Do you have a screenshot
or example worksheet (real or artificial) that gives an example of how
they work?

- Owen

jonkuhn

unread,
Aug 14, 2008, 12:25:58 AM8/14/08
to Reinteract
> Reinteract uses a shallow copy on the objects. While a deep copy is
> more correct,
> if the mutation is a "shallow" ... something like Array.append() then
> you are paying
> a big penalty in memory usage and time unnecessarily.

Yeah that makes sense, I was just pointing out that my object could be
copied by
the functions in the copy module.

> My thought here is that (at least for modules we control), we could us
> decorators
> on methods:
>
> @reinteract.const
> define get_something(self): ...
>
> @reinteract.shallow_copy
> define set_member(self): ...
>
> @reinteract.deep_copy
> define change_stuff(self):...
>
> You could also imagine specifying a particular copy member function to use for a
> particular method.

Yeah this would definitely be ideal for Reinteract-aware classes.


> Hmm, non-modifcation much what I'd expect, but maybe I'm just too much
> of programmer :-)

Yeah I understand why it doesn't modify it, and I had no expectation
of
it modifying it, since I wrote the DataSet and TableWidget code and
didn't
implement anything that would cause modification. :-).

I was just speculating that perhaps a user who is less of a
programmer (maybe just knows enough to do some data analysis
ala MATLAB/Mathematica) may be confused. I think someone
did ask me once why they couldn't edit the data in the table view,
and I just said it was a read only view of the data. It really
doesn't
make sens to interactively modify the dataset itself anyway,
especially
not within the result widget of my table() function.

> Again I'd be surprised if editing was possible or if it changed the dataset.
> But I think there is some room for "interactive inputs". For example, I was
> creating some worksheets earlier to analyze sound files. For a worksheet
> like that, it would be neat to be able to pick different sound files with a
> file chooser, then interactively select the segment of sound you wanted
> to analyze.

> I'm not exactly sure how that would work. It could be something in the
> code like:
>
> sound = load_sound('foo.wav', 0.3, 0.512);
>
> where the loud_sound statement both a) loads the sound b) knows how to find the
> currently executing reinteract statement and add a control underneath it
>
> [ Foo.wav ] [ Browse ]
> Start [ 5.3 ] seconds End [ 11.4 ] seconds [ Play ]
>
> Where as you adjusted the controls the text of the statement chnaged.
> Or maybe it's
> split out so that you declare the dependencies for a reinteract
> worksheet in some sort
> of magic comment at the top of the worksheet and controls are added above the
> worksheet to load the data for the worksheet before the start of the code of the
> worksheet.

In my data analysis worksheets I usually use a quick-and-dirty
function that I wrote
to display a file chooser and have the user select the CSV file they
want to
analyze. The function just returns the filename. The worksheet just
assigns it
to a variable and then opens the file later. The function is blocking
so the user
has to pick something in order for the rest of the worksheet to be
useful. It would
be neat to be able to insert a browse button into the worksheet (like
you describe)
that they could click on when they wanted rather than forcing them to
select a
file when the worksheet loads (maybe there is a default file, but the
user can
change it if they want to).

Basically I think there are two types of interactivity that may be
interesting within
worksheets:

One would be what you described before where interactively adding a
title
to a plot modifies the plot(x, y) statement to become plot(x, y,
title='Y vs X').
This would require the widget in the result object to be able to
access and modify
its "parent" statement in reinteract (the statement that returned the
result that
is being interacted with). This just makes it more user friendly to
add titles to
plots and that without directly modifying code. If these types of
interactions
only modified the display of the result object, the statement editing
could
probably be performed without having to mark any lines in the
worksheet as dirty.

Another type of interactivity may be "worksheet controls" for
situations where
you want to display a "Browse..." button in the worksheet to allow the
user to
change what file is being opened. Or to give the user a "pretty" way
to change
variables/parameters in the worksheet. When the user modifies these
controls
all of the lines below them in the worksheet would end up being marked
dirty
since the parameters the user is changing may have cascading effects
further
down in the worksheet. When they were done modifying something they
would
recalculate.

I think most interactive actions we have talked about fall into these
categories.

Interactively modifying a DataSet objects themselves (which I hinted
at
before) probably does not make any sense at all the more I think about
it. It
would end up being very messy and not really useful.

> I took a brief look earlier at the versions you posted tohttp://www.reinteract.org/trac/ticket/33, but it wasn't immediately clear to me
> offhand how they were intended to be used. Do you have a screenshot
> or example worksheet (real or artificial) that gives an example of how
> they work?

I when I get a chance, l put together an example worksheet and a
screenshot and send them to you along with the latest copies of the
classes, so you can see what they are used.

- Jon

jonkuhn

unread,
Aug 14, 2008, 12:29:30 AM8/14/08
to Reinteract
Oops, I made my post all ugly with manual line breaks.

Owen Taylor

unread,
Aug 14, 2008, 9:02:51 PM8/14/08
to reint...@googlegroups.com
On Thu, Aug 14, 2008 at 12:25 AM, jonkuhn <jon...@gmail.com> wrote:
[...]

>
> In my data analysis worksheets I usually use a quick-and-dirty
> function that I wrote to display a file chooser and have the user select the
> CSV file they want to analyze. The function just returns the filename. The
> worksheet just assigns it to a variable and then opens the file later. The function
> is blocking so the user has to pick something in order for the rest of
> the worksheet to be useful.

Yeah, that works for something quick.

That exactly mirrors my thoughts on the issue.

> Interactively modifying a DataSet objects themselves (which I hinted
> at before) probably does not make any sense at all the more I think about
> it. It would end up being very messy and not really useful.

I think there's a separate potential feature where you can add data files
to a notebook as a "data library". Then there might be grid views for entering
and editing data there separate from worksheets.

>> I took a brief look earlier at the versions you posted to
>> http://www.reinteract.org/trac/ticket/33, but it wasn't immediately clear to me
>> offhand how they were intended to be used. Do you have a screenshot
>> or example worksheet (real or artificial) that gives an example of how
>> they work?
>
> I when I get a chance, l put together an example worksheet and a
> screenshot and send them to you along with the latest copies of the
> classes, so you can see what they are used.

Cool.

- Owen

Reply all
Reply to author
Forward
0 new messages