# Ticket #13078(closed enhancement: fixed)

Opened 12 months ago

## Make it easier to do custom tick formatting

Reported by: Owned by: kcrisman jason, was minor sage-5.9 graphics ppurka N/A Volker Braun, Karl-Dieter Crisman Punarbasu Purkayastha sage-5.9.beta1

This sage-support thread raised the interesting question of having not just custom ticks, but then custom formatting of said ticks one-by-one (say, setting the ticks but wanting them labeled x_0, x_1, etc.).

User "ObsessiveMathsFreak?" contributed the following code there.

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

def plot_labels(x_ticks,x_labels,y_ticks,y_labels,fontsize=12):
import matplotlib.ticker
def latex_ticklabels(lbls_x,lbls_y):
return [matplotlib.ticker.FixedFormatter(lbls_x),matplotlib.ticker.FixedFormatter(lbls_y)]

ft=fontsize
return plot([],ticks=[x_ticks,y_ticks],tick_formatter=latex_ticklabels(x_labels,y_labels),fontsize=ft)

PL=plot_labels([1,3],["$x_1$","$x_2$"],[1,2,3.5],["$y_1$","$y_2$","$y_3$"],fontsize=15)

P0=plot(x,(x,0,2))
P1=plot(x^2,(x,0,4))

show(P0+PL)
show(P1+PL)

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

I hope this is useful


Maybe there is a way we can work that into our tick and formatting framework.

## Change History

### comment:3 Changed 11 months ago by ppurka

I already have a solution for this. See the originally linked thread. I am waiting for some of the other tickets related to the plot commands to get merged so that I don't need to rebase this.

### comment:4 Changed 11 months ago by kcrisman

I'll put that here as well.

• ## sage/plot/graphics.py

# patch
diff --git a/sage/plot/graphics.py b/sage/plot/graphics.py
 a raise ValueError('Expand the range of the dependent variable to allow two multiples of your tick locator (option ticks).') x_formatter, y_formatter = tick_formatter from matplotlib.ticker import FuncFormatter from matplotlib.ticker import FuncFormatter, FixedFormatter from sage.misc.latex import latex from sage.symbolic.ring import SR if x_formatter is None: x_formatter = FuncFormatter(lambda n,pos: _multiple_of_constant(n,pos,x_const)) elif x_formatter == "latex": x_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n)) elif isinstance(x_formatter, (list, tuple)): x_formatter = FixedFormatter(x_formatter) if y_formatter is None: y_formatter = OldScalarFormatter() elif y_formatter in SR: y_formatter = FuncFormatter(lambda n,pos: _multiple_of_constant(n,pos,y_const)) elif y_formatter == "latex": y_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n)) elif isinstance(y_formatter, (list, tuple)): y_formatter = FixedFormatter(y_formatter) subplot.xaxis.set_major_locator(x_locator) subplot.yaxis.set_major_locator(y_locator)

You also said:

This can be tested with a plot like this:

plot(x, (x,0,2), ticks=[[1,2],[1,2,3.5]], tick_formatter=[["$x_1$","$x_2$"],["$y_1$","$y_2$","$y_3$"]])


There should probably be more checks like

len(ticks[0]) == len(tick_formatter[0])


and so on, but that can be addressed in a proper patch.

### comment:5 Changed 11 months ago by kcrisman

There should probably be more checks like

Yeah, and that ticks was in fact passed in, etc. This will be very nice.

### comment:6 Changed 10 months ago by ppurka

• Status changed from new to needs_review
• Description modified (diff)

Added patch for custom tick labeling.

### comment:7 Changed 10 months ago by kcrisman

Swift glance is all I have time for now, but on the face of it this seems good. ppurka, do we want the possibility of custom formatting even if for some reason someone just guesses that there are three ticks and then pass in the correct list? What about if people send in other custom tick options? For instance, I could imagine

plot(sin(x),(x,-2*pi,2*pi),ticks=pi,tick_formatter=['$-2\pi$','','','','$2\pi$'])


or something. Notice also that we want to make sure that it works properly with the tick_formatter convention that if there is only one item, not a list of two lists/None, then it corresponds to the horizontal axis. Otherwise things get too weird.

Last edited 10 months ago by kcrisman (previous) (diff)

### comment:8 Changed 10 months ago by ppurka

The docstring says

         If the value of this keyword is a single item, then this will
give the formatting for the horizontal axis *only* (except for
the ""latex"" option).  If it is a list or tuple, the first is
for the horizontal axis, the second for the vertical axis. The
options are below:


Notice that prior to this, a list was not allowed only for the horizontal axis. It was always a single item, whatever that meant (and it didn't mean a list).

I really want to force the requirement of having ticks and tick_formatter being of correct lengths. I want to avoid situations where,for instance, the user inputs ticks=pi and then doesn't give the correct number of custom labels. Is there some easy way of determining how many tick positions will be present in an axis?

### comment:9 Changed 10 months ago by kcrisman

Yeah, that was probably a little sloppy, and in any case, this is new functionality. If it doesn't break the old one... since these things have to be strings, maybe one could check if the first item was a string, and then let it go through... Just seems like

plot(sin(x),(x,-2*pi,2*pi),ticks=[1,2,3],tick_formatter=['a','b','c'])


should work, by analogy with the other things that already work.

My point is that there isn't an easy way to determine how many there will be, but that once one does know (for instance, by plotting the picture with ticks=pi), then they might as well be able to do the formatting I suggest. Wouldn't matplotlib raise an error we could catch if the formatter (at least, the FixedFormatter) had too many/too few entries?

### comment:10 follow-up: ↓ 11 Changed 10 months ago by ppurka

Actually matplotlib does not complain at all. I can remove the check for the lengths of ticks and tick_formatter and it will do all of what you mention above. Then it will be up to the user to ensure sane input.

EDIT: Also, the entries of the list passed in tick_fomratter need not be strings. They can be even numbers.

Last edited 10 months ago by ppurka (previous) (diff)

### comment:11 in reply to: ↑ 10 Changed 10 months ago by kcrisman

Actually matplotlib does not complain at all. I can remove the check for the lengths of ticks and tick_formatter and it will do all of what you mention above. Then it will be up to the user to ensure sane input.

Oh, that's actually bad. I happen to like sanity checks for things like this - despite the fact that we should know what we're doing, this is math software, not software software, so I think it's better to have that. Hmm, I'm not sure to do now. Is there a way to get the number of major ticks from mpl before using the formatter? Sorry for so many questions; hopefully someone else will have more well-baked ideas.

### comment:12 follow-up: ↓ 13 Changed 10 months ago by kcrisman

EDIT: Also, the entries of the list passed in tick_fomratter need not be strings. They can be even numbers.

Hmm, but what would it do with the preparser? Like if tick_formatter=[[1,3,88],None]? Would we get just the printed version, or ... just curious. I'm sorry I only have time to kibitz and not actually try this out.

### comment:13 in reply to: ↑ 12 Changed 10 months ago by ppurka

EDIT: Also, the entries of the list passed in tick_fomratter need not be strings. They can be even numbers.

Hmm, but what would it do with the preparser? Like if tick_formatter=[[1,3,88],None]? Would we get just the printed version, or ... just curious. I'm sorry I only have time to kibitz and not actually try this out.

It seems to work just fine. It evaluates the expression and prints the result as the tick label.

### comment:15 Changed 10 months ago by ppurka

• Authors set to Punarbasu Purkayastha

### comment:16 follow-up: ↓ 17 Changed 7 months ago by vbraun

• Reviewers set to Volker Braun

Looks good to me.

### comment:17 in reply to: ↑ 16 Changed 4 months ago by kcrisman

Looks good to me.

Is that a positive review? My suggestions were not enough to warrant holding this up.

### comment:18 Changed 2 months ago by knsam

*ping*: Hello! It looks like we are really close here! Can we look into this and see if we can merge this into Sage? :)

### comment:19 Changed 2 months ago by kcrisman

• Status changed from needs_review to positive_review

### comment:20 Changed 2 months ago by ppurka

• Reviewers changed from Volker Braun to Volker Braun, Karl-Dieter Crisman

Thanks ;-)

### comment:21 Changed 2 months ago by jdemeyer

• Milestone changed from sage-5.8 to sage-5.9

### comment:22 Changed 2 months ago by jdemeyer

• Status changed from positive_review to needs_work

This should be rebased to sage-5.9.beta0:

applying /padic/release/merger/patches/trac_13078-add_custom_tick_labeling.patch
patching file sage/plot/graphics.py
Hunk #1 succeeded at 1420 with fuzz 2 (offset 31 lines).
patching file sage/plot/plot.py
Hunk #1 succeeded at 134 with fuzz 1 (offset 0 lines).


### Changed 2 months ago by ppurka

apply to devel/sage

### comment:23 Changed 2 months ago by ppurka

• Status changed from needs_work to positive_review

Rebased it to sage-5.9.beta0. There are no updates, and the only changes should be zero fuzz and zero offset. Hence, I am setting it back to positive review.

### comment:24 Changed 2 months ago by jdemeyer

• Status changed from positive_review to closed
• Resolution set to fixed
• Merged in set to sage-5.9.beta1
Note: See TracTickets for help on using tickets.