# Ticket #1431: trac_1431.patch

File trac_1431.patch, 13.4 KB (added by kcrisman, 12 years ago)

Based on 4.2

• ## sage/plot/misc.py

# HG changeset patch
# User Karl-Dieter Crisman <kcrisman@gmail.com>
# Date 1258040665 18000
# Node ID 85754465ba10a3880f3f7c746b73de2692547246
# Parent  f872a92f5f9afff4b4971afc82bf9270faec269b
Trac 1431 - allows easier custom ticks and formatting, particularly for multiples of pi

diff -r f872a92f5f9a -r 85754465ba10 sage/plot/misc.py
 a # we probably have a constant pass return tuple(sorted(vars, key=lambda x: str(x))), tuple(sorted(free_variables, key=lambda x: str(x))) def multiple_of_pi(n,pos): """ Function for internal use in formatting ticks on axes with nice-looking multiples of \pi.  Should only be used via keyword argument tick_formatter='pi' in :meth:plot.show. See documentation for the matplotlib.ticker module for more details. EXAMPLES: Here is the intended use:: sage: plot(sin(x), (x,0,2*pi), tick_locator=pi/3,tick_formatter='pi') Here is an unintended use, which yields unexpected results:: sage: plot(x^2, (x, -2, 2), tick_formatter='pi') """ from sage.symbolic.constants import pi from sage.misc.latex import latex from sage.rings.arith import convergents c=[i for i in convergents(n/pi) if i.denominator()<12] return '$%s$'%latex(c[-1]*pi)
• ## sage/plot/plot.py

diff -r f872a92f5f9a -r 85754465ba10 sage/plot/plot.py
 a sage: plot(x^2,(x,480,500))  # no scientific notation sage: plot(x^2,(x,300,500))  # scientific notation on y-axis But you can fix your own labels, if you know what to expect and have a preference:: sage: plot(x^2,(x,300,500),tick_locator=[None,50000]) Next we construct the reflection of the above polygon about the y-axis by iterating over the list of first-coordinates of the first graphic element of P (which is the actual sage: g2 = plot(2*exp(-30*x) - exp(-3*x), 0, 1) sage: show(graphics_array([g1, g2], 2, 1), xmin=0) Pi Axis: In the PyX manual, the point of this example is to show labeling the X-axis using rational multiples of Pi. Sage currently has no support for controlling how the ticks on the x and y axes are labeled, so this is really a bad example:: Pi Axis:: sage: g1 = plot(sin(x), 0, 2*pi) sage: g2 = plot(cos(x), 0, 2*pi, linestyle = "--") sage: g1 + g2    # show their sum sage: (g1+g2).show(tick_locator=pi/6, tick_formatter='pi')  # show their sum, nicely formatted An illustration of integration:: fontsize=None, aspect_ratio=None, gridlines=None, gridlinesstyle=None, vgridlinesstyle=None, hgridlinesstyle=None,transparent=False, axes_pad=.02) axes_pad=.02, tick_locator=None, tick_formatter=None) def show(self, **kwds): """ etc.  To get axes that are exactly the specified limits, set axes_pad to zero. - tick_locator - A matplotlib locator for the major ticks, or a number. There are several options.  For more information about locators, type from matplotlib import ticker and then ticker?. - If this is a locator object, then it is the locator for the horizontal axis.  A value of None means use the default locator. - If it is a list of two locators, then the first is for the horizontal axis and one for the vertical axis.  A value of None means use the default locator (so a value of [None, my_locator] uses my_locator for the vertical axis and the default for the horizontal axis). - If in either case above one of the entries is a number m (something which can be coerced to a float), it will be replaced by a MultipleLocator which places major ticks at integer multiples of m.  See examples. - tick_formatter - A matplotlib formatter for the major ticks. There are several options.  For more information about formatters, type from matplotlib import ticker and then ticker?. - If this is a formatter object, then it is the formatter for the horizontal axis.  A value of None means use the default locator. - If it is a list of two formatters, then the first is for the horizontal axis and one for the vertical axis.  A value of None means use the default formatter (so a value of [None, my_formatter] uses my_formatter for the vertical axis and the default for the horizontal axis). - If in either case above one of the entries is the string "pi", ticks will be formatted nicely at rational multiples of \pi. This should only be used with a tick_locator option using nice rational multiples of \pi! EXAMPLES:: sage: c = circle((1,1), 1, color='red') sage: plot(sin(x), (x, -pi, pi),thickness=2)+point((pi, -1), pointsize=15) sage: plot(sin(x), (x, -pi, pi),thickness=2,axes_pad=0)+point((pi, -1), pointsize=15) Via matplotlib, Sage allows setting of custom ticks.  See documentation for :meth:show for more details. :: sage: plot(sin(pi*x), (x, -8, 8)) # Labels not so helpful sage: plot(sin(pi*x), (x, -8, 8), tick_locator=2) # Multiples of 2 sage: plot(1.5/(1+e^(-x)), (x, -10, 10)) # doesn't quite show value of inflection point sage: plot(1.5/(1+e^(-x)), (x, -10, 10), tick_locator=[None, 1.5/4]) # It's right at f(x)=0.75! But be careful to leave enough room for at least two major ticks, so that the user can tell what the scale is. :: sage: plot(x^2,(x,1,8),tick_locator=6) Traceback (most recent call last): ... ValueError: Expand the range of the independent variable to allow two multiples of your tick_locator. We can also do custom formatting if you need it, with a built-in one for multiples of \pi.  See documentation for :meth:show for more details. :: sage: plot(sin(x),x,0,2*pi,tick_locator=pi/3,tick_formatter='pi') """ # This option should not be passed on to save(). axes=None, axes_labels=None, fontsize=None, frame=False, verify=True, aspect_ratio = None, gridlines=None, gridlinesstyle=None, vgridlinesstyle=None, hgridlinesstyle=None,axes_pad=0.02): vgridlinesstyle=None, hgridlinesstyle=None,axes_pad=0.02, tick_formatter=None, tick_locator=None): r""" Return a matplotlib figure object representing the graphic :meth:show method (this function accepts all except the transparent argument). """ if not isinstance(tick_locator, (list, tuple)): tick_locator = (tick_locator, None) if not isinstance(tick_formatter, (list, tuple)): tick_formatter = (tick_formatter, None) self.set_axes_range(xmin, xmax, ymin, ymax) d = self.get_axes_range() xmin = d['xmin'] # For now, set the formatter to the old one, since that is # sort of what we are used to.  We should eventually look at # the default one to see if we like it better. from matplotlib.ticker import OldScalarFormatter, MaxNLocator subplot.xaxis.set_major_locator(MaxNLocator(nbins=9)) subplot.yaxis.set_major_locator(MaxNLocator(nbins=9)) subplot.xaxis.set_major_formatter(OldScalarFormatter()) subplot.yaxis.set_major_formatter(OldScalarFormatter()) from matplotlib.ticker import OldScalarFormatter, MaxNLocator, MultipleLocator, Locator x_locator, y_locator = tick_locator if x_locator is None: x_locator = MaxNLocator(nbins=9) elif isinstance(x_locator,Locator): pass else: # x_locator is a number which can be made a float from sage.functions.other import ceil, floor if floor(xmax/x_locator)-ceil(xmin/x_locator)>1: x_locator=MultipleLocator(float(x_locator)) else: # not enough room for two major ticks raise ValueError('Expand the range of the independent variable to allow two multiples of your tick_locator.') if y_locator is None: y_locator = MaxNLocator(nbins=9) elif isinstance(y_locator,Locator): pass else: # y_locator is a number which can be made a float from sage.functions.other import ceil, floor if floor(ymax/y_locator)-ceil(ymin/y_locator)>1: y_locator=MultipleLocator(float(y_locator)) else: # not enough room for two major ticks raise ValueError('Expand the range of the dependent variable to allow two multiples of your tick_locator.') x_formatter, y_formatter = tick_formatter if x_formatter is None: x_formatter = OldScalarFormatter() elif x_formatter=='pi': from misc import multiple_of_pi from matplotlib.ticker import FuncFormatter x_formatter = FuncFormatter(multiple_of_pi) if y_formatter is None: y_formatter = OldScalarFormatter() elif y_formatter=='pi': from misc import multiple_of_pi from matplotlib.ticker import FuncFormatter y_formatter = FuncFormatter(multiple_of_pi) subplot.xaxis.set_major_locator(x_locator) subplot.yaxis.set_major_locator(y_locator) subplot.xaxis.set_major_formatter(x_formatter) subplot.yaxis.set_major_formatter(y_formatter) subplot.set_frame_on(True) if axes: # For now, set the formatter to the old one, since that is # sort of what we are used to.  We should eventually look at # the default one to see if we like it better. from matplotlib.ticker import OldScalarFormatter, MaxNLocator subplot.xaxis.set_major_locator(MaxNLocator(nbins=10,steps=[1,2,5,10])) subplot.yaxis.set_major_locator(MaxNLocator(nbins=10,steps=[1,2,5,10])) subplot.xaxis.set_major_formatter(OldScalarFormatter()) subplot.yaxis.set_major_formatter(OldScalarFormatter()) from matplotlib.ticker import OldScalarFormatter, MaxNLocator, MultipleLocator, Locator x_locator, y_locator = tick_locator if x_locator is None: x_locator = MaxNLocator(nbins=9, steps=[1,2,5,10]) elif isinstance(x_locator,Locator): pass else: # x_locator is a number which can be made a float from sage.functions.other import ceil, floor if floor(xmax/x_locator)-ceil(xmin/x_locator)>1: x_locator=MultipleLocator(float(x_locator)) else: # not enough room for two major ticks raise ValueError('Expand the range of the independent variable to allow two multiples of your tick_locator.') if y_locator is None: y_locator = MaxNLocator(nbins=9, steps=[1,2,5,10]) elif isinstance(y_locator,Locator): pass else: # y_locator is a number which can be made a float from sage.functions.other import ceil, floor if floor(ymax/y_locator)-ceil(ymin/y_locator)>1: y_locator=MultipleLocator(float(y_locator)) else: # not enough room for two major ticks raise ValueError('Expand the range of the dependent variable to allow two multiples of your tick_locator.') x_formatter, y_formatter = tick_formatter if x_formatter is None: x_formatter = OldScalarFormatter() elif x_formatter=='pi': from misc import multiple_of_pi from matplotlib.ticker import FuncFormatter x_formatter = FuncFormatter(multiple_of_pi) if y_formatter is None: y_formatter = OldScalarFormatter() elif y_formatter=='pi': from misc import multiple_of_pi from matplotlib.ticker import FuncFormatter y_formatter = FuncFormatter(multiple_of_pi) subplot.xaxis.set_major_locator(x_locator) subplot.yaxis.set_major_locator(y_locator) subplot.xaxis.set_major_formatter(x_formatter) subplot.yaxis.set_major_formatter(y_formatter) # Make ticklines go on both sides of the axes #             if xmiddle: