# 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()
```