Ticket #4529: logplots.py

File logplots.py, 19.7 KB (added by ppurka, 9 years ago)

plot in logarithmic scale.

Line 
1from sage.all import *
2import os, types
3
4ALLOWED_EXTENSIONS = ['.eps', '.pdf', '.png', '.ps', '.svg']
5
6class LogGraphics(SageObject):
7    r"""
8    class LogGraphics is use to create a graphics object which is one of
9    the 'loglog', 'semilogx' or 'semilogy' plot type graphic. If both of
10    your axes are linear then use the Graphics() class or plot() function
11    of Sage directly.
12
13    Examples::
14
15    # This declares that p is an instance of type 'loglog' plot
16        sage: p = LogGraphics('loglog')
17        sage: pplot = p(range(10), map(lambda i:e^i, range(10)))
18        sage: q = LogGraphics('loglog')
19        sage: qplot = q(range(10), map(lambda i:5^i, range(10)))
20        sage: r = pplot + qplot # This works
21
22    # This declares that q is an instance of type 'semilogx' plot
23        sage: q = LogGraphics('semilogx')
24        sage: qplot = q(range(10), map(lambda i:5^i, range(10)))
25        sage: r = pplot + qplot # Error. Can not combine different scalings
26    """
27
28    def __add__(self, g):
29        if not isinstance(g, LogGraphics):
30            raise TypeError("You can not add a LogGraphics object to "
31            "a non LogGraphics object")
32        if self._plottype != g._plottype:
33            raise TypeError("Both the graphics must have the same scale "
34            "along both the x-axis and y-axis")
35        G = LogGraphics(self._plottype)
36        G._xdata = self._xdata + g._xdata
37        G._ydata = self._ydata + g._ydata
38        G._options = self._options + g._options
39        return G
40
41    def __call__(self, xdata, ydata, **options):
42        self._xdata = [xdata]
43        self._ydata = [ydata]
44        self._options[0].update(**options)
45        return self
46
47    def __init__(self, plottype):
48        if plottype not in ['loglog', 'semilogx', 'semilogy']:
49            raise ValueError("plottype must be one of 'loglog', 'semilogx'"
50            " or 'semilogy'")
51        self._options   = [dict(plotjoined=True, color='blue')]
52        self._plottype  = plottype
53        self._xdata     = []
54        self._ydata     = []
55
56    def _repr_(self):
57        if sage.plot.plot.SHOW_DEFAULT:
58            self.show()
59            return ''
60        else:
61            return 'LogGraphics object of type %s with %d plots'%(
62                self._plottype, len(self._xdata))
63
64    def matplotlib(self, **options):
65        r"""
66        This function calls the actual matplotlib commands. It uses pylab
67        """
68        axes_labels = []        # x and y axis labels
69        gopts       = dict()    # grid options
70        has_legend  = False     # Whether there is a legend
71        lopts       = dict()    # legend options
72        title       = ''        # Title of the plot
73
74        # Fix the xmin and xmax of the data
75        [xmin, xmax] = self.get_min_max(self._xdata)
76        [ymin, ymax] = self.get_min_max(self._ydata, flag=False)
77
78
79        import matplotlib.pyplot as Pyl
80        Pyl.hold(False)
81        # Ensure that matplotlib does not flip the figure along x-axis
82        Pyl.axis([xmin, xmax, ymin, ymax])
83
84        for i in xrange(len(self._xdata)):
85            o = self._options[i].copy()
86            o.update(**options)
87            if 'plotjoined' in o:
88                if o['plotjoined']:
89                    if 'linestyle' not in o:
90                        o.update(linestyle='-')
91                else:
92                    if 'marker' not in o:
93                        o.update(marker='o')
94                    o.update(linestyle='')
95                del o['plotjoined']
96           
97            for _o in o.copy():
98                if _o == 'axes_labels':
99                    axes_labels = o.pop(_o)
100                elif _o == 'color':
101                    from sage.plot.colors import rgbcolor, Color, to_mpl_color
102                    o.update(color=to_mpl_color(o[_o]))
103                elif _o == 'fancybox':
104                    lopts.update(fancybox=o[_o])
105                    del o[_o]
106                elif _o == 'fontsize':
107                    has_legend = True
108                    from matplotlib.font_properties import FontProperties
109                    f = FontProperties(size=o[_o])
110                    lopts.update(prop=f)
111                    del o[_o]
112                elif _o == 'gridlines':
113                    gopts.update(b=o[_o])
114                    gopts.update(which='both')
115                    del o[_o]
116                elif _o == 'legend_label' or _o == 'legend':
117                    has_legend = True
118                    o.update(label=o[_o])
119                    del o[_o]
120                elif _o == 'linewidth':
121                    o.update(linewidth=float(o[_o]))
122                elif _o == 'loc':
123                    lopts[_o] = o.pop(_o)
124                elif _o == 'title':
125                    title = o.pop(_o)
126                elif _o == 'markersize':
127                    o.update(markersize=float(o[_o]))
128                elif _o == 'rgbcolor':
129                    from sage.plot.colors import rgbcolor, Color, to_mpl_color
130                    o.update(color=to_mpl_color(o[_o]))
131                    del o[_o]
132                elif _o == 'size':
133                    o.update(markersize=float(o[_o]))
134                    del o[_o]
135                elif _o == 'thickness':
136                    o.update(linewidth=float(o[_o]))
137                    del o[_o]
138                   
139            if self._plottype == 'loglog':
140                Pyl.loglog(self._xdata[i], self._ydata[i], **o)
141            elif self._plottype == 'semilogx':
142                Pyl.semilogx(self._xdata[i], self._ydata[i], **o)
143            elif self._plottype == 'semilogy':
144                Pyl.semilogy(self._xdata[i], self._ydata[i], **o)
145
146            # Now set the hold
147            Pyl.hold(True)
148           
149        if len(gopts) > 0:
150            Pyl.grid(**gopts)
151        if has_legend or len(lopts) > 0:
152            Pyl.legend(**lopts)
153        if title != '':
154            Pyl.title(title)
155        if axes_labels != []:
156            Pyl.xlabel(axes_labels[0])
157            Pyl.ylabel(axes_labels[1])
158
159        return Pyl
160
161    def get_min_max(self, list_of_lists, flag=None):
162        """
163        def get_min_max(list_of_lists)
164        Returns the minimum of all possible values among all the lists in
165        list_of_lists.
166       
167        Each list in list_of_lists is assumed to be sorted in either
168        increasing or decreasing order. If any list is sorted in decreasing
169        order, then the final output has lmin > lmax.
170
171        Input:  a list of lists
172        Output: [lmin, lmax], which corresponds to the min and max points
173        of an axis.
174
175        Examples::
176
177            sage: r=[10^-i for i in range(5)]; s=[1/_ for _ in r]
178            sage: LogGraphics('loglog').get_min_max([r, s])
179            [10000, 1/10000]
180            sage: t=[2*_ for _ in s]
181            sage: LogGraphics('loglog').get_min_max([t, s])
182            [1, 20000]
183        """
184        # When calling with .ydata, just get the min and max.
185        # Don't reorder y-axis. To do this, just set flag=False while
186        # calling this function.
187        if flag is None:
188            flag = False
189            for l in list_of_lists:
190                if l[0] > l[-1]:
191                    flag = True
192                    break
193
194        if flag:
195            # The plot will be decreasing along the axis. So, it must start
196            # from largest and go to smallest.
197            lmin = max([l[0]  if l[0] > l[-1] else l[-1] for l in
198                        list_of_lists])
199            lmax = min([l[-1] if l[0] > l[-1] else l[0]  for l in
200                        list_of_lists])
201        else:
202            # This is the usual stuff. values increase along the axis
203            lmin = min([l[0]  if l[0] < l[-1] else l[-1] for l in
204                        list_of_lists])
205            lmax = max([l[-1] if l[0] < l[-1] else l[0]  for l in
206                        list_of_lists])
207
208        return [lmin, lmax]
209
210
211    def save(self, **options):
212        r"""
213        Save the graphics to an image file.
214        """
215        filename = options.pop('filename')
216        if filename is None:
217            filename = sage.misc.misc.graphics_filename()
218        ext = os.path.splitext(filename)[1].lower()
219       
220        if ext not in ALLOWED_EXTENSIONS:
221            raise ValueError("allowed file extensions for images are '"
222                             + "', '".join(ALLOWED_EXTENSIONS) + "'!")
223        else:
224            figure = self.matplotlib(**options)
225            figure.savefig(filename) # matplotlib handles the file ext.
226
227    def show(self, **options):
228        r"""
229        Show the image on the sage notebook or on the system image viewer
230        """
231        # This option should not be passed on to save().
232        linkmode = options.pop('linkmode', False)
233        if sage.misc.latex.EMBEDDED_MODE:
234            options.setdefault('filename', sage.misc.misc.graphics_filename())
235            self.save(**options)
236            if linkmode == True:
237                return "<img src='cell://%s'>" % options['filename']
238            else:
239                html("<img src='cell://%s'>" % options['filename'])
240        else:
241            options.setdefault('filename', sage.misc.misc.tmp_filename()
242                    + '.png')
243            self.save(**options)
244            os.system('%s %s 2>/dev/null 1>/dev/null &'
245                      % (sage.misc.viewer.browser(), options['filename']))
246
247#------------------------------------------------------------------------#
248#                                                                        #
249#   User facing functions are now given below. Main functions are        #
250#   log_list_plot() and log_plot(). log_plot() calls log_list_plot()     #
251#   eventually, but it first generates xdata and ydata.                  #
252#                                                                        #
253#------------------------------------------------------------------------#
254
255
256def log_list_plot(xdata, ydata, scale, **options):
257    r"""
258    log_list_plot(xdata, ydata, scale, **options):
259    Plot a loglog, semilogx, or semilogy plot of the points taken as
260    pairwise tuples from xdata and ydata. xdata is a list of points on the
261    horizontal axis and ydata is the corresponding list on the vertical
262    axis.
263    The scale has to be one of three strings: 'loglog', 'semilogx',
264    'semilogy'.
265    The additional relevant options are:
266        - axes_labels: list of two strings. The first for the horizontal
267          axis and the second for the vertical axis. Default None.
268        - color: set the color of the line or marker. Default 'blue'
269        - fontsize: the size of the fonts used in legend. Default ??
270        - gridlines: whether to draw grid. Default is False
271        - legend_label: the label for the legend in the plot. Default None
272        - legend: (See legend_label)
273        - loc: The location of the legend. See http://goo.gl/G5BBR
274        - marker:   If you want to place a marker at every point then
275          provide it as a string. Examples are '*', 'o', '^', etc.
276          See http://goo.gl/e27PT for all the options. Default 'o'
277        - markersize: the size of the marker. Default ??
278        - size: (See markersize)
279        - title: The title of the plot. Default is None
280        - linestyle: The style of the line. Examples are '-', '--', '-.',
281          etc. Default is '-' if plotjoined is True and '' if False.
282          See http://goo.gl/wn1Cy for other options
283        - plot_points: The number of points to evaluate the function at.
284          Default 200
285        - plotjoined:  Whether to draw a curve through the points at which
286          the function is evaluated. Default True
287        - rgbcolor: tuple of three numbers, each between 0 and 1, which
288          represents the color of the line or marker as an RGB tuple
289        - thickness: the thickness of the line drawn in the plot. Default 1
290        - linewidth: (See thickness)
291
292    Examples::
293       
294        sage: xdata = range(1,10); ydata = map(lambda i: 10^i, xdata)
295        sage: log_list_plot(xdata, ydata, 'semilogy',
296                color='red', plotjoined=False)
297        sage: semilogy_list_plot(xdata, ydata, 'semilogx',
298                rgbcolor=(1,0,0))
299        sage: semilogy_list_plot(xdata, ydata, 'loglog',
300                legend_label='a plot')
301    """
302    if scale != 'loglog' and scale != 'semilogx' and scale != 'semilogy':
303        raise ValueError("scale must be one of 'loglog', 'semilogx' or "
304            "'semilogy'" )
305    return LogGraphics(scale)(xdata, ydata, **options)
306
307def log_plot(f, scale, *args, **options):
308    r"""
309    log_plot(f, scale, *args, **options):
310    Plot a loglog, semilogx, or semilogy plot of the function f. The scale
311    argument must be provided and it has to one of the strings 'loglog',
312    'semilogx' or 'semilogy' to indicate which kind of plot is desired.
313
314    The relevant options are:
315        - axes_labels: list of two strings. The first for the horizontal
316          axis and the second for the vertical axis. Default None.
317        - color: set the color of the line or marker. Default 'blue'
318        - fontsize: the size of the fonts used in legend. Default ??
319        - gridlines: whether to draw grid. Default is False
320        - legend_label: the label for the legend in the plot. Default None
321        - legend: (See legend_label)
322        - loc: The location of the legend. See http://goo.gl/G5BBR
323        - marker:   If you want to place a marker at every point then
324          provide it as a string. Examples are '*', 'o', '^', etc.
325          See http://goo.gl/e27PT for all the options. Default 'o'
326        - markersize: the size of the marker. Default ??
327        - size: (See markersize)
328        - title: The title of the plot. Default is None
329        - linestyle: The style of the line. Examples are '-', '--', '-.',
330          etc. Default is '-' if plotjoined is True and '' if False.
331          See http://goo.gl/wn1Cy for other options
332        - plot_points: The number of points to evaluate the function at.
333          Default 200
334        - plotjoined:  Whether to draw a curve through the points at which
335          the function is evaluated. Default True
336        - rgbcolor: tuple of three numbers, each between 0 and 1, which
337          represents the color of the line or marker as an RGB tuple
338        - thickness: the thickness of the line drawn in the plot. Default 1
339        - linewidth: (See thickness)
340
341    Examples::
342
343        sage: log_plot(e^x, 'loglog', (x, 1, 10), color='red',
344                plotjoined=False)
345        sage: log_plot(e^x, 'semilogx', 1, 10, rgbcolor=(1,0,0),
346                plotjoined=False)
347        sage: log_plot(e^x, 'semilogy', legend_label='a plot', thickness=3)
348        sage: log_plot(e^x, 'semilogy', marker='*', size=20)
349
350    """
351    if scale != 'loglog' and scale != 'semilogx' and scale != 'semilogy':
352        raise ValueError("scale must be one of 'loglog', 'semilogx' or "
353            "'semilogy'" )
354
355    # Don't want to expose this function to outside
356    def _generate_data(f, *args, **options):
357        r"""
358        This function is used internally by log_plot
359        """
360        n = len(args)
361        if n == 0:
362            ranges = (0, 1)
363        elif n == 1:
364            if len(args[0]) == 2:
365                ranges = args[0]
366            elif len(args[0]) == 3:
367                ranges = args[0]
368            else:
369                raise ValueError("You must pass either a tuple of 2 entries "
370                    "[for ex: (1, 2)] or of 3 entries [for ex: (x, 1, 2)] "
371                    "after the function.")
372        elif n == 2 or n == 3:
373            ranges = tuple(args)
374        else:
375            raise KeyError("Too many arguments passed to function. Any extra"
376                " arguments must be passed as keywords [ex: use color='red' "
377                " instead of 'red'].")
378        from sage.plot.misc import setup_for_eval_on_grid
379        from sage.plot.plot import generate_plot_points
380        plot_points = options.pop('plot_points', 200)
381        f, ranges = setup_for_eval_on_grid(f, [ranges], plot_points=plot_points)
382        data = generate_plot_points(f, ranges[0][:2], plot_points)
383        return zip(*data)
384
385    xdata, ydata = _generate_data(f, *args, **options)
386    if 'plot_points' in options:
387        del options['plot_points']
388
389    return log_list_plot(xdata, ydata, scale, **options)
390
391def loglog_list_plot(xdata, ydata, **options):
392    r"""
393    loglog_list_plot(xdata, ydata, **options):
394    Both the horizontal and vertical axes are plotted on a logarithmic
395    scale.
396
397    See the documentation of log_list_plot for all the options.
398
399    Examples::
400       
401        sage: xdata = range(1,10); ydata = map(lambda i: 10^i, xdata)
402        sage: loglog_list_plot(xdata, ydata, color='red', plotjoined=False)
403        sage: loglog_list_plot(xdata, ydata, rgbcolor=(1,0,0))
404        sage: loglog_list_plot(xdata, ydata, legend_label='a plot')
405    """
406    return log_list_plot(xdata, ydata, 'loglog', **options)
407
408def loglog_plot(f, *args, **options):
409    r"""
410    loglog_plot(f, *args, **options):
411    Plot a log-log plot of the function f.  Both the horizontal and
412    vertical axes are on a logarithmic scale.
413
414    See the documention of log_plot for other options.
415
416    Examples::
417
418        sage: loglog_plot(e^x, (x, 1, 10), color='red', plotjoined=False)
419        sage: loglog_plot(e^x, 1, 10, rgbcolor=(1,0,0), plotjoined=False)
420        sage: loglog_plot(e^x, legend_label='a plot', thickness=3)
421        sage: loglog_plot(e^x, marker='*', size=20)
422    """
423    return log_plot(f, 'loglog', *args, **options)
424
425def semilogx_list_plot(xdata, ydata, **options):
426    r"""
427    semilogx_list_plot(xdata, ydata, **options):
428    Plot a semilogx function, with vertical axis on linear scale and
429    horizontal axis on log scale.
430
431    See the documentation of log_list_plot for all the options.
432
433    Examples::
434       
435        sage: xdata = range(1,10); ydata = map(lambda i: 10^i, xdata)
436        sage: semilogx_list_plot(xdata, ydata, color='red', plotjoined=False)
437        sage: semilogx_list_plot(xdata, ydata, rgbcolor=(1,0,0))
438        sage: semilogx_list_plot(xdata, ydata, legend_label='a plot')
439    """
440    return log_list_plot(xdata, ydata, 'semilogx', **options)
441
442def semilogx_plot(f, *args, **options):
443    r"""
444    semilogx_plot(f, *args, **options):
445    Plot a semilogx plot of the function f. The horizontal axis is
446    on a logarithmic scale and the vertical axis is on a linear scale.
447
448    See the documention of log_plot for other options.
449
450    Examples::
451
452        sage: semilogx_plot(e^x, (x, 1, 10), color='red', plotjoined=False)
453        sage: semilogx_plot(e^x, 1, 10, rgbcolor=(1,0,0), plotjoined=False)
454        sage: semilogx_plot(e^x, legend_label='a plot', thickness=3)
455        sage: semilogx_plot(e^x, marker='*', size=20)
456    """
457    return log_plot(f, 'semilogx', *args, **options)
458
459def semilogy_list_plot(xdata, ydata, **options):
460    r"""
461    semilogy_list_plot(xdata, ydata, **options):
462    Plot a semilogy function, with vertical axis on log scale and
463    horizontal axis on linear scale.
464
465    See the documentation of log_list_plot for all the options.
466
467    Examples::
468       
469        sage: xdata = range(1,10); ydata = map(lambda i: 10^i, xdata)
470        sage: semilogy_list_plot(xdata, ydata, color='red', plotjoined=False)
471        sage: semilogy_list_plot(xdata, ydata, rgbcolor=(1,0,0))
472        sage: semilogy_list_plot(xdata, ydata, legend_label='a plot')
473    """
474    return log_list_plot(xdata, ydata, 'semilogy', **options)
475
476def semilogy_plot(f, *args, **options):
477    r"""
478    semilogy_plot(f, *args, **options):
479    Plot a semilogy plot of the function f. The horizontal axis is
480    on a linear scale while the vertical axis is logarithmic scale.
481
482    See the documention of log_plot for other options.
483
484    Examples::
485
486        sage: semilogy_plot(e^x, (x, 1, 10), color='red', plotjoined=False)
487        sage: semilogy_plot(e^x, 1, 10, rgbcolor=(1,0,0), plotjoined=False)
488        sage: semilogy_plot(e^x, legend_label='a plot', thickness=3)
489        sage: semilogy_plot(e^x, marker='*', size=20)
490    """
491    return log_plot(f, 'semilogy', *args, **options)