Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Square plots #4310

Merged
merged 8 commits into from Jun 16, 2015
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions doc/users/whats_new/square_plot_feature.rst
@@ -0,0 +1,11 @@
Square Plot
-------------------------

Square plot:

Implemented square plots feature as a new parameter in the axis function. When argument 'square' is specified, equal scaling is set, and the limits are set such that xmax-xmin == ymax-ymin.

Example:

``ax.axis('square')``

68 changes: 59 additions & 9 deletions lib/matplotlib/axes/_base.py
Expand Up @@ -1286,26 +1286,68 @@ def apply_aspect(self, position=None):
self.set_xbound((x0, x1))

def axis(self, *v, **kwargs):
"""
Convenience method for manipulating the x and y view limits
and the aspect ratio of the plot. For details, see
:func:`~matplotlib.pyplot.axis`.
"""Set axis properties.

If no positional or kwargs are given, returns the current
axes limits are returned with no side effects.

If a positional argument is passed the keyword arguments are ignored.

If no positional argument is passed, the keyword arguments are used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these three explanations above be replaced or enhanced with 3 signatures? It's easier to understand

Valid signatures:
xmin, xmax, ymin, ymax = axis()
xmin, xmax, ymin, ymax = axis(list_arg)
xmin, xmax, ymin, ymax = axis(string_arg)
xmin, xmax, ymin, ymax = axis(**kwargs)

than the explanation of it. If something like this can't go in the block above, maybe examples can be included.


Parameters
----------
v : list of float or {'on', 'off', 'equal', 'tight', 'scaled',\
'normal', 'auto', 'image', 'square'}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you build the docs does this look ok?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the \ puts everything on one line. The only thing is the table size:
axis_doc

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table looks fine to me size wise. There are a lot of options so it
needs lot of descriptions.

On Sun, Apr 19, 2015 at 5:39 PM basharovV notifications@github.com wrote:

In lib/matplotlib/axes/_base.py
#4310 (comment):

  •    Convenience method for manipulating the x and y view limits
    
  •    and the aspect ratio of the plot. For details, see
    
  •    :func:`~matplotlib.pyplot.
    

axis`.

  •    """Set axis properties.
    
  •    If no positional or kwargs are given, returns the current
    
  •    axes limits are returned with no side effects.
    
  •    If a positional argument is passed the keyword arguments are ignored.
    
  •    If no positional argument is passed, the keyword arguments are used.
    
  •    Parameters
    

  •    v : list of float or {'on', 'off', 'equal', 'tight', 'scaled',\
    
  •     'normal', 'auto', 'image', 'square'}
    

Yes, the \ puts everything on one line. The only thing is the table size:
[image: axis_doc]
https://cloud.githubusercontent.com/assets/8410950/7221487/d00fd61c-e6ba-11e4-91d2-dbca6173cf28.png


Reply to this email directly or view it on GitHub
https://github.com/matplotlib/matplotlib/pull/4310/files#r28657228.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I was referring to the actual padding in the table. It's not consistent with the other tables, but that's just an aesthetic thing.

Optional positional argument

Value to set the axis settings to.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Value to..." -> "Axis data limits set from a list; or a command relating to axes."

This is all a horrible Matlab legacy, and consequently it's a very poor fit for numpydoc.

If a list it is interpreted as [xmin, xmax, ymin, ymax].
If a string

========== ===============================================
Value Description
========== ===============================================
'on' Toggle axis lines and labels on
'off' Toggle axis lines and labels off
'equal' Equal scaling by changing limits
'scaled' Equal scaling by changing box dimensions
'tight' Limits set such that all data is shown
'auto' Automatic scaling, fill rectangle with data
'normal' Same as 'auto'; deprecated
'image' 'scaled' with axis limits equal to data limits
'square' Square plot; both axis have equal limit range
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"axis" -> "axes" (plural)
Maybe reword: "Square plot; similar to 'scaled', but initially forcing xmax-xmin = ymax-ymin"

========== ===============================================

emit : bool, optional
Passed to set_{x,y}lim functions, if observers
are notified of axis limit change

xmin, ymin, xmax, ymax : float, optional
The axis limits to be set

Returns
-------
xmin, xmax, ymin, ymax : float
The axis limits

*kwargs* are passed on to :meth:`set_xlim` and
:meth:`set_ylim`
"""

if len(v) == 0 and len(kwargs) == 0:
xmin, xmax = self.get_xlim()
ymin, ymax = self.get_ylim()
return xmin, xmax, ymin, ymax

emit = kwargs.get('emit', True)

if len(v) == 1 and is_string_like(v[0]):
s = v[0].lower()
if s == 'on':
self.set_axis_on()
elif s == 'off':
self.set_axis_off()
elif s in ('equal', 'tight', 'scaled', 'normal', 'auto', 'image'):
elif s in ('equal', 'tight', 'scaled', 'normal',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring on this method also needs to be updated.

'auto', 'image', 'square'):
self.set_autoscale_on(True)
self.set_aspect('auto')
self.autoscale_view(tight=False)
Expand All @@ -1322,15 +1364,23 @@ def axis(self, *v, **kwargs):
self.autoscale_view(tight=True)
self.set_autoscale_on(False)
self.set_aspect('equal', adjustable='box', anchor='C')

elif s == 'square':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be documented as a valid input to axis.

self.set_aspect('equal', adjustable='box', anchor='C')
self.set_autoscale_on(False)
xlim = self.get_xlim()
ylim = self.get_ylim()
edge_size = max(np.diff(xlim), np.diff(ylim))
self.set_xlim([xlim[0], xlim[0] + edge_size],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to set up a callback on the zoom signal to enforce this after a user zoom, but that may be a bit too involved (as you would need to keep track of the cid and remove it if a different aspect was set.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore this comment.

emit=emit, auto=False)
self.set_ylim([ylim[0], ylim[0] + edge_size],
emit=emit, auto=False)
else:
raise ValueError('Unrecognized string %s to axis; '
'try on or off' % s)
xmin, xmax = self.get_xlim()
ymin, ymax = self.get_ylim()
return xmin, xmax, ymin, ymax

emit = kwargs.get('emit', True)
try:
v[0]
except IndexError:
Expand Down
6 changes: 6 additions & 0 deletions lib/matplotlib/pyplot.py
Expand Up @@ -1406,6 +1406,12 @@ def axis(*v, **kwargs):
as kwargs selectively to alter just those limits without changing
the others.

>>> axis('square')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is in the part of pyplot which is auto-generated by boilerplate.py so will be overwritten next time it is run.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have checked, the axis function is above the auto-generated part, so the changes will remain.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, sorry for the noise.


changes the limit ranges (*xmax*-*xmin*) and (*ymax*-*ymin*) of
the *x* and *y* axes to be the same, and have the same scaling,
resulting in a square plot.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't axis()'s docstring in pyplot autogenerated

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's outside the autogenerated part, running boilerplate.py doesn't affect it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are two for two in confusing devs on this!

Which does raise the question why isn't this part of boilerplate generated
code?

On Mon, Apr 20, 2015, 18:40 basharovV notifications@github.com wrote:

In lib/matplotlib/pyplot.py
#4310 (comment):

@@ -1406,6 +1406,12 @@ def axis(_v, *_kwargs):
as kwargs selectively to alter just those limits without changing
the others.

  •  >>> axis('square')
    
  • changes the limit ranges (xmax-xmin) and (ymax-ymin) of
  • the x and y axes to be the same, and have the same scaling,
  • resulting in a square plot.

It's outside the autogenerated part, running boilerplate.py doesn't affect
it.


Reply to this email directly or view it on GitHub
https://github.com/matplotlib/matplotlib/pull/4310/files#r28736156.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The boilerplate generation is applied only to plotting functions. It adds thegca(), the hold kwarg, and the draw_if_interactive. There are many functions that are only in pyplot. If I remember correctly, axis started out as pyplot-only but was added to Axes later. Certainly boilerplate.py could be modified to handle more Axes methods, but I don't think there is any point in doing anything like that until various other major changes settle down.

The xmin, xmax, ymin, ymax tuple is returned

.. seealso::
Expand Down
10 changes: 10 additions & 0 deletions lib/matplotlib/tests/test_axes.py
Expand Up @@ -3635,6 +3635,16 @@ def test_bar_negative_width():
assert_equal(b._width, 1)
assert_equal(b._height, indx + 1)

@cleanup
def test_square_plot():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can probably be done with out an image test, just check that the aspect is equal and the ranges are the same.

x = np.arange(4)
y = np.array([1., 3., 5., 7.])
fig, ax = plt.subplots()
ax.plot(x, y, 'mo')
ax.axis('square')
xlim, ylim = ax.get_xlim(), ax.get_ylim()
assert_true(np.diff(xlim) == np.diff(ylim))
assert_true(ax.get_aspect() == 'equal')

if __name__ == '__main__':
import nose
Expand Down