Source code for heavyedge.io.profile

"""Processed profile data files."""

import numbers
import warnings
from collections.abc import Sequence
from pathlib import Path

import h5py
import numpy as np

__all__ = [
    "ProfileData",
]


def _deprecated(version, replace):
    removed_version = str(int(version.split(".")[0]) + 1) + ".0"

    def decorator(func):
        def wrapper(*args, **kwargs):
            warnings.warn(
                f"{func.__name__}() is deprecated since HeavyEdge {version} "
                f"and will be removed in {removed_version}. "
                f"Use {replace} instead.",
                category=DeprecationWarning,
                stacklevel=2,
            )
            return func(*args, **kwargs)

        return wrapper

    return decorator


[docs] class ProfileData: """Preprocessed 1-dimensional profile data as hdf5 file. Parameters ---------- path : pathlike Path to the hdf5 file. mode : {'r', 'w', 'r+', 'a', 'w-'} Mode to open the file. kwargs : dict Optional arguments passed to :class:`h5py.File`. Notes ----- ``self[key]`` returns a tuple of full profile data, profile length(s) and profile name(s). If ``key`` is a sequence, it must be sorted in ascending order. Examples -------- >>> from heavyedge import get_sample_path, ProfileData >>> with ProfileData(get_sample_path("Prep-Type3.h5")) as data: ... Ys, _, _ = data[:] >>> import matplotlib.pyplot as plt # doctest: +SKIP ... plt.plot(Ys.T) """ def __init__(self, path, mode="r", **kwargs): self.path = Path(path).expanduser() self._file = h5py.File(path, mode, **kwargs) def __enter__(self): return self def __exit__(self, type, value, trace_back): self._file.close() def __len__(self): return self.shape()[0] def __getitem__(self, key): if isinstance(key, numbers.Integral): profile = self._file["profiles"][key] length = self._file["len"][key] name = str(self._file["names"][key], encoding="utf-8") return (profile, length, name) elif isinstance(key, (slice, Sequence, np.ndarray)): profiles = self._file["profiles"][key] lengths = self._file["len"][key] names = np.char.decode( self._file["names"][key].astype("S"), encoding="utf-8" ) return (profiles, lengths, names) else: raise TypeError(f"Invalid index type: {type(key)}") def close(self): self._file.close()
[docs] def create(self, M, resolution, name=None): """Create datasets and write metadata. Parameters ---------- M : int Maximum length of profile data. resolution : float Spatial resolution of the profile data. name : str, optional Unique name to identify the dataset. Returns ------- obj Returns the object itself. """ if name is None: name = str(self.path.with_suffix("")) self._file.attrs["name"] = name self._file.attrs["res"] = resolution self._file.create_dataset( "profiles", (0, M), maxshape=(None, M), dtype=float, ) self._file.create_dataset( "len", (0,), maxshape=(None,), dtype=int, ) self._file.create_dataset( "names", (0,), maxshape=(None,), dtype=h5py.string_dtype(), ) return self
[docs] def name(self): """Unique name of the dataset. Returns ------- str """ return self._file.attrs["name"]
[docs] def resolution(self): """Spatial resolution of the profile data. Returns ------- float """ return self._file.attrs["res"]
[docs] def shape(self): """Shape of profile dataset. Returns ------- (N, M) """ return self._file["profiles"].shape
[docs] def x(self): """Spatial coordinates. Returns ------- (M,) ndarray """ return np.arange(self.shape()[1]) / self.resolution()
[docs] def write_profiles(self, profiles, lengths, names): """Append profiles data to file. Parameters ---------- profiles : (N, M) ndarray of float 1-dimensional profile. lengths : (N,) array of int Number of data in *profiles* from reference point to contact point. names : list of str Profile names. """ N = len(profiles) dset = self._file["profiles"] dset.resize(dset.shape[0] + N, axis=0) dset[-N:] = profiles dset = self._file["len"] dset.resize(dset.shape[0] + N, axis=0) dset[-N:] = lengths dset = self._file["names"] dset.resize(dset.shape[0] + N, axis=0) dset[-N:] = names
[docs] def profiles(self): """Yield profiles. Profiles are cropped by the contact point. Yields ------ 1-D ndarray """ for profile, length in zip(self._file["profiles"], self._file["len"]): yield profile[:length]
@_deprecated("1.5", "__getitem__() method") def profile_names(self): """Yield profile names. .. deprecated:: 1.5 This method will be removed in HeavyEdge 2.0. Directly iterate over the object instead. Yields ------ str """ for name in self._file["names"]: yield str(name, encoding="utf-8") @_deprecated("1.5", "__getitem__() method") def all_profiles(self): """Return all profiles. .. deprecated:: 1.5 This method will be removed in HeavyEdge 2.0. Directly index the object instead. Returns ------- (N, M) ndarray All N profiles data. """ return self._file["profiles"][:]