Source code for halotools.utils.array_utils
"""
Modules performing small, commonly used tasks throughout the package.
"""
import numpy as np
from astropy.utils.misc import NumpyRNGContext
from ..custom_exceptions import HalotoolsError
__all__ = ('custom_len', 'find_idx_nearest_val', 'randomly_downsample_data',
'array_is_monotonic', 'unsorting_indices')
[docs]
def custom_len(x):
""" Simple method to return a zero-valued 1-D numpy array
with the length of the input x.
Parameters
----------
x : array_like
Can be an iterable such as a list or non-iterable such as a float.
Returns
-------
array_length : int
length of x
Notes
-----
Simple workaround of an awkward feature of numpy. When evaluating
the built-in len() function on non-iterables such as a
float or int, len() returns a TypeError, rather than unity.
Most annoyingly, the same is true on an object such as x=numpy.array(4),
even though such an object formally counts as an Iterable, being an ndarray.
This nuisance is so ubiquitous that it's convenient to have a single
line of code that replaces the default python len() function with sensible behavior.
Examples
--------
>>> x, y, z = 0, [0], None
>>> xlen, ylen, zlen = custom_len(x), custom_len(y), custom_len(z)
"""
try:
array_length = len(x)
except TypeError:
array_length = 1
return array_length
[docs]
def find_idx_nearest_val(array, value):
""" Method returns the index where the input array is closest
to the input value.
Parameters
----------
array : array_like
value : float or int
Returns
-------
idx_nearest : int
Examples
--------
>>> x = np.linspace(0, 1000, num=int(1e5))
>>> val = 45.5
>>> idx_nearest_val = find_idx_nearest_val(x, val)
>>> nearest_val = x[idx_nearest_val]
Notes
------
Use of this function is deprecated. The `numpy.argmin` function provides
equivalent behavior.
"""
if custom_len(array) == 0:
msg = "find_idx_nearest_val method was passed an empty array"
raise HalotoolsError(msg)
idx_sorted = np.argsort(array)
sorted_array = np.array(array[idx_sorted])
idx = np.searchsorted(sorted_array, value, side="left")
if idx >= len(array):
idx_nearest = idx_sorted[len(array)-1]
return idx_nearest
elif idx == 0:
idx_nearest = idx_sorted[0]
return idx_nearest
else:
if abs(value - sorted_array[idx-1]) < abs(value - sorted_array[idx]):
idx_nearest = idx_sorted[idx-1]
return idx_nearest
else:
idx_nearest = idx_sorted[idx]
return idx_nearest
[docs]
def randomly_downsample_data(array, num_downsample, seed=None):
""" Method returns a length-num_downsample random downsampling of the input array.
Parameters
----------
array : array
num_downsample : int
Size of the desired downsampled version of the data
Returns
-------
downsampled_array : array or Astropy Table
Random downsampling of the input array
Examples
--------
>>> x = np.linspace(0, 1000, num=int(1e5))
>>> desired_sample_size = int(1e3)
>>> downsampled_x = randomly_downsample_data(x, desired_sample_size)
"""
num_downsample = int(num_downsample)
input_array_length = custom_len(array)
if num_downsample > input_array_length:
raise SyntaxError("Length of the desired downsampling = %i, "
"which exceeds input array length = %i " % (num_downsample, input_array_length))
else:
with NumpyRNGContext(seed):
randomizer = np.random.random(input_array_length)
idx_sorted = np.argsort(randomizer)
return array[idx_sorted[0:num_downsample]]
[docs]
def array_is_monotonic(array, strict=False):
"""
Method determines whether an input array is monotonic.
Parameters
-----------
array : array_like
strict : bool, optional
If set to True, an array must be strictly monotonic to pass the criteria.
Default is False.
Returns
-------
flag : int
If input ``array`` is monotonically increasing, the returned flag = 1;
if ``array`` is monotonically decreasing, flag = -1. Otherwise, flag = 0.
Examples
--------
>>> x = np.linspace(0, 10, 100)
>>> assert array_is_monotonic(x) == 1
>>> assert array_is_monotonic(x[::-1]) == -1
>>> y = np.ones(100)
>>> assert array_is_monotonic(y) == 1
>>> assert array_is_monotonic(y, strict=True) == 0
Notes
-----
If the input ``array`` is constant-valued, method returns flag = 1.
"""
if custom_len(array) < 3:
msg = ("Input array to the array_is_monotonic method has less then 3 elements")
raise HalotoolsError(msg)
d = np.diff(array)
if strict is True:
if np.all(d > 0):
return 1
elif np.all(d < 0):
return -1
else:
return 0
else:
if np.all(d >= 0):
return 1
elif np.all(d <= 0):
return -1
else:
return 0
[docs]
def unsorting_indices(sorting_indices):
""" Return the indexing array that inverts `numpy.argsort`.
Parameters
----------
sorting_indices : array_like
Length-Npts array - the output of `numpy.argsort`
Returns
-------
unsorting_indices : array_like
Length-Npts array
Examples
--------
>>> x = np.random.rand(100)
>>> idx_sorted = np.argsort(x)
>>> x_sorted = x[idx_sorted]
>>> idx_unsorted = unsorting_indices(idx_sorted)
>>> assert np.all(x == x_sorted[idx_unsorted])
"""
npts = len(sorting_indices)
unsorting_indices = np.zeros_like(sorting_indices, dtype=int)
unsorting_indices[sorting_indices] = np.arange(npts).astype(int)
return unsorting_indices