Manipulating cubes

Modifying the spectral axis

As mentioned in Accessing data, it is straightforward to find the coordinates along the spectral axis using the spectral_axis attribute:

>>> cube.spectral_axis  
[ -2.97198762e+03  -2.63992044e+03  -2.30785327e+03  -1.97578610e+03
  -1.64371893e+03  -1.31165176e+03  -9.79584583e+02  -6.47517411e+02
  ...
   3.15629983e+04   3.18950655e+04   3.22271326e+04   3.25591998e+04
   3.28912670e+04   3.32233342e+04] m / s

The default units of a spectral axis are determined from the FITS header or WCS object used to initialize the cube, but it is also possible to change the spectral axis unit using with_spectral_unit():

>>> from astropy import units as u
>>> cube2 = cube.with_spectral_unit(u.km / u.s)  
>>> cube2.spectral_axis  
[ -2.97198762e+00  -2.63992044e+00  -2.30785327e+00  -1.97578610e+00
  -1.64371893e+00  -1.31165176e+00  -9.79584583e-01  -6.47517411e-01
  ...
   3.02347296e+01   3.05667968e+01   3.08988639e+01   3.12309311e+01
   3.15629983e+01   3.18950655e+01   3.22271326e+01   3.25591998e+01
   3.28912670e+01   3.32233342e+01] km / s

It is also possible to change from velocity to frequency for example, but this requires specifying the rest frequency or wavelength as well as a convention for the doppler shift calculation:

>>> cube3 = cube.with_spectral_unit(u.GHz, velocity_convention='radio',
...                                 rest_value=200 * u.GHz)  
[ 220.40086492  220.40062079  220.40037667  220.40013254  220.39988841
  220.39964429  220.39940016  220.39915604  220.39891191  220.39866778
  ...
  220.37645231  220.37620818  220.37596406  220.37571993  220.3754758
  220.37523168  220.37498755  220.37474342  220.3744993   220.37425517] GHz

The new cubes will then preserve the new spectral units when computing moments for example (see Moment maps and statistics).

Extracting a spectral slab

Given a spectral cube, it is easy to extract a sub-cube covering only a subset of the original range in the spectral axis. To do this, you can use the spectral_slab() method. This method takes lower and upper bounds for the spectral axis, as well as an optional rest frequency, and returns a new SpectralCube instance. The bounds can be specified as a frequency, wavelength, or a velocity but the units have to match the type of the spectral units in the cube (if they do not match, first use with_spectral_unit() to ensure that they are in the same units). The bounds should be given as Astropy Quantities as follows:

>>> from astropy import units as u
>>> subcube = cube.spectral_slab(-50 * u.km / u.s, +50 * u.km / u.s)  

The resulting cube subcube (which is also a SpectralCube instance) then contains all channels that overlap with the range -50 to 50 km/s relative to the rest frequency assumed by the world coordinates, or the rest frequency specified by a prior call to with_spectral_unit().

Extracting a sub-cube by indexing

It is also easy to extract a sub-cube from pixel coordinates using standard Numpy slicing notation:

>>> sub_cube = cube[:100, 10:50, 10:50]  

This returns a new SpectralCube object with updated WCS information.

Extracting a subcube from a ds9 region

Starting with spectral_cube v0.2, you can use ds9 regions to extract subcubes. The minimal enclosing subcube will be extracted with a two-dimensional mask corresponding to the ds9 region. pyregion is required for region parsing:

>>> region_list = pyregion.open('file.reg')  
>>> sub_cube = cube.subcube_from_ds9region(region_list)  

If you want to loop over individual regions with a single region file, you need to convert the individual region to a shape list due to limitations in pyregion:

>>> region_list = pyregion.open('file.reg')  
>>> for region in region_list: 
>>>     sub_cube = cube.subcube_from_ds9region(pyregion.ShapeList([region])) 

You can also create a region on the fly using ds9 region syntax. This extracts a 0.1 degree circle around the Galactic Center:

>>> region_list = pyregion.parse("galactic; circle(0,0,0.1)")  
>>> sub_cube = cube.subcube_from_ds9region(region_list)  

Extract the minimal valid subcube

If you have a mask that masks out some of the cube edges, such that the resulting sub-cube might be smaller in memory, it can be useful to extract the minimal enclosing sub-cube:

>>> sub_cube = cube.minimal_subcube()  

You can also shrink any cube by this mechanism:

>>> sub_cube = cube.with_mask(smaller_region).minimal_subcube()