/specutils/manipulation/smoothing.py

https://github.com/adrn/specutils
Python | 251 lines | 75 code | 36 blank | 140 comment | 15 complexity | 20303ec1a9ed2d3ce93f95b718d0d373 MD5 | raw file
  1. import copy
  2. import warnings
  3. from astropy import convolution
  4. from astropy.nddata import StdDevUncertainty, VarianceUncertainty, InverseVariance
  5. import astropy.units as u
  6. from astropy.utils.exceptions import AstropyUserWarning
  7. from scipy.signal import medfilt
  8. import numpy as np
  9. from ..spectra import Spectrum1D
  10. __all__ = ['convolution_smooth', 'box_smooth', 'gaussian_smooth',
  11. 'trapezoid_smooth', 'median_smooth']
  12. def convolution_smooth(spectrum, kernel):
  13. """
  14. Apply a convolution based smoothing to the spectrum. The kernel must be one
  15. of the 1D kernels defined in `astropy.convolution`.
  16. This method can be used along but also is used by other specific methods below.
  17. If the spectrum uncertainty exists and is StdDevUncertainty, VarianceUncertainty or InverseVariance
  18. then the errors will be propagated through the convolution using a standard propagation of errors. The
  19. covariance is not considered, currently.
  20. Parameters
  21. ----------
  22. spectrum : `~specutils.Spectrum1D`
  23. The `~specutils.Spectrum1D` object to which the smoothing will be applied.
  24. kernel : `astropy.convolution.Kernel1D` subclass or array.
  25. The convolution based smoothing kernel - anything that `astropy.convolution.convolve` accepts.
  26. Returns
  27. -------
  28. spectrum : `~specutils.Spectrum1D`
  29. Output `~specutils.Spectrum1D` which is copy of the one passed in with the updated flux.
  30. Raises
  31. ------
  32. ValueError
  33. In the case that ``spectrum`` and ``kernel`` are not the correct types.
  34. """
  35. # Parameter checks
  36. if not isinstance(spectrum, Spectrum1D):
  37. raise ValueError('The spectrum parameter must be a Spectrum1D object')
  38. # Get the flux of the input spectrum
  39. flux = spectrum.flux
  40. # Smooth based on the input kernel
  41. smoothed_flux = convolution.convolve(flux, kernel)
  42. # Propagate the uncertainty if it exists...
  43. uncertainty = copy.deepcopy(spectrum.uncertainty)
  44. if uncertainty is not None:
  45. if isinstance(uncertainty, StdDevUncertainty):
  46. # Convert
  47. values = uncertainty.array
  48. ivar_values = 1 / values**2
  49. # Propagate
  50. prop_ivar_values = convolution.convolve(ivar_values, kernel)
  51. # Put back in
  52. uncertainty.array = 1 / np.sqrt(prop_ivar_values)
  53. elif isinstance(uncertainty, VarianceUncertainty):
  54. # Convert
  55. values = uncertainty.array
  56. ivar_values = 1 / values
  57. # Propagate
  58. prop_ivar_values = convolution.convolve(ivar_values, kernel)
  59. # Put back in
  60. uncertainty.array = 1 / prop_ivar_values
  61. elif isinstance(uncertainty, InverseVariance):
  62. # Convert
  63. ivar_values = uncertainty.array
  64. # Propagate
  65. prop_ivar_values = convolution.convolve(ivar_values, kernel)
  66. # Put back in
  67. uncertainty.array = prop_ivar_values
  68. else:
  69. uncertainty = None
  70. warnings.warn("Uncertainty is {} but convolutional error propagation is not defined for that type. Uncertainty will be dropped in the convolved spectrum.".format(type(uncertainty)),
  71. AstropyUserWarning)
  72. # Return a new object with the smoothed flux.
  73. return Spectrum1D(flux=u.Quantity(smoothed_flux, spectrum.unit),
  74. spectral_axis=u.Quantity(spectrum.spectral_axis,
  75. spectrum.spectral_axis_unit),
  76. wcs=spectrum.wcs,
  77. uncertainty=uncertainty,
  78. velocity_convention=spectrum.velocity_convention,
  79. rest_value=spectrum.rest_value)
  80. def box_smooth(spectrum, width):
  81. """
  82. Smooth a `~specutils.Spectrum1D` instance based on a `astropy.convolution.Box1DKernel` kernel.
  83. Parameters
  84. ----------
  85. spectrum : `~specutils.Spectrum1D`
  86. The spectrum object to which the smoothing will be applied.
  87. width : number
  88. The width of the kernel, in pixels, as defined in `astropy.convolution.Box1DKernel`
  89. Returns
  90. -------
  91. spectrum : `~specutils.Spectrum1D`
  92. Output `~specutils.Spectrum1D` which a copy of the one passed in with the updated flux.
  93. Raises
  94. ------
  95. ValueError
  96. In the case that ``width`` is not the correct type or value.
  97. """
  98. # Parameter checks
  99. if not isinstance(width, (int, float)) or width <= 0:
  100. raise ValueError('The width parameter, {}, must be a number greater than 0'.format(
  101. width))
  102. # Create the gaussian kernel
  103. box1d_kernel = convolution.Box1DKernel(width)
  104. # Call and return the convolution smoothing.
  105. return convolution_smooth(spectrum, box1d_kernel)
  106. def gaussian_smooth(spectrum, stddev):
  107. """
  108. Smooth a `~specutils.Spectrum1D` instance based on a `astropy.convolution.Gaussian1DKernel`.
  109. Parameters
  110. ----------
  111. spectrum : `~specutils.Spectrum1D`
  112. The spectrum object to which the smoothing will be applied.
  113. stddev : number
  114. The stddev of the kernel, in pixels, as defined in `astropy.convolution.Gaussian1DKernel`
  115. Returns
  116. -------
  117. spectrum : `~specutils.Spectrum1D`
  118. Output `~specutils.Spectrum1D` which is copy of the one passed in with the updated flux.
  119. Raises
  120. ------
  121. ValueError
  122. In the case that ``stddev`` is not the correct type or value.
  123. """
  124. # Parameter checks
  125. if not isinstance(stddev, (int, float)) or stddev <= 0:
  126. raise ValueError('The stddev parameter, {}, must be a number greater than 0'.format(
  127. stddev))
  128. # Create the gaussian kernel
  129. gaussian_kernel = convolution.Gaussian1DKernel(stddev)
  130. # Call and return the convolution smoothing.
  131. return convolution_smooth(spectrum, gaussian_kernel)
  132. def trapezoid_smooth(spectrum, width):
  133. """
  134. Smoothing based on a `astropy.convolution.Trapezoid1DKernel` kernel.
  135. Parameters
  136. ----------
  137. spectrum : `~specutils.Spectrum1D`
  138. The `~specutils.Spectrum1D` object to which the smoothing will be applied.
  139. width : number
  140. The width of the kernel, in pixels, as defined in `astropy.convolution.Trapezoid1DKernel`
  141. Returns
  142. -------
  143. spectrum : `~specutils.Spectrum1D`
  144. Output `~specutils.Spectrum1D` which is copy of the one passed in with the updated flux.
  145. Raises
  146. ------
  147. ValueError
  148. In the case that ``width`` is not the correct type or value.
  149. """
  150. # Parameter checks
  151. if not isinstance(width, (int, float)) or width <= 0:
  152. raise ValueError('The stddev parameter, {}, must be a number greater than 0'.format(
  153. width))
  154. # Create the gaussian kernel
  155. trapezoid_kernel = convolution.Trapezoid1DKernel(width)
  156. # Call and return the convolution smoothing.
  157. return convolution_smooth(spectrum, trapezoid_kernel)
  158. def median_smooth(spectrum, width):
  159. """
  160. Smoothing based on a median filter. The median filter smoothing
  161. is implemented using the `scipy.signal.medfilt` function.
  162. Parameters
  163. ----------
  164. spectrum : `~specutils.Spectrum1D`
  165. The `~specutils.Spectrum1D` object to which the smoothing will be applied.
  166. width : number
  167. The width of the median filter in pixels.
  168. Returns
  169. -------
  170. spectrum : `~specutils.Spectrum1D`
  171. Output `~specutils.Spectrum1D` which is copy of the one passed in with the updated flux.
  172. Raises
  173. ------
  174. ValueError
  175. In the case that ``spectrum`` or ``width`` are not the correct type or value.
  176. """
  177. # Parameter checks
  178. if not isinstance(spectrum, Spectrum1D):
  179. raise ValueError('The spectrum parameter must be a Spectrum1D object')
  180. if not isinstance(width, (int, float)) or width <= 0:
  181. raise ValueError('The stddev parameter, {}, must be a number greater than 0'.format(
  182. width))
  183. # Get the flux of the input spectrum
  184. flux = spectrum.flux
  185. # Smooth based on the input kernel
  186. smoothed_flux = medfilt(flux, width)
  187. # Return a new object with the smoothed flux.
  188. return Spectrum1D(flux=u.Quantity(smoothed_flux, spectrum.unit),
  189. spectral_axis=u.Quantity(spectrum.spectral_axis,
  190. spectrum.spectral_axis_unit),
  191. wcs=spectrum.wcs,
  192. velocity_convention=spectrum.velocity_convention,
  193. rest_value=spectrum.rest_value)