Skip to content

epymorph.util

epymorph general utility functions and classes.

acceptable_name module-attribute

acceptable_name = compile('^[a-zA-Z][a-zA-Z0-9_-]*$')

A pattern that matches generally acceptable names for use across epymorph.

T module-attribute

T = TypeVar('T')

A module-attribute

A = TypeVar('A')

B module-attribute

B = TypeVar('B')

P module-attribute

P = ParamSpec('P')

K module-attribute

K = TypeVar('K')

V module-attribute

V = TypeVar('V')

K_co module-attribute

K_co = TypeVar('K_co', covariant=True)

V_co module-attribute

V_co = TypeVar('V_co', covariant=True)

N module-attribute

N = TypeVar('N', bound=number)

NDIndices module-attribute

NDIndices = NDArray[intp]

DataT module-attribute

DataT = TypeVar('DataT', bound=generic)

DateValueType module-attribute

DateValueType = void

The numpy dtype used for structured arrays of date and value. numpy doesn't let us type these very explicitly, so this alias is used to express intention, but know it comes with few guarantees.

So basically it implies: [("date", "datetime64[D]"), ("value", DataT)]

T_contra module-attribute

T_contra = TypeVar('T_contra', contravariant=True)

match module-attribute

match = _Matchers()

Convenience constructors for various matchers.

SingletonT module-attribute

SingletonT = TypeVar('SingletonT', bound='Singleton')

ANSIColor

Bases: Enum

BLACK class-attribute instance-attribute

BLACK = 30

RED class-attribute instance-attribute

RED = 31

GREEN class-attribute instance-attribute

GREEN = 32

YELLOW class-attribute instance-attribute

YELLOW = 33

BLUE class-attribute instance-attribute

BLUE = 34

MAGENTA class-attribute instance-attribute

MAGENTA = 35

CYAN class-attribute instance-attribute

CYAN = 36

WHITE class-attribute instance-attribute

WHITE = 37

BRIGHT_BLACK class-attribute instance-attribute

BRIGHT_BLACK = 90

BRIGHT_RED class-attribute instance-attribute

BRIGHT_RED = 91

BRIGHT_GREEN class-attribute instance-attribute

BRIGHT_GREEN = 92

BRIGHT_YELLOW class-attribute instance-attribute

BRIGHT_YELLOW = 93

BRIGHT_BLUE class-attribute instance-attribute

BRIGHT_BLUE = 94

BRIGHT_MAGENTA class-attribute instance-attribute

BRIGHT_MAGENTA = 95

BRIGHT_CYAN class-attribute instance-attribute

BRIGHT_CYAN = 96

BRIGHT_WHITE class-attribute instance-attribute

BRIGHT_WHITE = 97

ANSIStyle

Bases: Enum

RESET class-attribute instance-attribute

RESET = 0

BOLD class-attribute instance-attribute

BOLD = 1

DIM class-attribute instance-attribute

DIM = 2

ITALIC class-attribute instance-attribute

ITALIC = 3

UNDERLINE class-attribute instance-attribute

UNDERLINE = 4

STRIKETHROUGH class-attribute instance-attribute

STRIKETHROUGH = 9

KeyValue

Bases: Generic[K, V], NamedTuple

A generic named tuple for key/value pairs.

key instance-attribute

key: K

value instance-attribute

value: V

CovariantMapping

Bases: Protocol[K_co, V_co]

A type for covariant mappings, which restricts usage to only those methods which are safe under covariance. For many use-cases these limitations are acceptable and wind up simplifying type expression.

keys abstractmethod

keys() -> KeysView[K_co]

items abstractmethod

items() -> ItemsView[K_co, V_co]

values abstractmethod

values() -> ValuesView[V_co]

NumpyTypeError

Bases: Exception

Describes an error checking the type or shape of a numpy array.

Matcher

Bases: Generic[T_contra], ABC

A generic matcher. Returns True if a match, False otherwise.

expected abstractmethod

expected() -> str

Describes what the expected value is.

__call__ abstractmethod

__call__(value: Any) -> bool

MatchAny

Bases: Matcher[Any]

Always matches (returns True).

expected

expected() -> str

Describes what the expected value is.

__call__

__call__(_value: Any) -> bool

MatchEqual

MatchEqual(acceptable: T_contra)

Bases: Matcher[T_contra]

Matches a specific value by checking for equality (==).

expected

expected() -> str

Describes what the expected value is.

__call__

__call__(value: Any) -> bool

MatchAnyIn

MatchAnyIn(acceptable: list[T_contra])

Bases: Matcher[T_contra]

Matches for presence in a list of values (in).

expected

expected() -> str

Describes what the expected value is.

__call__

__call__(value: T_contra) -> bool

MatchDType

MatchDType(*acceptable: DTypeLike)

Bases: Matcher[DTypeLike]

Matches one or more numpy dtypes using np.issubdtype().

expected

expected() -> str

Describes what the expected value is.

__call__

__call__(value: DTypeLike) -> bool

MatchDTypeCast

MatchDTypeCast(*acceptable: DTypeLike)

Bases: Matcher[DTypeLike]

Matches one or more numpy dtypes using np.can_cast(casting='safe').

expected

expected() -> str

Describes what the expected value is.

__call__

__call__(value: DTypeLike) -> bool

MatchShapeLiteral

MatchShapeLiteral(acceptable: tuple[int, ...])

Bases: Matcher[NDArray]

Matches a numpy array shape to a known literal value. (For matching relative to simulation dimensions, you want DataShapeMatcher.)

expected

expected() -> str

Describes what the expected value is.

__call__

__call__(value: NDArray) -> bool

MatchDimensions

MatchDimensions(acceptable: int)

Bases: Matcher[NDArray]

Matches a numpy array purely on the number of dimensions.

expected

expected() -> str

Describes what the expected value is.

__call__

__call__(value: NDArray) -> bool

Event

Event()

Bases: Generic[T]

A typed pub-sub event.

has_subscribers property

has_subscribers: bool

True if at least one listener is subscribed to this event.

subscribe

subscribe(sub: Callable[[T], None]) -> Callable[[], None]

Subscribe a handler to this event. Returns an unsubscribe function.

publish

publish(event: T) -> None

Publish an event occurrence to all current subscribers.

Subscriber

Subscriber()

Utility class to track a list of subscriptions for ease of unsubscription. Consider using this via the subscriptions() context.

subscribe

subscribe(
    event: Event[T], handler: Callable[[T], None]
) -> None

Subscribe through this Subscriber to the given event.

unsubscribe

unsubscribe() -> None

Unsubscribe from all of this Subscriber's subscriptions.

Singleton

Bases: type

A metaclass for classes you want to treat as singletons.

__call__

__call__(*args, **kwargs)

StringBuilder

StringBuilder(indent: str = '')

line

line(line: str = '') -> Self

line_if

line_if(condition: bool, line: str = '') -> Self

lines

lines(lines: Iterable[str]) -> Self

block

block(
    indent: str = "    ",
    *,
    opener: str | None = None,
    closer: str | None = None,
) -> Generator[StringBuilder, None, None]

build

build() -> str

to_lines

to_lines() -> Iterable[str]

normalize_str

normalize_str(value: str) -> str

Normalize a string for permissive search.

normalize_list

normalize_list(values: list[str]) -> list[str]

Normalize a list of strings for permissive search.

ansi_stylize

ansi_stylize(
    text: str,
    color: ANSIColor | None = WHITE,
    style: ANSIStyle | None = None,
) -> str

Uses ANSI escape codes to stylize a given text, which may not work everywhere. Any applied color or style are reset after the text.

Parameters:

  • color (AnsiColor, default: WHITE ) –

    The text color to apply.

  • style (AnsiStyle, default: None ) –

    The text style to apply.

identity

identity(x: T) -> T

A function which just returns the argument it is called with.

constant

constant(x: T) -> Callable[..., T]

A function which returns a constant value, regardless of what arguments its called with.

noop

noop()

A function which does nothing.

call_all

call_all(*fs: Callable[[], Any]) -> None

Given a list of no-arg functions, call all of the functions and return None.

cache_transparent

cache_transparent(
    func: Callable[P, T],
) -> Callable[P, T]

Decorates a function with functools.cache but maintains its signature.

index_where

index_where(
    it: Iterable[T], predicate: Callable[[T], bool]
) -> int

Find the first index of it where predicate evaluates to True. Return -1 if no such value exists.

index_of

index_of(it: Iterable[T], item: T) -> int

Find the first index of it where item evaluates as equal. Return -1 if no such value exists.

list_not_none

list_not_none(it: Iterable[T | None]) -> list[T]

Convert an iterable to a list, skipping any entries that are None.

are_unique

are_unique(xs: Iterable[T]) -> bool

Returns True if all items in the iterable are unique.

are_instances

are_instances(
    xs: list[Any] | tuple[Any], of_type: type[T]
) -> TypeGuard[list[T] | tuple[T]]

TypeGuards a collection to check that all items are instances of the given type (of_type).

filter_unique

filter_unique(xs: Iterable[T]) -> list[T]

Convert an iterable to a list, keeping only the unique values and maintaining the order as first-seen.

filter_with_mask

filter_with_mask(
    xs: Iterable[A],
    predicate: Callable[[A], TypeGuard[B]],
) -> tuple[list[B], list[bool]]

Filters the given iterable for items which match predicate, and also returns a boolean mask the same length as the iterable with the results of predicate for each item.

zip_list

zip_list(
    xs: Iterable[A], ys: Iterable[B]
) -> list[tuple[A, B]]

Zip (strict) two iterables together as a list.

as_dict

as_dict(mapping: CovariantMapping[K, V]) -> dict[K, V]

map_values

map_values(
    f: Callable[[A], B], xs: Mapping[K, A]
) -> dict[K, B]

Maps the values of a Mapping into a dict by applying the given function.

normalize

normalize(arr: NDArray[N]) -> NDArray[N]

Normalize the values in an array by subtracting the min and dividing by the range.

row_normalize

row_normalize(
    arr: NDArray[N],
    row_sums: NDArray[N] | None = None,
    dtype: DTypeLike = None,
) -> NDArray[N]

Assuming arr is a 2D array, normalize values across each row by dividing by the row sum. If you've already calculated row sums, you can pass those in, otherwise they will be computed.

prefix

prefix(
    length: int,
) -> Callable[[NDArray[str_]], NDArray[str_]]

A vectorized operation to return the prefix of each value in an NDArray of strings.

mask

mask(
    length: int, selection: slice | list[int]
) -> NDArray[bool_]

Creates a boolean mask of a given length where all elements identified by selection are True and all others are False. The selection can be a slice or a list of indices.

pairwise_haversine

pairwise_haversine(
    coordinates: NDArray
    | tuple[NDArray[float64], NDArray[float64]],
    *,
    units: Literal["miles", "kilometers"] = "miles",
    radius: float | None = None,
) -> NDArray[float64]

Compute the distances between all pairs of coordinates.

Parameters:

  • coordinates (NDArray | tuple[NDArray, NDArray]) –

    The coordinates, given in one of two forms: either a structured numpy array with dtype [("longitude", np.float64), ("latitude", np.float64)] or a tuple of two numpy arrays, the first containing longitudes and the second latitudes. The coordinates must be given in degrees.

  • units (Literal["miles", "kilometers"] = "miles",, default: 'miles' ) –

    The units of distance to use for the result, unless radius is given.

  • radius (float, default: None ) –

    The radius of the Earth to use in calculating the results. If not given, we will use an appropriate value for the given units. Since the value of radius implies the distance units being used, if you specify radius the value of units is ignored.

Returns:

  • NDArray[np.float64] :

    An NxN array of distances where N is the number of coordinates given, representing the distance between each pair of coordinates. The output maintains the same ordering of coordinates as the input.

Raises:

  • ValueError :

    if coordinates are not given in an expected format

top

top(size: int, arr: NDArray) -> NDIndices

Find the top size elements in arr and return their indices. Assumes the array is flat and the kind of thing that can be order-compared.

bottom

bottom(size: int, arr: NDArray) -> NDIndices

Find the bottom size elements in arr and return their indices. Assumes the array is flat and the kind of thing that can be order-compared.

is_square

is_square(arr: NDArray) -> bool

Is this numpy array 2 dimensions and square in shape?

is_numeric

is_numeric(
    arr: NDArray,
) -> TypeGuard[NDArray[integer | floating]]

Is this numpy array a numeric (non-complex) type?

shape_matches

shape_matches(
    arr: NDArray, expected: tuple[int | Literal["?"], ...]
) -> bool

Does the shape of the given array match this expression? Shape expressions are a tuple where each dimension is either an integer or a '?' character to signify any length is allowed.

dtype_name

dtype_name(d: dtype) -> str

Tries to return the most-human-readable name for a numpy dtype.

date_value_dtype

date_value_dtype(value_dtype: DTypeLike) -> dtype

is_date_value_dtype

is_date_value_dtype(
    dtype: dtype | type[generic],
    *,
    value_dtype: dtype | type[generic] | None = None,
) -> TypeGuard[DateValueType]

Check if the given dtype is structured with 'date' and 'value' fields.

Parameters:

  • dtype (dtype | type[generic]) –

    The dtype to check.

  • value_dtype (dtype | type[generic] | None, default: None ) –

    The optional expected dtype of the 'value' field. If None (default), the dtype of the 'value' field is not checked.

Returns:

See Also

epymorph.util.is_date_value_array for examples.

is_date_value_array

is_date_value_array(
    array: NDArray,
    *,
    value_dtype: type[DataT] | None = None,
) -> TypeGuard[NDArray[DateValueType]]

Check if the given array is a structured array with 'date' and 'value' fields.

Parameters:

  • array (NDArray) –

    The array to check.

  • value_dtype (type[DataT], default: None ) –

    The optional expected dtype of the 'value' field. If None (default), the dtype of the 'value' field is not checked.

Returns:

Examples:

1
2
3
4
5
6
7
>>> import numpy as np
>>> array = np.array([('2021-01-01', 10), ('2021-01-02', 20)],
                     dtype=[('date', 'datetime64[D]'), ('value', np.int64)])
>>> is_date_value_array(array)
True
>>> is_date_value_array(array, value_dtype=np.int64)
True

to_date_value_array

to_date_value_array(
    dates: NDArray[datetime64], values: NDArray[DataT]
) -> NDArray[DateValueType]

Combine separate arrays of dates and values to create a structured date/value array with named fields. The given dates will be broadcast against the given values, so they must have the same first-dimension.

Parameters:

  • dates (NDArray[datetime64]) –

    An 1D array of dates.

  • values (NDArray[DataT]) –

    An array of values corresponding to the dates, the first dimension of this array must be equal to the number of dates.

Returns:

Raises:

  • ValueError

    If the dimensionality of the arrays is not compatible.

Examples:

>>> import numpy as np
>>> dates = np.array(['2021-01-01', '2021-01-02'], dtype='datetime64[D]')
>>> values = np.array([[10, 20], [30, 40]])
>>> to_date_value_array(dates, values)
array(
    [
        [('2021-01-01', 10), ('2021-01-01', 20)],
        [('2021-01-02', 30), ('2021-01-02', 40)],
    ],
    dtype=[('date', 'datetime64[D]'), ('value', '<i8')],
)

extract_date_value

extract_date_value(
    date_values: NDArray[DateValueType],
    value_dtype: type[DataT] | None = None,
) -> tuple[NDArray[datetime64], NDArray[DataT]]

Extract separate arrays of dates and values from a structured date/value array.

Parameters:

  • date_values (NDArray[DateValueType]) –

    A structured array with fields 'date' and 'value'.

  • value_dtype (type[DataT], default: None ) –

    The expected dtype of the 'value' field. If None (default), the dtype of the 'value' field is not checked.

Returns:

Raises:

  • ValueError

    If value_dtype is provided and the dtype of the 'value' field does not match.

Examples:

1
2
3
4
5
6
>>> import numpy as np
>>> date_values = np.array([('2021-01-01', 10), ('2021-01-02', 20)],
                           dtype=[('date', 'datetime64[D]'), ('value', np.int64)])
>>> extract_date_value(date_values)
(array(['2021-01-01', '2021-01-02'], dtype='datetime64[D]'),
 array([10, 20]))

check_ndarray

check_ndarray(
    value: Any,
    *,
    dtype: Matcher[DTypeLike] = MatchAny(),
    shape: Matcher[NDArray] = MatchAny(),
) -> None

Checks that a value is a numpy array matching the given dtype and shape Matchers. Raises a NumpyTypeError if a check doesn't pass.

progress

progress(percent: float, length: int = 20) -> str

Creates a progress bar string.

subscriptions

subscriptions() -> Generator[Subscriber, None, None]

Manage a subscription context, where all subscriptions added through the returned Subscriber will be automatically unsubscribed when the context closes.

string_builder

string_builder(
    indent: str = "",
    *,
    opener: str | None = None,
    closer: str | None = None,
) -> Generator[StringBuilder, None, None]