Source code for heavyedge.profile
"""Various functions for edge profiles."""
__all__ = [
"preprocess",
"fill_after",
]
import numpy as np
from scipy.ndimage import gaussian_filter1d
from scipy.signal import find_peaks
from scipy.stats import linregress
[docs]
def preprocess(Ys, sigma, std_thres):
"""Preprocess raw profiles.
Parameters
----------
Ys : (N, M) array
Array of N profiles.
sigma : scalar
Standard deviation of Gaussian filter for smoothing.
std_thres : scalar
Standard deviation threshold to detect contact point.
Returns
-------
Ys : (N, M) array
Preprocessed profile data.
Ls : (N,) array
Length of *Y* until the contact point.
Notes
-----
Profiles undergo the following steps:
1. Profile direction is set so that the contact point is on the right hand side.
2. Contact point is detected, and set to have zero height.
Examples
--------
>>> import numpy as np
>>> from heavyedge import get_sample_path, RawProfileCsvs
>>> from heavyedge.profile import preprocess
>>> raw = RawProfileCsvs(get_sample_path("Type3"))
>>> Ys = np.array([raw[i][0] for i in range(len(raw))])
>>> Ys_processed, Ls = preprocess(Ys, 32, 0.01)
>>> import matplotlib.pyplot as plt # doctest: +SKIP
... for Y, L in zip(Ys_processed, Ls):
... plt.plot(Y[:L])
"""
ret_Ys, ret_Ls = [], []
for Y in Ys:
Y_proc, L = _preprocess(Y, sigma, std_thres)
ret_Ys.append(Y_proc)
ret_Ls.append(L)
return np.array(ret_Ys), np.array(ret_Ls)
def _preprocess(Y, sigma, std_thres):
if Y[0] < Y[-1]:
# Make plateau is on the left and cp is on the right
Y = np.flip(Y)
X = np.arange(len(Y))
h_xx = gaussian_filter1d(Y, sigma, order=2, mode="nearest")
if len(h_xx) > 0:
peaks, _ = find_peaks(h_xx)
else:
peaks = np.empty(0, dtype=int)
candidates = []
for i, peak_idx in enumerate(peaks):
x = X[peak_idx:]
if not len(x) > 2:
continue
y = Y[x]
reg = linregress(x, y)
residuals = y - (reg.intercept + reg.slope * x)
std = np.sqrt(np.sum(residuals**2) / (len(x) - 2))
if std < std_thres:
candidates.append(i)
if candidates:
cp = peaks[candidates[np.argmax(h_xx[peaks[candidates]])]]
else:
cp = len(Y) - 1
# If any point before cp is lower than the detected contact point,
# set that as contact point instead.
cp = np.argmin(Y[: cp + 1])
Y = Y - Y[cp]
return Y, cp + 1
[docs]
def fill_after(Ys, Ls, fill_value):
"""Fill arrays with a constant value after specified lengths.
The input array *Ys* is modified.
Parameters
----------
Ys : (N, M) array
Array of N profiles.
Ls : (N,) array
Length of each profile.
fill_value : scalar
Value to fill *Ys*.
Examples
--------
>>> from heavyedge import get_sample_path, ProfileData
>>> from heavyedge.profile import fill_after
>>> with ProfileData(get_sample_path("Prep-Type2.h5")) as data:
... x = data.x()
... Ys, Ls, _ = data[:]
>>> fill_after(Ys, Ls, float("nan"))
>>> import matplotlib.pyplot as plt # doctest: +SKIP
... plt.plot(Ys.T)
"""
_, M = Ys.shape
Ys[np.arange(M)[None, :] >= Ls[:, None]] = fill_value