From: <ef...@us...> - 2009-04-04 22:52:59
|
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. |