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

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

Based on 4.3.5, apply only this patch to Sage library

  • sage/plot/misc.py

    # HG changeset patch
    # User Karl-Dieter Crisman <kcrisman@gmail.com>
    # Date 1271787464 14400
    # Node ID 5296ca88bd3a5e500b0f0300d86bfb67c4465864
    # Parent  ea02bc44fa947f97673768aede1126e6bc7b2c79
    Trac 1431 - includes formatting and location of ticks in 2d plots
    
    diff -r ea02bc44fa94 -r 5296ca88bd3a sage/plot/misc.py
    a b  
    427427            # we probably have a constant
    428428            pass
    429429    return tuple(sorted(vars, key=lambda x: str(x))), tuple(sorted(free_variables, key=lambda x: str(x)))
     430
     431def multiple_of_constant(n,pos,const):
     432    """
     433    Function for internal use in formatting ticks on axes with
     434    nice-looking multiples of various symbolic constants, such
     435    as `\pi` or `e`.  Should only be used via keyword argument
     436    `tick_formatter` in :meth:`plot.show`.  See documentation
     437    for the matplotlib.ticker module for more details.
     438
     439    EXAMPLES:
     440
     441    Here is the intended use::
     442
     443        sage: plot(sin(x), (x,0,2*pi), tick_locator=pi/3, tick_formatter=pi)
     444
     445    Here is an unintended use, which yields unexpected results::
     446
     447        sage: plot(x^2, (x, -2, 2), tick_formatter=pi)
     448
     449    We can also use more unusual constant choices::
     450
     451        sage: plot(ln(x), (x,0,10), tick_locator=e, tick_formatter=e)
     452        sage: plot(x^2, (x,0,10), tick_locator=[sqrt(2),8], tick_formatter=sqrt(2))
     453    """
     454    from sage.misc.latex import latex
     455    from sage.rings.arith import convergents
     456    c=[i for i in convergents(n/const.n()) if i.denominator()<12]
     457    return '$%s$'%latex(c[-1]*const)
  • sage/plot/plot.py

    diff -r ea02bc44fa94 -r 5296ca88bd3a 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),tick_locator=[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(tick_locator=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, tick_locator=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        - ``tick_locator`` - 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.  See examples.
     1295
     1296        - ``tick_formatter`` - A matplotlib formatter for the major
     1297          ticks. There are several options.  For more information about
     1298          formatters, type ``from matplotlib import ticker`` and then
     1299          ``ticker?``.
     1300
     1301          - If this is a formatter object, then it is the formatter for
     1302            the horizontal axis.  A value of None means use the default
     1303            locator.
     1304
     1305          - If it is a list of two formatters, then the first is for the
     1306            horizontal axis and one for the vertical axis.  A value of
     1307            None means use the default formatter (so a value of
     1308            [None, my_formatter] uses my_formatter for the vertical axis
     1309            and the default for the horizontal axis).
     1310
     1311          - If in either case above one of the entries is a symbolic
     1312            constant such as `\pi`, `e`, or `sqrt(2)`, ticks will be
     1313            formatted nicely at rational multiples of this constant.
     1314            This should only be used with a ``tick_locator`` option using
     1315            nice rational multiples of that constant!
     1316
     1317          - If in either case above one of the entries is the string
     1318            ``"latex"``, then the formatting will be nice typesetting
     1319            of the ticks.  This is intended to be used when the tick
     1320            locator for a given axis is a list including some symbolic
     1321            elements.  See examples.
     1322
    12701323        EXAMPLES::
    12711324       
    12721325            sage: c = circle((1,1), 1, color='red')
     
    14221475            sage: plot(sin(x), (x, -pi, pi),thickness=2)+point((pi, -1), pointsize=15)
    14231476            sage: plot(sin(x), (x, -pi, pi),thickness=2,axes_pad=0)+point((pi, -1), pointsize=15)
    14241477       
     1478        Via matplotlib, Sage allows setting of custom ticks.  See
     1479        documentation for :meth:`show` for more details.
     1480
     1481        ::
     1482
     1483            sage: plot(sin(pi*x), (x, -8, 8)) # Labels not so helpful
     1484            sage: plot(sin(pi*x), (x, -8, 8), tick_locator=2) # Multiples of 2
     1485            sage: plot(sin(pi*x), (x, -8, 8), tick_locator=[[-7,-3,0,3,7],[-1/2,0,1/2]]) # Your choices
     1486            sage: plot(1.5/(1+e^(-x)), (x, -10, 10)) # doesn't quite show value of inflection point
     1487            sage: plot(1.5/(1+e^(-x)), (x, -10, 10), tick_locator=[None, 1.5/4]) # It's right at f(x)=0.75!
     1488
     1489        But be careful to leave enough room for at least two major ticks, so that
     1490        the user can tell what the scale is.
     1491
     1492        ::
     1493
     1494            sage: plot(x^2,(x,1,8),tick_locator=6)
     1495            Traceback (most recent call last):
     1496            ...
     1497            ValueError: Expand the range of the independent variable to allow two multiples of your `tick_locator`.
     1498
     1499        We can also do custom formatting if you need it.  See documentation
     1500        for :meth:`show` for more details.
     1501
     1502        ::
     1503
     1504            sage: plot(sin(x),(x,0,2*pi),tick_locator=pi/3,tick_formatter=pi)
     1505            sage: plot(2*x+1,(x,0,5),tick_locator=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex")
     1506
    14251507        """
    14261508
    14271509        # This option should not be passed on to save().
     
    15741656              axes=None, axes_labels=None, fontsize=None,
    15751657             frame=False, verify=True, aspect_ratio = None,
    15761658             gridlines=None, gridlinesstyle=None,
    1577              vgridlinesstyle=None, hgridlinesstyle=None,axes_pad=0.02):
     1659             vgridlinesstyle=None, hgridlinesstyle=None,axes_pad=0.02,
     1660             tick_formatter=None, tick_locator=None):
    15781661        r"""
    15791662        Return a matplotlib figure object representing the graphic
    15801663
     
    15971680        :meth:`show` method (this function accepts all except the
    15981681        transparent argument).
    15991682        """
     1683
     1684        if not isinstance(tick_locator, (list, tuple)):
     1685            tick_locator = (tick_locator, None)
     1686
     1687        if not isinstance(tick_formatter, (list, tuple)):
     1688            tick_formatter = (tick_formatter, None)
     1689           
    16001690        self.set_axes_range(xmin, xmax, ymin, ymax)
    16011691        d = self.get_axes_range()
    16021692        xmin = d['xmin']
     
    16671757            # For now, set the formatter to the old one, since that is
    16681758            # sort of what we are used to.  We should eventually look at
    16691759            # 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())
     1760
     1761            from matplotlib.ticker import OldScalarFormatter, MaxNLocator, MultipleLocator, FixedLocator, Locator
     1762            x_locator, y_locator = tick_locator
     1763            if x_locator is None:
     1764                x_locator = MaxNLocator(nbins=9)
     1765            elif isinstance(x_locator,Locator):
     1766                pass
     1767            elif isinstance(x_locator,list):
     1768                x_locator = FixedLocator(x_locator)
     1769            else: # x_locator is a number which can be made a float
     1770                from sage.functions.other import ceil, floor
     1771                if floor(xmax/x_locator)-ceil(xmin/x_locator)>1:
     1772                    x_locator=MultipleLocator(float(x_locator))
     1773                else: # not enough room for two major ticks
     1774                    raise ValueError('Expand the range of the independent variable to allow two multiples of your `tick_locator`.')
     1775            if y_locator is None:
     1776                y_locator = MaxNLocator(nbins=9)
     1777            elif isinstance(y_locator,Locator):
     1778                pass
     1779            elif isinstance(y_locator,Locator):
     1780                y_locator = FixedLocator(y_locator)
     1781            else: # y_locator is a number which can be made a float
     1782                from sage.functions.other import ceil, floor
     1783                if floor(ymax/y_locator)-ceil(ymin/y_locator)>1:
     1784                    y_locator=MultipleLocator(float(y_locator))
     1785                else: # not enough room for two major ticks
     1786                    raise ValueError('Expand the range of the dependent variable to allow two multiples of your `tick_locator`.')
     1787
     1788            x_formatter, y_formatter = tick_formatter
     1789            from matplotlib.ticker import FuncFormatter
     1790            from sage.misc.latex import latex
     1791            from sage.symbolic.ring import SR
     1792            if x_formatter is None:
     1793                x_formatter = OldScalarFormatter()
     1794            elif x_formatter in SR:
     1795                from misc import multiple_of_constant
     1796                x_const = x_formatter
     1797                x_formatter = FuncFormatter(lambda n,pos: multiple_of_constant(n,pos,x_const))
     1798            elif x_formatter == "latex":
     1799                x_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n))
     1800            if y_formatter is None:
     1801                y_formatter = OldScalarFormatter()
     1802            elif y_formatter in SR:
     1803                from misc import multiple_of_constant
     1804                y_const = y_formatter
     1805                y_formatter = FuncFormatter(lambda n,pos: multiple_of_constant(n,pos,y_const))
     1806            elif y_formatter == "latex":
     1807                y_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n))
     1808
     1809            subplot.xaxis.set_major_locator(x_locator)
     1810            subplot.yaxis.set_major_locator(y_locator)
     1811            subplot.xaxis.set_major_formatter(x_formatter)
     1812            subplot.yaxis.set_major_formatter(y_formatter)
    16751813           
    16761814            subplot.set_frame_on(True)
    16771815            if axes:
     
    17281866            # For now, set the formatter to the old one, since that is
    17291867            # sort of what we are used to.  We should eventually look at
    17301868            # 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())
     1869           
     1870            from matplotlib.ticker import OldScalarFormatter, MaxNLocator, MultipleLocator, FixedLocator, Locator
     1871            x_locator, y_locator = tick_locator
     1872            if x_locator is None:
     1873                x_locator = MaxNLocator(nbins=9, steps=[1,2,5,10])
     1874            elif isinstance(x_locator,Locator):
     1875                pass
     1876            elif isinstance(x_locator,list):
     1877                x_locator = FixedLocator(x_locator)
     1878            else: # x_locator is a number which can be made a float
     1879                from sage.functions.other import ceil, floor
     1880                if floor(xmax/x_locator)-ceil(xmin/x_locator)>1:
     1881                    x_locator=MultipleLocator(float(x_locator))
     1882                else: # not enough room for two major ticks
     1883                    raise ValueError('Expand the range of the independent variable to allow two multiples of your `tick_locator`.')
     1884            if y_locator is None:
     1885                y_locator = MaxNLocator(nbins=9, steps=[1,2,5,10])
     1886            elif isinstance(y_locator,Locator):
     1887                pass
     1888            elif isinstance(y_locator,list):
     1889                y_locator = FixedLocator(y_locator)
     1890            else: # y_locator is a number which can be made a float
     1891                from sage.functions.other import ceil, floor
     1892                if floor(ymax/y_locator)-ceil(ymin/y_locator)>1:
     1893                    y_locator=MultipleLocator(float(y_locator))
     1894                else: # not enough room for two major ticks
     1895                    raise ValueError('Expand the range of the dependent variable to allow two multiples of your `tick_locator`.')
    17361896
     1897            x_formatter, y_formatter = tick_formatter
     1898            from matplotlib.ticker import FuncFormatter
     1899            from sage.misc.latex import latex
     1900            from sage.symbolic.ring import SR
     1901            if x_formatter is None:
     1902                x_formatter = OldScalarFormatter()
     1903            elif x_formatter in SR:
     1904                from misc import multiple_of_constant
     1905                x_const = x_formatter
     1906                x_formatter = FuncFormatter(lambda n,pos: multiple_of_constant(n,pos,x_const))
     1907            elif x_formatter == "latex":
     1908                x_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n))
     1909            if y_formatter is None:
     1910                y_formatter = OldScalarFormatter()
     1911            elif y_formatter in SR:
     1912                from misc import multiple_of_constant
     1913                y_const = y_formatter
     1914                y_formatter = FuncFormatter(lambda n,pos: multiple_of_constant(n,pos,y_const))
     1915            elif y_formatter == "latex":
     1916                y_formatter = FuncFormatter(lambda n,pos: '$%s$'%latex(n))
     1917
     1918            subplot.xaxis.set_major_locator(x_locator)
     1919            subplot.yaxis.set_major_locator(y_locator)
     1920            subplot.xaxis.set_major_formatter(x_formatter)
     1921            subplot.yaxis.set_major_formatter(y_formatter)
    17371922
    17381923            # Make ticklines go on both sides of the axes
    17391924            #             if xmiddle: