John Hunter wrote:

> The fundamental problem here is that some artists (Line2D) have
> support for storing original unitized  data (_xorig, _yorig) and
> handling the conversion on unit change internally with the callback,
> and some artists (eg Patches) do not .  axes._process_plot_var_arg
> subserves both plot (Line2D) and fill (Polygon), one of which is
> expecting possibly unitized data and one which is only capable of
> handling already converted data.  Hence the fix one problem, create
> another bind we are now in.
> 
> So yes, we need a standard.

John,

As you know, I agree.  This has been a frustrating problem for a long 
time.

> 
> I think the resolution might be in having intermediate higher level
> container plot item objects (an ErrorBar, LintPlot, FilledRegion)
> which store the original data, manage the units, and pass converted
> data back to the primitives.  This is obviously a major refactoring,
> and would require some thought, but may be the best way to go.
> Handling the conversions in the plotting functions (eg fill, errorbar)
> is probably not the right way because there is no obvious way to
> support unit changes (eg inches to cm) since the data is already
> converted, the artists already created.

I'm not sure I understand the use case for unit *changes*, as opposed to 
initial unit specification.

> 
> Having the artist primitives store the original, possibly unitized
> data, and register callbacks for unit changes can work, but the
> problem is how to create the artist primitives in such a way the unit
> data is passed through correctly.  The problem here is that some
> operations don't make sense for certain unit types -- think addition
> with datetimes.  Some functions, eg bar or errorbar, which need to do
> a lot of arithmetic on the input arrays, may want to do:
> 
>   xmid = 0.5*(x[1:] + x[:-1])
> 
> which would not work for x if x is datetime (can't add two dates).
> distance and scaling should always be well defined, so one should be
> able to do:
> 
>   xmid = x[1:] + 0.5*(x[1:]-x[:-1])
> 
> So one solution is to require all plotting functions to respect the
> "no addition" rule, ie define the set of operations that are allowed
> for plotting functions, and all artists to handle original unitized
> data with internal conversion.  This is a fair amount of work at the
> plotting function layer, is invasive to the artist primitives, and
> requires the extra storage at the artist layer, but could work.

Sounds horrible to me.  I would really like to see clear stratification, 
with all complicated and flexible argument handling restricted to some 
not-too-low level.

> 
> The other solution, what I referred to as the intermediate plot item
> container, is to have a class ErrorBar, eg, which is like the errorbar
> method, but has an API like
> 
>   class ErrorBar:
>     def __init__(self, all the errorbar args, possibly unitized):
>       self.store_all_original_data_here()
>       self.store_all_primitives_from_converted_data_here()
> 
>       def callback():
>           self.update_all_stored_primitives_newly_converted_original_data()
>       self.connect_callback_to_unit_change(callback)
> 
> 
> This has the advantage that the plot item container class can always
> work with arrays of floats (removing the onerous restriction on what
> kind of binary relations are allowed) and removes the restrictions on
> creating artists which are unit aware.

I think something like this is the way to go.  Even without the problem 
with units, I would like to see things like the bar family, errorbar, 
and boxplot moved out into their own classes; and there is no reason not 
to do the same for simple line plots (which are anything but simple in 
their input argument handling).  Then the Axes class can concentrate on 
Axes creation and manipulation.

I think there are also opportunities for factoring out common operations 
involving input parameter handling--not just units conversion, but 
validation, checking dimensions, generating X and Y with meshgrid when 
needed, etc.  Some of these things are already partly factored out, but 
helpers are scattered around, and I suspect there is some unproductive 
duplication.

Of course, the big question is how to get all this done... Fortunately, 
unless I am missing a key point, this sort of refactoring can be done 
incrementally; it is not as drastic as the transforms refactoring was.

Eric

> 
> It also makes for a nicer API:
> 
>   eb = ErrorBar(something)
>   eb.draw()
> 
>   # hmm, the cap widths are too small
>   eb.capwidth = 12
>   eb.draw()
> 
> ie, instead of getting back a bunch of artist primitives from errorbar
> which may be difficult to manipulate, you get back an ErrorBar object
> that knows how to update and plot itself.
> 
> With traits or properties so that the eb.capwidth attr setting
> triggers a unitized updating of primitives, then everything is fairly
> transparent to the user.
> 
> It would also make it easier support containers of artists for logical
> groupings during animation, zorder buffering/blitting, etc.
> 
> 
> 
> JDH

------------------------------------------------------------------------------
Crystal Reports - New Free Runtime and 30 Day Trial
Check out the new simplified licensing option that enables 
unlimited royalty-free distribution of the report engine 
for externally facing server and web deployment. 
http://p.sf.net/sfu/businessobjects
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel

Reply via email to