# Copyright (c) 2006-2012 Filip Wasilewski # Copyright (c) 2012-2016 The PyWavelets Developers # # See COPYING for license details. """ The thresholding helper module implements the most popular signal thresholding functions. """ from __future__ import division, print_function, absolute_import import numpy as np __all__ = ['threshold', 'threshold_firm'] def soft(data, value, substitute=0): data = np.asarray(data) magnitude = np.absolute(data) with np.errstate(divide='ignore'): # divide by zero okay as np.inf values get clipped, so ignore warning. thresholded = (1 - value/magnitude) thresholded.clip(min=0, max=None, out=thresholded) thresholded = data * thresholded if substitute == 0: return thresholded else: cond = np.less(magnitude, value) return np.where(cond, substitute, thresholded) def nn_garrote(data, value, substitute=0): """Non-negative Garrote.""" data = np.asarray(data) magnitude = np.absolute(data) with np.errstate(divide='ignore'): # divide by zero okay as np.inf values get clipped, so ignore warning. thresholded = (1 - value**2/magnitude**2) thresholded.clip(min=0, max=None, out=thresholded) thresholded = data * thresholded if substitute == 0: return thresholded else: cond = np.less(magnitude, value) return np.where(cond, substitute, thresholded) def hard(data, value, substitute=0): data = np.asarray(data) cond = np.less(np.absolute(data), value) return np.where(cond, substitute, data) def greater(data, value, substitute=0): data = np.asarray(data) if np.iscomplexobj(data): raise ValueError("greater thresholding only supports real data") return np.where(np.less(data, value), substitute, data) def less(data, value, substitute=0): data = np.asarray(data) if np.iscomplexobj(data): raise ValueError("less thresholding only supports real data") return np.where(np.greater(data, value), substitute, data) thresholding_options = {'soft': soft, 'hard': hard, 'greater': greater, 'less': less, 'garrote': nn_garrote, # misspelled garrote for backwards compatibility 'garotte': nn_garrote, } def threshold(data, value, mode='soft', substitute=0): """ Thresholds the input data depending on the mode argument. In ``soft`` thresholding [1]_, data values with absolute value less than `param` are replaced with `substitute`. Data values with absolute value greater or equal to the thresholding value are shrunk toward zero by `value`. In other words, the new value is ``data/np.abs(data) * np.maximum(np.abs(data) - value, 0)``. In ``hard`` thresholding, the data values where their absolute value is less than the value param are replaced with `substitute`. Data values with absolute value greater or equal to the thresholding value stay untouched. ``garrote`` corresponds to the Non-negative garrote threshold [2]_, [3]_. It is intermediate between ``hard`` and ``soft`` thresholding. It behaves like soft thresholding for small data values and approaches hard thresholding for large data values. In ``greater`` thresholding, the data is replaced with `substitute` where data is below the thresholding value. Greater data values pass untouched. In ``less`` thresholding, the data is replaced with `substitute` where data is above the thresholding value. Lesser data values pass untouched. Both ``hard`` and ``soft`` thresholding also support complex-valued data. Parameters ---------- data : array_like Numeric data. value : scalar Thresholding value. mode : {'soft', 'hard', 'garrote', 'greater', 'less'} Decides the type of thresholding to be applied on input data. Default is 'soft'. substitute : float, optional Substitute value (default: 0). Returns ------- output : array Thresholded array. See Also -------- threshold_firm References ---------- .. [1] D.L. Donoho and I.M. Johnstone. Ideal Spatial Adaptation via Wavelet Shrinkage. Biometrika. Vol. 81, No. 3, pp.425-455, 1994. DOI:10.1093/biomet/81.3.425 .. [2] L. Breiman. Better Subset Regression Using the Nonnegative Garrote. Technometrics, Vol. 37, pp. 373-384, 1995. DOI:10.2307/1269730 .. [3] H-Y. Gao. Wavelet Shrinkage Denoising Using the Non-Negative Garrote. Journal of Computational and Graphical Statistics Vol. 7, No. 4, pp.469-488. 1998. DOI:10.1080/10618600.1998.10474789 Examples -------- >>> import numpy as np >>> import pywt >>> data = np.linspace(1, 4, 7) >>> data array([ 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. ]) >>> pywt.threshold(data, 2, 'soft') array([ 0. , 0. , 0. , 0.5, 1. , 1.5, 2. ]) >>> pywt.threshold(data, 2, 'hard') array([ 0. , 0. , 2. , 2.5, 3. , 3.5, 4. ]) >>> pywt.threshold(data, 2, 'garrote') array([ 0. , 0. , 0. , 0.9 , 1.66666667, 2.35714286, 3. ]) >>> pywt.threshold(data, 2, 'greater') array([ 0. , 0. , 2. , 2.5, 3. , 3.5, 4. ]) >>> pywt.threshold(data, 2, 'less') array([ 1. , 1.5, 2. , 0. , 0. , 0. , 0. ]) """ try: return thresholding_options[mode](data, value, substitute) except KeyError: # Make sure error is always identical by sorting keys keys = ("'{0}'".format(key) for key in sorted(thresholding_options.keys())) raise ValueError("The mode parameter only takes values from: {0}." .format(', '.join(keys))) def threshold_firm(data, value_low, value_high): """Firm threshold. The approach is intermediate between soft and hard thresholding [1]_. It behaves the same as soft-thresholding for values below `value_low` and the same as hard-thresholding for values above `thresh_high`. For intermediate values, the thresholded value is in between that corresponding to soft or hard thresholding. Parameters ---------- data : array-like The data to threshold. This can be either real or complex-valued. value_low : float Any values smaller then `value_low` will be set to zero. value_high : float Any values larger than `value_high` will not be modified. Notes ----- This thresholding technique is also known as semi-soft thresholding [2]_. For each value, `x`, in `data`. This function computes:: if np.abs(x) <= value_low: return 0 elif np.abs(x) > value_high: return x elif value_low < np.abs(x) and np.abs(x) <= value_high: return x * value_high * (1 - value_low/x)/(value_high - value_low) ``firm`` is a continuous function (like soft thresholding), but is unbiased for large values (like hard thresholding). If ``value_high == value_low`` this function becomes hard-thresholding. If ``value_high`` is infinity, this function becomes soft-thresholding. Returns ------- val_new : array-like The values after firm thresholding at the specified thresholds. See Also -------- threshold References ---------- .. [1] H.-Y. Gao and A.G. Bruce. Waveshrink with firm shrinkage. Statistica Sinica, Vol. 7, pp. 855-874, 1997. .. [2] A. Bruce and H-Y. Gao. WaveShrink: Shrinkage Functions and Thresholds. Proc. SPIE 2569, Wavelet Applications in Signal and Image Processing III, 1995. DOI:10.1117/12.217582 """ if value_low < 0: raise ValueError("value_low must be non-negative.") if value_high < value_low: raise ValueError( "value_high must be greater than or equal to value_low.") data = np.asarray(data) magnitude = np.absolute(data) with np.errstate(divide='ignore'): # divide by zero okay as np.inf values get clipped, so ignore warning. vdiff = value_high - value_low thresholded = value_high * (1 - value_low/magnitude) / vdiff thresholded.clip(min=0, max=None, out=thresholded) thresholded = data * thresholded # restore hard-thresholding behavior for values > value_high large_vals = np.where(magnitude > value_high) if np.any(large_vals[0]): thresholded[large_vals] = data[large_vals] return thresholded