Source code for ccdproc.utils.slices

# Licensed under a 3-clause BSD style license - see LICENSE.rst

"""
Define utility functions and classes for ccdproc
"""

__all__ = ["slice_from_string"]


[docs]def slice_from_string(string, fits_convention=False): """ Convert a string to a tuple of slices. Parameters ---------- string : str A string that can be converted to a slice. fits_convention : bool, optional If True, assume the input string follows the FITS convention for indexing: the indexing is one-based (not zero-based) and the first axis is that which changes most rapidly as the index increases. Returns ------- slice_tuple : tuple of slice objects A tuple able to be used to index a numpy.array Notes ----- The ``string`` argument can be anything that would work as a valid way to slice an array in Numpy. It must be enclosed in matching brackets; all spaces are stripped from the string before processing. Examples -------- >>> import numpy as np >>> arr1d = np.arange(5) >>> a_slice = slice_from_string('[2:5]') >>> arr1d[a_slice] array([2, 3, 4]) >>> a_slice = slice_from_string('[ : : -2] ') >>> arr1d[a_slice] array([4, 2, 0]) >>> arr2d = np.array([arr1d, arr1d + 5, arr1d + 10]) >>> arr2d array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) >>> a_slice = slice_from_string('[1:-1, 0:4:2]') >>> arr2d[a_slice] array([[5, 7]]) >>> a_slice = slice_from_string('[0:2,0:3]') >>> arr2d[a_slice] array([[0, 1, 2], [5, 6, 7]]) """ no_space = string.replace(' ', '') if not no_space: return () if not (no_space.startswith('[') and no_space.endswith(']')): raise ValueError('Slice string must be enclosed in square brackets.') no_space = no_space.strip('[]') if fits_convention: # Special cases first # Flip dimension, with step no_space = no_space.replace('-*:', '::-') # Flip dimension no_space = no_space.replace('-*', '::-1') # Normal wildcard no_space = no_space.replace('*', ':') string_slices = no_space.split(',') slices = [] for string_slice in string_slices: slice_args = [int(arg) if arg else None for arg in string_slice.split(':')] a_slice = slice(*slice_args) slices.append(a_slice) if fits_convention: slices = _defitsify_slice(slices) return tuple(slices)
def _defitsify_slice(slices): """ Convert a FITS-style slice specification into a python slice. This means two things: + Subtract 1 from starting index because in the FITS specification arrays are one-based. + Do **not** subtract 1 from the ending index because the python convention for a slice is for the last value to be one less than the stop value. In other words, this subtraction is already built into python. + Reverse the order of the slices, because the FITS specification dictates that the first axis is the one along which the index varies most rapidly (aka FORTRAN order). """ python_slice = [] for a_slice in slices[::-1]: new_start = a_slice.start - 1 if a_slice.start is not None else None if new_start is not None and new_start < 0: raise ValueError("Smallest permissible FITS index is 1") if a_slice.stop is not None and a_slice.stop < 0: raise ValueError("Negative final index not allowed for FITS slice") new_slice = slice(new_start, a_slice.stop, a_slice.step) if (a_slice.start is not None and a_slice.stop is not None and a_slice.start > a_slice.stop): # FITS use a positive step index when dimension are inverted new_step = -1 if a_slice.step is None else -a_slice.step # Special case to prevent -1 as slice stop value new_stop = None if a_slice.stop == 1 else a_slice.stop-2 new_slice = slice(new_start, new_stop, new_step) python_slice.append(new_slice) return python_slice