Latex tick labels in plot

692 views
Skip to first unread message

ObsessiveMathsFreak

unread,
Jun 1, 2012, 11:22:13 AM6/1/12
to sage-s...@googlegroups.com
While the "ticks=" option in plot can be used to place custom tick marks on plot axes, how would you go about putting custom labels on each tick mark, preferably latex ones,. e.g. $x_0$, $x_1$, etc.

Is there a way of doing this automatically, given a list of ticks and labels?

kcrisman

unread,
Jun 1, 2012, 11:48:51 AM6/1/12
to sage-s...@googlegroups.com


On Friday, June 1, 2012 11:22:13 AM UTC-4, ObsessiveMathsFreak wrote:
While the "ticks=" option in plot can be used to place custom tick marks on plot axes, how would you go about putting custom labels on each tick mark, preferably latex ones,. e.g. $x_0$, $x_1$, etc.

Is there a way of doing this automatically, given a list of ticks and labels?


Yep, but you'd probably have to use a tiny bit of matplotlib.  You may find the FuncFormatter [1] useful.  Go to the Graphics object documentation [2] to see exactly how to then use such a formatter.


- kcrisman

ObsessiveMathsFreak

unread,
Jun 1, 2012, 1:26:54 PM6/1/12
to sage-s...@googlegroups.com

I've read a bit of the documentation, and I've managed to come up with the following code which half works

import matplotlib.ticker

#function to make life easier
def latex_ticklabels(lbls_x,lbls_y):
    #need to prefix empty string to labels for some reason(Don't know myself).
    return [matplotlib.ticker.IndexFormatter(["$\  $"]+lbls_x),matplotlib.ticker.IndexFormatter(["$\ $"]+lbls_y)]

#the labels we are going to use (Must match number of ticks I think)
x_labels=["$x_1$","$x_2$"]
y_labels=["$y_1$","$y_2$"]

P0=plot(x^2,(r,0,2),ticks=[[1,2],[0.5,2.4]],tick_formatter=latex_ticklabels(x_labels,y_labels),fontsize=15)

show(P0)

The problem is that if the tick marks are too close together, the labels repeat themselves, or seem not to appear. It's very frustrating.  For example try

P0=plot(x^2,(r,0,2),ticks=[[1,2],[0.5,1]],tick_formatter=latex_ticklabels(x_labels,y_labels),fontsize=15)
show(P0)

Anyway, hopefully this will work for now.

Michael Orlitzky

unread,
Jun 1, 2012, 2:50:19 PM6/1/12
to sage-s...@googlegroups.com
On 06/01/12 13:26, ObsessiveMathsFreak wrote:
>
> The problem is that if the tick marks are too close together, the labels
> repeat themselves, or seem not to appear. It's very frustrating. For
> example try
>
> P0=plot(x^2,(r,0,2),ticks=[[1,2],[0.5,1]],tick_formatter=latex_ticklabels(x_labels,y_labels),fontsize=15)
> show(P0)
>
> Anyway, hopefully this will work for now.

Try FixedFormatter instead of IndexFormatter. I had better results with
it (although it occasionally seems off-by-one?).

ObsessiveMathsFreak

unread,
Jun 1, 2012, 4:01:43 PM6/1/12
to sage-s...@googlegroups.com
Brilliant! That actually worked thanks. Indexformatter gave me terrible problems subsequently, not working when the ticks were too large in value. FixedFormat seems to work better. Here's an updated form of that code
============================================

import matplotlib.ticker

#function to make life easier
def latex_ticklabels(lbls_x,lbls_y):
    #need to prefix empty string to labels for some reason(Don't know myself).
    return [matplotlib.ticker.FixedFormatter(lbls_x),matplotlib.ticker.FixedFormatter(lbls_y)]


#the labels we are going to use (Must match number of ticks I think)
x_labels=["$x_1$","$x_2$"]
y_labels=["$y_1$","$y_2$","$y_3$"]

P0=plot(x^2,(r,0,2),ticks=[[1,2],[0.5,2.4,3]],tick_formatter=latex_ticklabels(x_labels,y_labels),fontsize=15)

show(P0)
============================================

In desperation, I was also working on a method of (simply?) using text labels at each tick. I'll include it here for people who have trouble getting matplotlib to work

============================================
import matplotlib.ticker


x_ticks=[1,8]
x_labels=["$x_1$","$x_2$"]

y_ticks=[6,16,60]
y_labels=["$y_1$","$y_2$","$y_3$"]

P0=plot(x^2,(r,0,8),ticks=[x_ticks,y_ticks],tick_formatter=[matplotlib.ticker.NullFormatter(),matplotlib.ticker.NullFormatter()])

x_offset=1
PX_LABELS=sum( [text(x_label,(x_tick,-x_offset),color="black",vertical_alignment="top",fontsize=15) for [x_tick,x_label] in zip(x_ticks,x_labels)])

y_offset=0.03
PY_LABELS=sum( [text(y_label,(-y_offset,y_tick),color="black",horizontal_alignment="right",fontsize=15) for [y_tick,y_label] in zip(y_ticks,y_labels)])

show(P0+PX_LABELS+PY_LABELS)

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


I think that such functionality should eventually be part of plot by default.

ObsessiveMathsFreak

unread,
Jun 1, 2012, 4:54:45 PM6/1/12
to sage-s...@googlegroups.com


On Friday, June 1, 2012 4:22:13 PM UTC+1, ObsessiveMathsFreak wrote:
While the "ticks=" option in plot can be used to place custom tick marks on plot axes, how would you go about putting custom labels on each tick mark, preferably latex ones,. e.g. $x_0$, $x_1$, etc.

Is there a way of doing this automatically, given a list of ticks and labels?


On Friday, June 1, 2012 4:22:13 PM UTC+1, ObsessiveMathsFreak wrote:
While the "ticks=" option in plot can be used to place custom tick marks on plot axes, how would you go about putting custom labels on each tick mark, preferably latex ones,. e.g. $x_0$, $x_1$, etc.

Is there a way of doing this automatically, given a list of ticks and labels?


On Friday, June 1, 2012 4:22:13 PM UTC+1, ObsessiveMathsFreak wrote:
While the "ticks=" option in plot can be used to place custom tick marks on plot axes, how would you go about putting custom labels on each tick mark, preferably latex ones,. e.g. $x_0$, $x_1$, etc.

Is there a way of doing this automatically, given a list of ticks and labels?


ObsessiveMathsFreak

unread,
Jun 1, 2012, 4:55:39 PM6/1/12
to sage-s...@googlegroups.com
Sorry about the replys above. This new Google groups interface is really confusing.

Anyway, I coded up a plot_labels function to make adding labels to plots a little more modular.

The function basically creates an empty axis with labels which can be attached to other plots. Code follows:
==========================

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



On Friday, June 1, 2012 4:22:13 PM UTC+1, ObsessiveMathsFreak wrote:
While the "ticks=" option in plot can be used to place custom tick marks on plot axes, how would you go about putting custom labels on each tick mark, preferably latex ones,. e.g. $x_0$, $x_1$, etc.

Is there a way of doing this automatically, given a list of ticks and labels?



On Friday, June 1, 2012 4:22:13 PM UTC+1, ObsessiveMathsFreak wrote:

kcrisman

unread,
Jun 1, 2012, 9:39:25 PM6/1/12
to sage-s...@googlegroups.com


On Friday, June 1, 2012 4:55:39 PM UTC-4, ObsessiveMathsFreak wrote:
Sorry about the replys above. This new Google groups interface is really confusing.


Agreed!

 
Anyway, I coded up a plot_labels function to make adding labels to plots a little more modular.

The function basically creates an empty axis with labels which can be attached to other plots. Code follows:
==========================

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




I've opened http://trac.sagemath.org/sage_trac/ticket/13078 and put this code there, in the hope that it will be to someone later on.  Thanks!

P Purkayastha

unread,
Jun 2, 2012, 11:34:39 AM6/2/12
to sage-s...@googlegroups.com
Trac login is broken for quite a while, so I will reply here. This is quite easy to patch. For instance, the following patch for the case "frame=False" and "axes=True" (the default) works:

diff --git a/sage/plot/graphics.py b/sage/plot/graphics.py
--- a/sage/plot/graphics.py
+++ b/sage/plot/graphics.py
@@ -1913,7 +1913,7 @@
                     
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:
@@ -1924,6 +1924,8 @@
                 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:
@@ -1932,6 +1934,8 @@
                 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)


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.
Reply all
Reply to author
Forward
0 new messages