Opened 10 years ago

Closed 9 years ago

#13078 closed enhancement (fixed)

Make it easier to do custom tick formatting

Reported by: kcrisman Owned by: jason, was
Priority: minor Milestone: sage-5.9
Component: graphics Keywords:
Cc: ppurka Merged in: sage-5.9.beta1
Authors: Punarbasu Purkayastha Reviewers: Volker Braun, Karl-Dieter Crisman
Report Upstream: N/A Work issues:
Branch: Commit:
Dependencies: Stopgaps:

Status badges

Description (last modified by ppurka)

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.


Apply trac_13078-add_custom_tick_labeling.patch to devel/sage.

Attachments (1)

trac_13078-add_custom_tick_labeling.patch (5.5 KB) - added by ppurka 9 years ago.
apply to devel/sage

Download all attachments as: .zip

Change History (25)

comment:1 Changed 10 years ago by ppurka

  • Cc ppurka added

comment:2 Changed 10 years ago by kcrisman

See also #9578 and #2189.

comment:3 Changed 10 years 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 10 years 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 b  
    19131913                    raise ValueError('Expand the range of the dependent variable to allow two multiples of your tick locator (option `ticks`).')
    19141914
    19151915            x_formatter, y_formatter = tick_formatter
    1916             from matplotlib.ticker import FuncFormatter
     1916            from matplotlib.ticker import FuncFormatter, FixedFormatter
    19171917            from sage.misc.latex import latex
    19181918            from sage.symbolic.ring import SR
    19191919            if x_formatter is None:
     
    19241924                x_formatter = FuncFormatter(lambda n,pos: _multiple_of_constant(n,pos,x_const))
    19251925            elif x_formatter == "latex":
    19261926                x_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n))
     1927            elif isinstance(x_formatter, (list, tuple)):
     1928                x_formatter = FixedFormatter(x_formatter)
    19271929            if y_formatter is None:
    19281930                y_formatter = OldScalarFormatter()
    19291931            elif y_formatter in SR:
     
    19321934                y_formatter = FuncFormatter(lambda n,pos: _multiple_of_constant(n,pos,y_const))
    19331935            elif y_formatter == "latex":
    19341936                y_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n))
     1937            elif isinstance(y_formatter, (list, tuple)):
     1938                y_formatter = FixedFormatter(y_formatter)
    19351939
    19361940            subplot.xaxis.set_major_locator(x_locator)
    19371941            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 10 years 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 years ago by ppurka

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

Added patch for custom tick labeling.

comment:7 Changed 10 years 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 years ago by kcrisman (previous) (diff)

comment:8 Changed 10 years 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 years 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: Changed 10 years 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 years ago by ppurka (previous) (diff)

comment:11 in reply to: ↑ 10 Changed 10 years 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: Changed 10 years 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 years ago by ppurka

Replying to 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.

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

comment:14 Changed 9 years ago by jdemeyer

Please fill in your real name as Author.

comment:15 Changed 9 years ago by ppurka

  • Authors set to Punarbasu Purkayastha

comment:16 follow-up: Changed 9 years ago by vbraun

  • Reviewers set to Volker Braun

Looks good to me.

comment:17 in reply to: ↑ 16 Changed 9 years 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 9 years 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 9 years ago by kcrisman

  • Status changed from needs_review to positive_review

comment:20 Changed 9 years ago by ppurka

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

Thanks ;-)

comment:21 Changed 9 years ago by jdemeyer

  • Milestone changed from sage-5.8 to sage-5.9

comment:22 Changed 9 years 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 9 years ago by ppurka

apply to devel/sage

comment:23 Changed 9 years 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 9 years ago by jdemeyer

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