import numpy as np
import pandas as pd
from .type_converters import as_vector
[docs]
def find_closest(
closest_to, list_to_search_in, direction="both", strictly=False, return_index=False
):
"""**Find the closest number in the array from a given number x**
Parameters
----------
closest_to : float
The target number(s) to find the closest of.
list_to_search_in : list
The list of values to look in.
direction : str
``"both"`` for smaller or greater, ``"greater"`` for only greater numbers and ``"smaller"`` for the closest smaller.
strictly : bool
``False`` for strictly superior or inferior or ``True`` for including equal.
return_index : bool
If ``True``, will return the index of the closest value in the list.
Returns
----------
closest : int
The closest number in the array.
Example
----------
.. ipython:: python
import neurokit2 as nk
# Single number
x = nk.find_closest(1.8, [3, 5, 6, 1, 2])
x
y = nk.find_closest(1.8, [3, 5, 6, 1, 2], return_index=True)
y
# Vectorized version
x = nk.find_closest([1.8, 3.6], [3, 5, 6, 1, 2])
x
"""
# Transform to arrays
closest_to = as_vector(closest_to)
list_to_search_in = pd.Series(as_vector(list_to_search_in))
out = [
_find_closest(i, list_to_search_in, direction, strictly, return_index) for i in closest_to
]
if len(out) == 1:
return out[0]
else:
return np.array(out)
# =============================================================================
# Internal
# =============================================================================
def _find_closest(
closest_to, list_to_search_in, direction="both", strictly=False, return_index=False
):
try:
index, closest = _find_closest_single_pandas(
closest_to, list_to_search_in, direction, strictly
)
except ValueError:
index, closest = np.nan, np.nan
if return_index is True:
return index
else:
return closest
# =============================================================================
# Methods
# =============================================================================
def _findclosest_base(x, vals, direction="both", strictly=False):
if direction == "both":
closest = min(vals, key=lambda y: np.abs(y - x))
if direction == "smaller":
if strictly is True:
closest = max(y for y in vals if y < x)
else:
closest = max(y for y in vals if y <= x)
if direction == "greater":
if strictly is True:
closest = min(filter(lambda y: y > x, vals))
else:
closest = min(filter(lambda y: y >= x, vals))
return closest
def _find_closest_single_pandas(x, vals, direction="both", strictly=False):
if direction in ["both", "all"]:
index = (np.abs(vals - x)).idxmin()
if direction in ["smaller", "below"]:
if strictly is True:
index = (np.abs(vals[vals < x] - x)).idxmin()
else:
index = (np.abs(vals[vals <= x] - x)).idxmin()
if direction in ["greater", "above"]:
if strictly is True:
index = (vals[vals > x] - x).idxmin()
else:
index = (vals[vals >= x] - x).idxmin()
closest = vals[index]
return index, closest