"""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"][:]