Revision: 7023
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=7023&view=rev
Author:   efiring
Date:     2009-04-04 22:52:53 +0000 (Sat, 04 Apr 2009)

Log Message:
-----------
Add log scale option to clip non-positive values instead of masking

Modified Paths:
--------------
    trunk/matplotlib/CHANGELOG
    trunk/matplotlib/doc/api/api_changes.rst
    trunk/matplotlib/examples/pylab_examples/log_demo.py
    trunk/matplotlib/lib/matplotlib/axes.py
    trunk/matplotlib/lib/matplotlib/scale.py

Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG  2009-04-01 20:44:58 UTC (rev 7022)
+++ trunk/matplotlib/CHANGELOG  2009-04-04 22:52:53 UTC (rev 7023)
@@ -1,3 +1,6 @@
+2009-04-04 Allow log axis scale to clip non-positive values to
+           small positive value; this is useful for errorbars. - EF
+
 2009-03-28 Make images handle nan in their array argument.
            A helper, cbook.safe_masked_invalid() was added. - EF
 

Modified: trunk/matplotlib/doc/api/api_changes.rst
===================================================================
--- trunk/matplotlib/doc/api/api_changes.rst    2009-04-01 20:44:58 UTC (rev 
7022)
+++ trunk/matplotlib/doc/api/api_changes.rst    2009-04-04 22:52:53 UTC (rev 
7023)
@@ -19,6 +19,12 @@
 
 Changes for 0.98.x
 ==================
+* Added new keyword parameters *nonposx*, *nonposy* to
+  :class:`matplotlib.axes.Axes` methods that set log scale
+  parameters.  The default is still to mask out non-positive
+  values, but the kwargs accept 'clip', which causes non-positive
+  values to be replaced with a very small positive value.
+
 * Added new :func:`matplotlib.pyplot.fignum_exists` and
   :func:`matplotlib.pyplot.get_fignums`; they merely expose
   information that had been hidden in :mod:`matplotlib._pylab_helpers`.

Modified: trunk/matplotlib/examples/pylab_examples/log_demo.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/log_demo.py        2009-04-01 
20:44:58 UTC (rev 7022)
+++ trunk/matplotlib/examples/pylab_examples/log_demo.py        2009-04-04 
22:52:53 UTC (rev 7023)
@@ -6,21 +6,33 @@
 t = np.arange(0.01, 20.0, 0.01)
 
 # log y axis
-plt.subplot(311)
+plt.subplot(221)
 plt.semilogy(t, np.exp(-t/5.0))
-plt.ylabel('semilogy')
+plt.title('semilogy')
 plt.grid(True)
 
 # log x axis
-plt.subplot(312)
+plt.subplot(222)
 plt.semilogx(t, np.sin(2*np.pi*t))
-plt.ylabel('semilogx')
+plt.title('semilogx')
 plt.grid(True)
 
 # log x and y axis
-plt.subplot(313)
+plt.subplot(223)
 plt.loglog(t, 20*np.exp(-t/10.0), basex=4)
 plt.grid(True)
-plt.ylabel('loglog base 4 on x')
+plt.title('loglog base 4 on x')
 
+# with errorbars: clip non-positive values
+ax = plt.subplot(224)
+ax.set_xscale("log", nonposx='clip')
+ax.set_yscale("log", nonposy='clip')
+
+x = 10.0**np.linspace(0.0, 2.0, 20)
+y = x**2.0
+plt.errorbar(x, y, xerr=0.1*x, yerr=5.0+0.75*y)
+ax.set_ylim(ymin=0.1)
+ax.set_title('Errorbars go negative')
+
+
 plt.show()

Modified: trunk/matplotlib/lib/matplotlib/axes.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/axes.py     2009-04-01 20:44:58 UTC (rev 
7022)
+++ trunk/matplotlib/lib/matplotlib/axes.py     2009-04-04 22:52:53 UTC (rev 
7023)
@@ -3455,6 +3455,10 @@
             plot; see :meth:`matplotlib.axes.Axes.set_xscale` /
             :meth:`matplotlib.axes.Axes.set_yscale` for details
 
+          *nonposx*/*nonposy*: ['mask' | 'clip' ]
+            non-positive values in *x* or *y* can be masked as
+            invalid, or clipped to a very small positive number
+
         The remaining valid kwargs are
         :class:`~matplotlib.lines.Line2D` properties:
 
@@ -3469,9 +3473,11 @@
 
         dx = {'basex': kwargs.pop('basex', 10),
               'subsx': kwargs.pop('subsx', None),
+              'nonposx': kwargs.pop('nonposx', 'mask'),
               }
         dy = {'basey': kwargs.pop('basey', 10),
               'subsy': kwargs.pop('subsy', None),
+              'nonposy': kwargs.pop('nonposy', 'mask'),
               }
 
         self.set_xscale('log', **dx)
@@ -3508,6 +3514,10 @@
             plot; see :meth:`~matplotlib.axes.Axes.set_xscale` for
             details.
 
+          *nonposx*: ['mask' | 'clip' ]
+            non-positive values in *x* can be masked as
+            invalid, or clipped to a very small positive number
+
         The remaining valid kwargs are
         :class:`~matplotlib.lines.Line2D` properties:
 
@@ -3521,6 +3531,7 @@
         if not self._hold: self.cla()
         d = {'basex': kwargs.pop( 'basex', 10),
              'subsx': kwargs.pop( 'subsx', None),
+             'nonposx': kwargs.pop('nonposx', 'mask'),
              }
 
         self.set_xscale('log', **d)
@@ -3554,6 +3565,10 @@
             plot; see :meth:`~matplotlib.axes.Axes.set_yscale` for
             details.
 
+          *nonposy*: ['mask' | 'clip' ]
+            non-positive values in *y* can be masked as
+            invalid, or clipped to a very small positive number
+
         The remaining valid kwargs are
         :class:`~matplotlib.lines.Line2D` properties:
 
@@ -3567,6 +3582,7 @@
         if not self._hold: self.cla()
         d = {'basey': kwargs.pop('basey', 10),
              'subsy': kwargs.pop('subsy', None),
+             'nonposy': kwargs.pop('nonposy', 'mask'),
              }
         self.set_yscale('log', **d)
         b =  self._hold

Modified: trunk/matplotlib/lib/matplotlib/scale.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/scale.py    2009-04-01 20:44:58 UTC (rev 
7022)
+++ trunk/matplotlib/lib/matplotlib/scale.py    2009-04-04 22:52:53 UTC (rev 
7023)
@@ -87,6 +87,10 @@
         return ma.MaskedArray(a, mask=mask)
     return a
 
+def _clip_non_positives(a):
+    a[a <= 0.0] = 1e-300
+    return a
+
 class LogScale(ScaleBase):
     """
     A standard logarithmic scale.  Care is taken so non-positive
@@ -104,14 +108,24 @@
 
     name = 'log'
 
-    class Log10Transform(Transform):
+    class LogTransformBase(Transform):
         input_dims = 1
         output_dims = 1
         is_separable = True
+
+        def __init__(self, nonpos):
+            Transform.__init__(self)
+            if nonpos == 'mask':
+                self._handle_nonpos = _mask_non_positives
+            else:
+                self._handle_nonpos = _clip_non_positives
+
+
+    class Log10Transform(LogTransformBase):
         base = 10.0
 
         def transform(self, a):
-            a = _mask_non_positives(a * 10.0)
+            a = self._handle_nonpos(a * 10.0)
             if isinstance(a, MaskedArray):
                 return ma.log10(a)
             return np.log10(a)
@@ -131,14 +145,11 @@
         def inverted(self):
             return LogScale.Log10Transform()
 
-    class Log2Transform(Transform):
-        input_dims = 1
-        output_dims = 1
-        is_separable = True
+    class Log2Transform(LogTransformBase):
         base = 2.0
 
         def transform(self, a):
-            a = _mask_non_positives(a * 2.0)
+            a = self._handle_nonpos(a * 2.0)
             if isinstance(a, MaskedArray):
                 return ma.log(a) / np.log(2)
             return np.log2(a)
@@ -158,14 +169,11 @@
         def inverted(self):
             return LogScale.Log2Transform()
 
-    class NaturalLogTransform(Transform):
-        input_dims = 1
-        output_dims = 1
-        is_separable = True
+    class NaturalLogTransform(LogTransformBase):
         base = np.e
 
         def transform(self, a):
-            a = _mask_non_positives(a * np.e)
+            a = self._handle_nonpos(a * np.e)
             if isinstance(a, MaskedArray):
                 return ma.log(a)
             return np.log(a)
@@ -190,12 +198,16 @@
         output_dims = 1
         is_separable = True
 
-        def __init__(self, base):
+        def __init__(self, base, nonpos):
             Transform.__init__(self)
             self.base = base
+            if nonpos == 'mask':
+                self._handle_nonpos = _mask_non_positives
+            else:
+                self._handle_nonpos = _clip_non_positives
 
         def transform(self, a):
-            a = _mask_non_positives(a * self.base)
+            a = self._handle_nonpos(a * self.base)
             if isinstance(a, MaskedArray):
                 return ma.log(a) / np.log(self.base)
             return np.log(a) / np.log(self.base)
@@ -224,6 +236,10 @@
         *basex*/*basey*:
            The base of the logarithm
 
+        *nonposx*/*nonposy*: ['mask' | 'clip' ]
+          non-positive values in *x* or *y* can be masked as
+          invalid, or clipped to a very small positive number
+
         *subsx*/*subsy*:
            Where to place the subticks between each major tick.
            Should be a sequence of integers.  For example, in a log10
@@ -235,18 +251,23 @@
         if axis.axis_name == 'x':
             base = kwargs.pop('basex', 10.0)
             subs = kwargs.pop('subsx', None)
+            nonpos = kwargs.pop('nonposx', 'mask')
         else:
             base = kwargs.pop('basey', 10.0)
             subs = kwargs.pop('subsy', None)
+            nonpos = kwargs.pop('nonposy', 'mask')
 
+        if nonpos not in ['mask', 'clip']:
+            raise ValueError("nonposx, nonposy kwarg must be 'mask' or 'clip'")
+
         if base == 10.0:
-            self._transform = self.Log10Transform()
+            self._transform = self.Log10Transform(nonpos)
         elif base == 2.0:
-            self._transform = self.Log2Transform()
+            self._transform = self.Log2Transform(nonpos)
         elif base == np.e:
-            self._transform = self.NaturalLogTransform()
+            self._transform = self.NaturalLogTransform(nonpos)
         else:
-            self._transform = self.LogTransform(base)
+            self._transform = self.LogTransform(base, nonpos)
 
         self.base = base
         self.subs = subs


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to