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

xticks/yticks default behaviour #5937

Closed
ferdinandvanwyk opened this issue Jan 28, 2016 · 7 comments
Closed

xticks/yticks default behaviour #5937

ferdinandvanwyk opened this issue Jan 28, 2016 · 7 comments
Labels
Difficulty: Easy https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues New feature

Comments

@ferdinandvanwyk
Copy link

I want to be able to set the xticks or yticks based on user input, but I can't seem to do this without invoking an if statement in order to use the default behaviour.

To get the default xticks for example, you can run:

import numpy as np                                                              
import matplotlib.pyplot as plt

x = np.linspace(0,1)                                                            

plt.plot(x, x**2)                                                               
plt.xticks()                                                                    
plt.show()

However, if I want a user-set variable to set the xticks, I have to do this:

import numpy as np                                                              
import matplotlib.pyplot as plt

user_option = None

x = np.linspace(0,1)                                                            

plt.plot(x, x**2)
if user_option == None:                                                       
    plt.xticks()
else:
    plt.xticks(user_option)                                                                    
plt.show()

Since the code plt.xticks(None) gives the following error:

Traceback (most recent call last):
  File "tmp.py", line 9, in <module>
    plt.xticks(None)
  File "/home/vanwyk/py_envs/py3/lib/python3.4/site-packages/matplotlib/pyplot.py", line 1671, in xticks
    locs = ax.set_xticks(args[0])
  File "/home/vanwyk/py_envs/py3/lib/python3.4/site-packages/matplotlib/axes/_base.py", line 2850, in set_xticks
    ret = self.xaxis.set_ticks(ticks, minor=minor)
  File "/home/vanwyk/py_envs/py3/lib/python3.4/site-packages/matplotlib/axis.py", line 1595, in set_ticks
    if len(ticks) > 1:
TypeError: object of type 'NoneType' has no len()

Could the plt.xticks and plt.yticks functions allow the passing of None to retrieve default behaviour, or is there some way of getting the default ticks while still passing a variable?

@tacaswell tacaswell added this to the unassigned milestone Jan 28, 2016
@tacaswell tacaswell added New feature Difficulty: Easy https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues labels Jan 28, 2016
@tacaswell
Copy link
Member

You should really be using ax.set_xticks instead of pyplot if you writing tools for users.

@ferdinandvanwyk
Copy link
Author

Hi @tacaswell, thanks. Firstly, can you point me somewhere so I understand what you said above? What is it about having users that makes it more appropriate to use the ax.set_* notation? I guess I've never really considered the difference.

I am really interested in contributing to Matplotlib. Is the procedure that once things are posted here and are assigned a tag, e.g. 'Difficulty: Easy', anyone can have a go at fixing/implementing it?

@WeatherGod
Copy link
Member

I touch on the axes methods versus pyplot in my Anatomy of Matplotlib
tutorial here:
http://nbviewer.jupyter.org/github/WeatherGod/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb#Axes-methods-vs.-pyplot

What I don't talk about here is that when writing code for yourself, you
already have implicit knowledge of what you want the code to do. So the
pyplot approach works well with that. However, when writing code for other
people to use, that implicit knowledge is lost in the transfer and so you
can't safely assume that the plotting command you are issuing will occur on
the axes or the figure window that you intended.

As for rules & procedures, there aren't really any, but we do have a guide
to help first time contributors:
http://matplotlib.org/faq/howto_faq.html#contributing-howto

Cheers!
Ben Root

On Fri, Jan 29, 2016 at 6:51 AM, Ferdinand van Wyk <notifications@github.com

wrote:

Hi @tacaswell https://github.com/tacaswell, thanks. Firstly, can you
point me somewhere so I understand what you said above? What is it about
having users that makes it more appropriate to use the ax.set_* notation?
I guess I've never really considered the difference.

I am really interested in contributing to Matplotlib. Is the procedure
that once things are posted here and are assigned a tag, e.g. 'Difficulty:
Easy', anyone can have a go at fixing/implementing it?


Reply to this email directly or view it on GitHub
#5937 (comment)
.

@ferdinandvanwyk
Copy link
Author

@WeatherGod Thanks, that makes sense now.

Are we agreed that both the pyplot and axis interface need to change? In other words, both plt.xticks(None) and ax.set_xticks(None) (and similarly for y) should recover the default behaviour?

I've looked into it and once fixed I'll set up a PR for review.

@ferdinandvanwyk
Copy link
Author

Hi,

I've tried implementing this so that None recovers the default behaviour but I have come across the following issue:

Executing the code for the test plot above (for the most up to date Master branch):

import numpy as np                                                              
import matplotlib.pyplot as plt

x = np.linspace(0,1)                                                            

fig, ax = plt.subplots()
ax.plot(x, x**2)                                                                                                                                   
plt.show()

I get the following plot:

test1

However calling ax.set_xticks() on the default xticks (basically the behaviour I'm trying to recover when a user passes in None) I get something different:

x = np.linspace(0,1)                                                            

ax.plot(x, x**2)
ax.set_xticks(ax.get_xticks())                                                                                                                                   
plt.show()

test2

Running ax.get_xticks() on its own for dev branch confirms this:

>> ax.get_xticks()
array([-0.2,  0. ,  0.2,  0.4,  0.6,  0.8,  1. ,  1.2])

Note: I'm not finding the same for the release version. Running the above code on 1.5.1 gives:

>> ax.get_xticks()
array([0. ,  0.2,  0.4,  0.6,  0.8,  1. ])

This may even constitute a new ticket since this seems like a bug as opposed to a feature request.

Does anyone have any ideas? I'm new to the Matplotlib source code so it may take a while for me to find the issue here.

Ubuntu 14.04 LTS
Dev version: 1.5.1-1084.g1943acc
RC version: 1.5.1
Python 3.4.3 running in separate virtual environments

@tacaswell
Copy link
Member

This is intended behavior. As part of the 2.0 style over-haul we by default added padding to the data limits to improve how the plots look. The ticks are located via a Locator instance on each of the Axis objects that the Axes object has. These locators look at the axis limits and determine where the ticks should be located. Because it is hard to know if your next tick will be with in the data limits the locators are allowed to return extra ticks which are outside of the data range which are then simply not drawn.

Calling ax.get_xticks() eventually falls through to just calling the locator object, which returns all the plausible tick locations (as it is normally the job of some other part of the code to filter if those ticks are in the view limits).

When ax.set_xticks it eventually falls through to Axis.set_ticks which replaces whatever the locator on the axis is with a FixedLocator which always returns the same tick positions (which if you use this and then pan out of the old data limits you will have no ticks) and helpfully expands your data limits to show all of the ticks.

After a bit more consideration, I am not sure what the behavior of ax.set_xticks(None) should be. A sequence of ticks is a required arg so there is no default behavior. Similarly for plt.xticks, the behavior depends on the number of *args in receives and none of the value have a default value.

The convention that mpl (mostly) follows is that None -> take default value, but in these cases I do not immediately see what that value should be.

@ferdinandvanwyk
Copy link
Author

Thanks for the explanation @tacaswell, I thought I was going slightly crazy seeing subtly different plots between dev and release branches. I did think the extra padding helped frame the plots a bit better so if that's the default behaviour from now on then the functionality for setting tick defaults moves to the application side. It's a shame, however since I really wanted to avoid adding logical checks in loops producing 1000's of plots or duplicating code to have two different versions depending on user set options.

Thanks for taking the time to help. I think we can close this ticket now and I'll find some other ticket to start my Matplotlib contributions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty: Easy https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues New feature
Projects
None yet
Development

No branches or pull requests

3 participants