Ticket #1431: trac_1431-ticks-and-formatting-and-latex.patch

File trac_1431-ticks-and-formatting-and-latex.patch, 20.2 KB (added by kcrisman, 11 years ago)

Based on 4.3.5, apply only this patch to Sage library

  • sage/misc/latex.py

    # HG changeset patch
    # User Karl-Dieter Crisman <kcrisman@gmail.com>
    # Date 1274813092 14400
    # Node ID d663adeb4f15db988548c0a31bf5e53c80cb782c
    # Parent  1451c00a8d44f78f01cb0dc22ebb7464cc6ad0e2
    Trac 1431 - formatting and location of ticks in 2d plots plus latex bug fix
    
    diff -r 1451c00a8d44 -r d663adeb4f15 sage/misc/latex.py
    a b  
    244244def str_function(x, escape_underscores=False):
    245245    r"""
    246246    Returns the LaTeX code for a string ``x``.  If ``x`` contains only
    247     digits, then return ``x`` itself.  Otherwise, enclose ``x`` in
    248     "\texttt{}" and return that.
     247    digits, digits with a single decimal point, or either of these with
     248    a minus sign in front, then return ``x`` itself.  Otherwise, enclose
     249    ``x`` in "\texttt{}" and return that.
    249250
    250251    If optional argument ``escape_underscores`` is True,
    251252    replace "_" with "\\_".
     
    265266        sage: from sage.misc.latex import str_function
    266267        sage: str_function('34')
    267268        '34'
     269        sage: str_function('34.5')
     270        '34.5'
     271        sage: str_function('-34.5')
     272        '-34.5'
    268273        sage: str_function('abc')
    269274        '\\texttt{abc}'
    270275        sage: str_function('hello_world')  # note that this produces invalid LaTeX
     
    272277        sage: str_function('hello_world', escape_underscores=True)  # valid LaTeX
    273278        '\\texttt{hello\\_world}'
    274279    """
    275     m = re.match('[0-9]*$', x)
    276     if m is None:  # x contains something other than digits
     280    m = re.match('-?[0-9]*\.?[0-9]*$', x)
     281    if m is None:  # x contains something other than digits (possibly with decimal point and minus sign)
    277282        if escape_underscores:
    278283            return '\\texttt{%s}'%(x.replace('_','\\_'))
    279284        else:
  • sage/plot/misc.py

    diff -r 1451c00a8d44 -r d663adeb4f15 sage/plot/misc.py
    a b  
    467467            # we probably have a constant
    468468            pass
    469469    return tuple(sorted(vars, key=lambda x: str(x))), tuple(sorted(free_variables, key=lambda x: str(x)))
     470
     471def _multiple_of_constant(n,pos,const):
     472    """
     473    Function for internal use in formatting ticks on axes with
     474    nice-looking multiples of various symbolic constants, such
     475    as `\pi` or `e`.  Should only be used via keyword argument
     476    `tick_formatter` in :meth:`plot.show`.  See documentation
     477    for the matplotlib.ticker module for more details.
     478
     479    EXAMPLES:
     480
     481    Here is the intended use::
     482
     483        sage: plot(sin(x), (x,0,2*pi), ticks=pi/3, tick_formatter=pi)
     484
     485    Here is an unintended use, which yields unexpected (and probably
     486    undesired) results::
     487
     488        sage: plot(x^2, (x, -2, 2), tick_formatter=pi)
     489
     490    We can also use more unusual constant choices::
     491
     492        sage: plot(ln(x), (x,0,10), ticks=e, tick_formatter=e)
     493        sage: plot(x^2, (x,0,10), ticks=[sqrt(2),8], tick_formatter=sqrt(2))
     494    """
     495    from sage.misc.latex import latex
     496    from sage.rings.arith import convergents
     497    c=[i for i in convergents(n/const.n()) if i.denominator()<12]
     498    return '$%s$'%latex(c[-1]*const)
  • sage/plot/plot.py

    diff -r 1451c00a8d44 -r d663adeb4f15 sage/plot/plot.py
    a b  
    109109    sage: plot(x^2,(x,480,500))  # no scientific notation
    110110    sage: plot(x^2,(x,300,500))  # scientific notation on y-axis
    111111
     112But you can fix your own labels, if you know what to expect and
     113have a preference::
     114
     115    sage: plot(x^2,(x,300,500),ticks=[None,50000])
     116
    112117Next we construct the reflection of the above polygon about the
    113118`y`-axis by iterating over the list of first-coordinates of
    114119the first graphic element of `P` (which is the actual
     
    190195    sage: g2 = plot(2*exp(-30*x) - exp(-3*x), 0, 1)
    191196    sage: show(graphics_array([g1, g2], 2, 1), xmin=0)
    192197
    193 Pi Axis: In the PyX manual, the point of this example is to show
    194 labeling the X-axis using rational multiples of Pi. Sage currently
    195 has no support for controlling how the ticks on the x and y axes
    196 are labeled, so this is really a bad example::
     198Pi Axis::
    197199
    198200    sage: g1 = plot(sin(x), 0, 2*pi)
    199201    sage: g2 = plot(cos(x), 0, 2*pi, linestyle = "--")
    200     sage: g1 + g2    # show their sum
     202    sage: (g1+g2).show(ticks=pi/6, tick_formatter=pi)  # show their sum, nicely formatted
    201203
    202204An illustration of integration::
    203205
     
    11921194                        fontsize=None, aspect_ratio=None,
    11931195                        gridlines=None, gridlinesstyle=None,
    11941196                        vgridlinesstyle=None, hgridlinesstyle=None,transparent=False,
    1195                         axes_pad=.02)
     1197                        axes_pad=.02, ticks=None, tick_formatter=None)
    11961198
    11971199    def show(self, **kwds):
    11981200        """
     
    12671269          etc.  To get axes that are exactly the specified limits, set
    12681270          ``axes_pad`` to zero.
    12691271
     1272        - ``ticks`` - A matplotlib locator for the major ticks, or
     1273          a number. There are several options.  For more information about
     1274          locators, type ``from matplotlib import ticker`` and then
     1275          ``ticker?``.
     1276
     1277          - If this is a locator object, then it is the locator for
     1278            the horizontal axis.  A value of None means use the default
     1279            locator.
     1280
     1281          - If it is a list of two locators, then the first is for the
     1282            horizontal axis and one for the vertical axis.  A value of
     1283            None means use the default locator (so a value of
     1284            [None, my_locator] uses my_locator for the vertical axis and
     1285            the default for the horizontal axis).
     1286
     1287          - If in either case above one of the entries is a number `m`
     1288            (something which can be coerced to a float), it will be
     1289            replaced by a MultipleLocator which places major ticks at
     1290            integer multiples of `m`.  See examples.
     1291
     1292          - If in either case above one of the entries is a list of
     1293            numbers, it will be replaced by a FixedLocator which places
     1294            ticks at the locations specified.  This includes the case of
     1295            of the empty list, which will give no ticks.  See examples.
     1296
     1297        - ``tick_formatter`` - A matplotlib formatter for the major
     1298          ticks. There are several options.  For more information about
     1299          formatters, type ``from matplotlib import ticker`` and then
     1300          ``ticker?``.
     1301
     1302          If the value of this keyword is a single item, then this will
     1303          give the formatting for the horizontal axis *only* (except for
     1304          the ``"latex"`` option).  If it is a list or tuple, the first
     1305          is for the horizontal axis, the second for the vertical axis.
     1306          The options are below:
     1307
     1308          - If one of the entries is a formatter object, then it used.
     1309            A value of None means to use the default locator (so using
     1310            ``tick_formatter=[None, my_formatter]`` uses my_formatter
     1311            for the vertical axis and the default for the horizontal axis).
     1312
     1313          - If one of the entries is a symbolic constant such as `\pi`,
     1314            `e`, or `sqrt(2)`, ticks will be formatted nicely at rational
     1315            multiples of this constant.
     1316
     1317          .. warning:: This should only be used with the ``ticks`` option
     1318             using nice rational multiples of that constant!
     1319
     1320          - If one of the entries is the string ``"latex"``, then the
     1321            formatting will be nice typesetting of the ticks.  This is
     1322            intended to be used when the tick locator for at least one of
     1323            the axes is a list including some symbolic elements.  See examples.
     1324
    12701325        EXAMPLES::
    12711326       
    12721327            sage: c = circle((1,1), 1, color='red')
     
    14221477            sage: plot(sin(x), (x, -pi, pi),thickness=2)+point((pi, -1), pointsize=15)
    14231478            sage: plot(sin(x), (x, -pi, pi),thickness=2,axes_pad=0)+point((pi, -1), pointsize=15)
    14241479       
     1480        Via matplotlib, Sage allows setting of custom ticks.  See above
     1481        for more details.
     1482
     1483        ::
     1484
     1485            sage: plot(sin(pi*x), (x, -8, 8)) # Labels not so helpful
     1486            sage: plot(sin(pi*x), (x, -8, 8), ticks=2) # Multiples of 2
     1487            sage: plot(sin(pi*x), (x, -8, 8), ticks=[[-7,-3,0,3,7],[-1/2,0,1/2]]) # Your choices
     1488            sage: plot(sin(pi*x), (x, -8, 8), ticks=[[],[]]) # No ticks at all!
     1489
     1490        This can be very helpful in showing certain features of plots.
     1491
     1492        ::
     1493            sage: plot(1.5/(1+e^(-x)), (x, -10, 10)) # doesn't quite show value of inflection point
     1494            sage: plot(1.5/(1+e^(-x)), (x, -10, 10), ticks=[None, 1.5/4]) # It's right at f(x)=0.75!
     1495
     1496        But be careful to leave enough room for at least two major ticks, so that
     1497        the user can tell what the scale is.
     1498
     1499        ::
     1500
     1501            sage: plot(x^2,(x,1,8),ticks=6)
     1502            Traceback (most recent call last):
     1503            ...
     1504            ValueError: Expand the range of the independent variable to allow two multiples of your tick locator (option `ticks`).
     1505
     1506        We can also do custom formatting if you need it.  See above for full
     1507        details.
     1508
     1509        ::
     1510
     1511            sage: plot(2*x+1,(x,0,5),ticks=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex")
     1512
     1513        This is particularly useful when setting custom ticks in multiples
     1514        of `pi`.
     1515
     1516        ::
     1517
     1518            sage: plot(sin(x),(x,0,2*pi),ticks=pi/3,tick_formatter=pi)
     1519
     1520        But keep in mind that you will get exactly the formatting you asked
     1521        for if you specify both formatters.  The first syntax is recommended
     1522        for best style in that case.
     1523
     1524        ::
     1525
     1526            sage: plot(arcsin(x),(x,-1,1),ticks=[None,pi/6],tick_formatter=["latex",pi]) # Nice-looking!
     1527            sage: plot(arcsin(x),(x,-1,1),ticks=[None,pi/6],tick_formatter=[None,pi]) # Not so nice-looking
     1528
    14251529        """
    14261530
    14271531        # This option should not be passed on to save().
     
    15741678              axes=None, axes_labels=None, fontsize=None,
    15751679             frame=False, verify=True, aspect_ratio = None,
    15761680             gridlines=None, gridlinesstyle=None,
    1577              vgridlinesstyle=None, hgridlinesstyle=None,axes_pad=0.02):
     1681             vgridlinesstyle=None, hgridlinesstyle=None,axes_pad=0.02,
     1682             tick_formatter=None, ticks=None):
    15781683        r"""
    15791684        Return a matplotlib figure object representing the graphic
    15801685
     
    15971702        :meth:`show` method (this function accepts all except the
    15981703        transparent argument).
    15991704        """
     1705
     1706        if not isinstance(ticks, (list, tuple)):
     1707            ticks = (ticks, None)
     1708
     1709        from sage.symbolic.ring import SR
     1710        if not isinstance(tick_formatter, (list, tuple)):  # make sure both formatters typeset or both don't
     1711            if tick_formatter == "latex" or tick_formatter in SR:
     1712                tick_formatter = (tick_formatter, "latex")
     1713            else:
     1714                tick_formatter = (tick_formatter, None)
     1715           
    16001716        self.set_axes_range(xmin, xmax, ymin, ymax)
    16011717        d = self.get_axes_range()
    16021718        xmin = d['xmin']
     
    16671783            # For now, set the formatter to the old one, since that is
    16681784            # sort of what we are used to.  We should eventually look at
    16691785            # the default one to see if we like it better.
    1670             from matplotlib.ticker import OldScalarFormatter, MaxNLocator
    1671             subplot.xaxis.set_major_locator(MaxNLocator(nbins=9))
    1672             subplot.yaxis.set_major_locator(MaxNLocator(nbins=9))
    1673             subplot.xaxis.set_major_formatter(OldScalarFormatter())
    1674             subplot.yaxis.set_major_formatter(OldScalarFormatter())
     1786
     1787            from matplotlib.ticker import OldScalarFormatter, MaxNLocator, MultipleLocator, FixedLocator, NullLocator, Locator
     1788            x_locator, y_locator = ticks
     1789            if x_locator is None:
     1790                x_locator = MaxNLocator(nbins=9)
     1791            elif isinstance(x_locator,Locator):
     1792                pass
     1793            elif x_locator == []:
     1794                x_locator = NullLocator()
     1795            elif isinstance(x_locator,list):
     1796                x_locator = FixedLocator(x_locator)
     1797            else: # x_locator is a number which can be made a float
     1798                from sage.functions.other import ceil, floor
     1799                if floor(xmax/x_locator)-ceil(xmin/x_locator)>1:
     1800                    x_locator=MultipleLocator(float(x_locator))
     1801                else: # not enough room for two major ticks
     1802                    raise ValueError('Expand the range of the independent variable to allow two multiples of your tick locator (option `ticks`).')
     1803            if y_locator is None:
     1804                y_locator = MaxNLocator(nbins=9)
     1805            elif isinstance(y_locator,Locator):
     1806                pass
     1807            elif y_locator == []:
     1808                y_locator = NullLocator()
     1809            elif isinstance(y_locator,list):
     1810                y_locator = FixedLocator(y_locator)
     1811            else: # y_locator is a number which can be made a float
     1812                from sage.functions.other import ceil, floor
     1813                if floor(ymax/y_locator)-ceil(ymin/y_locator)>1:
     1814                    y_locator=MultipleLocator(float(y_locator))
     1815                else: # not enough room for two major ticks
     1816                    raise ValueError('Expand the range of the dependent variable to allow two multiples of your tick locator (option `ticks`).')
     1817
     1818            x_formatter, y_formatter = tick_formatter
     1819            from matplotlib.ticker import FuncFormatter
     1820            from sage.misc.latex import latex
     1821            if x_formatter is None:
     1822                x_formatter = OldScalarFormatter()
     1823            elif x_formatter in SR:
     1824                from misc import _multiple_of_constant
     1825                x_const = x_formatter
     1826                x_formatter = FuncFormatter(lambda n,pos: _multiple_of_constant(n,pos,x_const))
     1827            elif x_formatter == "latex":
     1828                x_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n))
     1829            if y_formatter is None:
     1830                y_formatter = OldScalarFormatter()
     1831            elif y_formatter in SR:
     1832                from misc import _multiple_of_constant
     1833                y_const = y_formatter
     1834                y_formatter = FuncFormatter(lambda n,pos: _multiple_of_constant(n,pos,y_const))
     1835            elif y_formatter == "latex":
     1836                y_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n))
     1837
     1838            subplot.xaxis.set_major_locator(x_locator)
     1839            subplot.yaxis.set_major_locator(y_locator)
     1840            subplot.xaxis.set_major_formatter(x_formatter)
     1841            subplot.yaxis.set_major_formatter(y_formatter)
    16751842           
    16761843            subplot.set_frame_on(True)
    16771844            if axes:
     
    17281895            # For now, set the formatter to the old one, since that is
    17291896            # sort of what we are used to.  We should eventually look at
    17301897            # the default one to see if we like it better.
    1731             from matplotlib.ticker import OldScalarFormatter, MaxNLocator
    1732             subplot.xaxis.set_major_locator(MaxNLocator(nbins=10,steps=[1,2,5,10]))
    1733             subplot.yaxis.set_major_locator(MaxNLocator(nbins=10,steps=[1,2,5,10]))
    1734             subplot.xaxis.set_major_formatter(OldScalarFormatter())
    1735             subplot.yaxis.set_major_formatter(OldScalarFormatter())
     1898           
     1899            from matplotlib.ticker import OldScalarFormatter, MaxNLocator, MultipleLocator, FixedLocator, NullLocator, Locator
     1900            x_locator, y_locator = ticks
     1901            if x_locator is None:
     1902                x_locator = MaxNLocator(nbins=9, steps=[1,2,5,10])
     1903            elif isinstance(x_locator,Locator):
     1904                pass
     1905            elif x_locator == []:
     1906                x_locator = NullLocator()
     1907            elif isinstance(x_locator,list):
     1908                x_locator = FixedLocator(x_locator)
     1909            else: # x_locator is a number which can be made a float
     1910                from sage.functions.other import ceil, floor
     1911                if floor(xmax/x_locator)-ceil(xmin/x_locator)>1:
     1912                    x_locator=MultipleLocator(float(x_locator))
     1913                else: # not enough room for two major ticks
     1914                    raise ValueError('Expand the range of the independent variable to allow two multiples of your tick locator (option `ticks`).')
     1915            if y_locator is None:
     1916                y_locator = MaxNLocator(nbins=9, steps=[1,2,5,10])
     1917            elif isinstance(y_locator,Locator):
     1918                pass
     1919            elif y_locator == []:
     1920                y_locator = NullLocator()
     1921            elif isinstance(y_locator,list):
     1922                y_locator = FixedLocator(y_locator)
     1923            else: # y_locator is a number which can be made a float
     1924                from sage.functions.other import ceil, floor
     1925                if floor(ymax/y_locator)-ceil(ymin/y_locator)>1:
     1926                    y_locator=MultipleLocator(float(y_locator))
     1927                else: # not enough room for two major ticks
     1928                    raise ValueError('Expand the range of the dependent variable to allow two multiples of your tick locator (option `ticks`).')
    17361929
     1930            x_formatter, y_formatter = tick_formatter
     1931            from matplotlib.ticker import FuncFormatter
     1932            from sage.misc.latex import latex
     1933            from sage.symbolic.ring import SR
     1934            if x_formatter is None:
     1935                x_formatter = OldScalarFormatter()
     1936            elif x_formatter in SR:
     1937                from misc import _multiple_of_constant
     1938                x_const = x_formatter
     1939                x_formatter = FuncFormatter(lambda n,pos: _multiple_of_constant(n,pos,x_const))
     1940            elif x_formatter == "latex":
     1941                x_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n))
     1942            if y_formatter is None:
     1943                y_formatter = OldScalarFormatter()
     1944            elif y_formatter in SR:
     1945                from misc import _multiple_of_constant
     1946                y_const = y_formatter
     1947                y_formatter = FuncFormatter(lambda n,pos: _multiple_of_constant(n,pos,y_const))
     1948            elif y_formatter == "latex":
     1949                y_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n))
     1950
     1951            subplot.xaxis.set_major_locator(x_locator)
     1952            subplot.yaxis.set_major_locator(y_locator)
     1953            subplot.xaxis.set_major_formatter(x_formatter)
     1954            subplot.yaxis.set_major_formatter(y_formatter)
    17371955
    17381956            # Make ticklines go on both sides of the axes
    17391957            #             if xmiddle:
     
    17681986            subplot.yaxis.set_major_locator(NullLocator())
    17691987
    17701988        if frame or axes:
    1771             # Make minor tickmarks
    1772             from matplotlib.ticker import AutoMinorLocator
    1773             subplot.xaxis.set_minor_locator(AutoMinorLocator())
    1774             subplot.yaxis.set_minor_locator(AutoMinorLocator())
     1989            # Make minor tickmarks, unless we specify fixed ticks or no ticks
     1990            from matplotlib.ticker import AutoMinorLocator, FixedLocator, NullLocator
     1991            if isinstance(x_locator, (NullLocator, FixedLocator)):
     1992                subplot.xaxis.set_minor_locator(NullLocator())
     1993            else:
     1994                subplot.xaxis.set_minor_locator(AutoMinorLocator())
     1995            if isinstance(y_locator, (NullLocator, FixedLocator)):
     1996                subplot.yaxis.set_minor_locator(NullLocator())
     1997            else:
     1998                subplot.yaxis.set_minor_locator(AutoMinorLocator())
    17751999
    17762000            ticklabels=subplot.xaxis.get_majorticklabels() + \
    17772001                subplot.xaxis.get_minorticklabels() + \
     
    24022626        sage: plot(sin(x^2), (x, -3, 3), figsize=[8,2])
    24032627        sage: plot(sin(x^2), (x, -3, 3)).show(figsize=[8,2]) # These are equivalent
    24042628
     2629    This includes options for custom ticks and formatting.  See documentation
     2630    for :meth:`show` for more details.
     2631
     2632    ::
     2633
     2634        sage: plot(sin(pi*x), (x, -8, 8), ticks=[[-7,-3,0,3,7],[-1/2,0,1/2]])
     2635        sage: plot(2*x+1,(x,0,5),ticks=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex")
     2636
     2637    This is particularly useful when setting custom ticks in multiples of `pi`.
     2638
     2639    ::
     2640
     2641        sage: plot(sin(x),(x,0,2*pi),ticks=pi/3,tick_formatter=pi)
     2642
    24052643    A example with excluded values::
    24062644
    24072645        sage: plot(floor(x), (x, 1, 10), exclude = [1..10])