import numpy as np
from scipy import signal
from sklearn.utils.extmath import _incremental_mean_and_var
# from: https://github.com/scikit-learn/scikit-learn/blob/0.22.2/sklearn/preprocessing/_data.py
def _handle_zeros_in_scale(scale, copy=True):
"""Makes sure that whenever scale is zero, we handle it correctly.
This happens in most scalers when we have constant features."""
# if we are fitting on 1D arrays, scale might be a scalar
if np.isscalar(scale):
if scale == 0.0:
scale = 1.0
return scale
elif isinstance(scale, np.ndarray):
if copy:
# New array to avoid side-effects
scale = scale.copy()
scale[scale == 0.0] = 1.0
return scale
def _sign(x):
isnumpy = isinstance(x, np.ndarray)
isscalar = np.isscalar(x)
return np.sign(x) if isnumpy or isscalar else x.sign()
def _log1p(x):
isnumpy = isinstance(x, np.ndarray)
isscalar = np.isscalar(x)
return np.log1p(x) if isnumpy or isscalar else x.log1p()
def _abs(x):
isnumpy = isinstance(x, np.ndarray)
isscalar = np.isscalar(x)
return np.abs(x) if isnumpy or isscalar else x.abs()
def _asint(x):
# ugly wrapper to support torch/numpy arrays
isnumpy = isinstance(x, np.ndarray)
isscalar = np.isscalar(x)
return x.astype(int) if isnumpy else int(x) if isscalar else x.long()
def _asfloat(x):
# ugly wrapper to support torch/numpy arrays
isnumpy = isinstance(x, np.ndarray)
isscalar = np.isscalar(x)
return x.astype(np.float32) if isnumpy else float(x) if isscalar else x.float()
[docs]def mulaw(x, mu=256):
r"""Mu-Law companding
Method described in paper [1]_.
.. math::
f(x) = sign(x) \ln (1 + \mu |x|) / \ln (1 + \mu)
Args:
x (array-like): Input signal. Each value of input signal must be in
range of [-1, 1].
mu (number): Compression parameter ``μ``.
Returns:
array-like: Compressed signal ([-1, 1])
See also:
:func:`nnmnkwii.preprocessing.inv_mulaw`
:func:`nnmnkwii.preprocessing.mulaw_quantize`
:func:`nnmnkwii.preprocessing.inv_mulaw_quantize`
.. [1] Brokish, Charles W., and Michele Lewis. "A-law and mu-law companding
implementations using the tms320c54x." SPRA163 (1997).
"""
return _sign(x) * _log1p(mu * _abs(x)) / _log1p(mu)
[docs]def inv_mulaw(y, mu=256):
r"""Inverse of mu-law companding (mu-law expansion)
.. math::
f^{-1}(x) = sign(y) (1 / \mu) (1 + \mu)^{|y|} - 1)
Args:
y (array-like): Compressed signal. Each value of input signal must be in
range of [-1, 1].
mu (number): Compression parameter ``μ``.
Returns:
array-like: Uncomprresed signal (-1 <= x <= 1)
See also:
:func:`nnmnkwii.preprocessing.inv_mulaw`
:func:`nnmnkwii.preprocessing.mulaw_quantize`
:func:`nnmnkwii.preprocessing.inv_mulaw_quantize`
"""
return _sign(y) * (1.0 / mu) * ((1.0 + mu) ** _abs(y) - 1.0)
[docs]def mulaw_quantize(x, mu=256):
"""Mu-Law companding + quantize
Args:
x (array-like): Input signal. Each value of input signal must be in
range of [-1, 1].
mu (number): Compression parameter ``μ``.
Returns:
array-like: Quantized signal (dtype=int)
- y ∈ [0, mu] if x ∈ [-1, 1]
- y ∈ [0, mu) if x ∈ [-1, 1)
.. note::
If you want to get quantized values of range [0, mu) (not [0, mu]),
then you need to provide input signal of range [-1, 1).
Examples:
>>> from scipy.io import wavfile
>>> import pysptk
>>> import numpy as np
>>> from nnmnkwii import preprocessing as P
>>> fs, x = wavfile.read(pysptk.util.example_audio_file())
>>> x = (x / 32768.0).astype(np.float32)
>>> y = P.mulaw_quantize(x)
>>> print(y.min(), y.max(), y.dtype)
15 246 int64
See also:
:func:`nnmnkwii.preprocessing.mulaw`
:func:`nnmnkwii.preprocessing.inv_mulaw`
:func:`nnmnkwii.preprocessing.inv_mulaw_quantize`
"""
y = mulaw(x, mu)
# scale [-1, 1] to [0, mu]
return _asint((y + 1) / 2 * mu)
[docs]def inv_mulaw_quantize(y, mu=256):
"""Inverse of mu-law companding + quantize
Args:
y (array-like): Quantized signal (∈ [0, mu]).
mu (number): Compression parameter ``μ``.
Returns:
array-like: Uncompressed signal ([-1, 1])
Examples:
>>> from scipy.io import wavfile
>>> import pysptk
>>> import numpy as np
>>> from nnmnkwii import preprocessing as P
>>> fs, x = wavfile.read(pysptk.util.example_audio_file())
>>> x = (x / 32768.0).astype(np.float32)
>>> x_hat = P.inv_mulaw_quantize(P.mulaw_quantize(x))
>>> x_hat = (x_hat * 32768).astype(np.int16)
See also:
:func:`nnmnkwii.preprocessing.mulaw`
:func:`nnmnkwii.preprocessing.inv_mulaw`
:func:`nnmnkwii.preprocessing.mulaw_quantize`
"""
# [0, m) to [-1, 1]
y = 2 * _asfloat(y) / mu - 1
return inv_mulaw(y, mu)
[docs]def preemphasis(x, coef=0.97):
"""Pre-emphasis
Args:
x (1d-array): Input signal.
coef (float): Pre-emphasis coefficient.
Returns:
array: Output filtered signal.
See also:
:func:`inv_preemphasis`
Examples:
>>> from nnmnkwii.util import example_audio_file
>>> from scipy.io import wavfile
>>> fs, x = wavfile.read(example_audio_file())
>>> x = x.astype(np.float64)
>>> from nnmnkwii import preprocessing as P
>>> y = P.preemphasis(x, coef=0.97)
>>> assert x.shape == y.shape
"""
b = np.array([1.0, -coef], x.dtype)
a = np.array([1.0], x.dtype)
return signal.lfilter(b, a, x)
[docs]def inv_preemphasis(x, coef=0.97):
"""Inverse operation of pre-emphasis
Args:
x (1d-array): Input signal.
coef (float): Pre-emphasis coefficient.
Returns:
array: Output filtered signal.
See also:
:func:`preemphasis`
Examples:
>>> from nnmnkwii.util import example_audio_file
>>> from scipy.io import wavfile
>>> fs, x = wavfile.read(example_audio_file())
>>> x = x.astype(np.float64)
>>> from nnmnkwii import preprocessing as P
>>> x_hat = P.inv_preemphasis(P.preemphasis(x, coef=0.97), coef=0.97)
>>> assert np.allclose(x, x_hat)
"""
b = np.array([1.0], x.dtype)
a = np.array([1.0, -coef], x.dtype)
return signal.lfilter(b, a, x)
def _delta(x, window):
return np.correlate(x, window, mode="same")
def _apply_delta_window(x, window):
"""Returns delta features given a static features and a window.
Args:
x (numpy.ndarray): Input static features, of shape (``T x D``).
window (numpy.ndarray): Window coefficients.
Returns:
(ndarray): Delta features, shape (``T x D``).
"""
T, D = x.shape
y = np.zeros_like(x)
for d in range(D):
y[:, d] = _delta(x[:, d], window)
return y
[docs]def delta_features(x, windows):
"""Compute delta features and combine them.
This function computes delta features given delta windows, and then
returns combined features (e.g., static + delta + delta-delta).
Note that if you want to keep static features, you need to give
static window as well as delta windows.
Args:
x (numpy.ndarray): Input static features, of shape (``T x D``).
y (list): List of windows. See :func:`nnmnkwii.paramgen.mlpg` for what
the delta window means.
Returns:
numpy.ndarray: static + delta features (``T x (D * len(windows)``).
Examples:
>>> from nnmnkwii.preprocessing import delta_features
>>> windows = [
... (0, 0, np.array([1.0])), # static
... (1, 1, np.array([-0.5, 0.0, 0.5])), # delta
... (1, 1, np.array([1.0, -2.0, 1.0])), # delta-delta
... ]
>>> T, static_dim = 10, 24
>>> x = np.random.rand(T, static_dim)
>>> y = delta_features(x, windows)
>>> assert y.shape == (T, static_dim * len(windows))
"""
T, D = x.shape
assert len(windows) > 0
combined_features = np.empty((T, D * len(windows)), dtype=x.dtype)
# NOTE: bandmat case
if isinstance(windows[0], tuple):
for idx, (_, _, window) in enumerate(windows):
combined_features[:, D * idx : D * idx + D] = _apply_delta_window(x, window)
else:
for idx, window in enumerate(windows):
combined_features[:, D * idx : D * idx + D] = _apply_delta_window(x, window)
return combined_features
[docs]def trim_zeros_frames(x, eps=1e-7, trim="b"):
"""Remove leading and/or trailing zeros frames.
Similar to :func:`numpy.trim_zeros`, trimming trailing zeros features.
Args:
x (numpy.ndarray): Feature matrix, shape (``T x D``)
eps (float): Values smaller than ``eps`` considered as zeros.
trim (string): Representing trim from where.
Returns:
numpy.ndarray: Trimmed 2d feature matrix, shape (``T' x D``)
Examples:
>>> import numpy as np
>>> from nnmnkwii.preprocessing import trim_zeros_frames
>>> x = np.random.rand(100,10)
>>> y = trim_zeros_frames(x)
"""
assert trim in {"f", "b", "fb"}
T, D = x.shape
s = np.sum(np.abs(x), axis=1)
s[s < eps] = 0.0
if trim == "f":
return x[len(x) - len(np.trim_zeros(s, trim=trim)) :]
elif trim == "b":
end = len(np.trim_zeros(s, trim=trim)) - len(x)
if end == 0:
return x
else:
return x[:end]
elif trim == "fb":
f = len(np.trim_zeros(s, trim="f"))
b = len(np.trim_zeros(s, trim="b"))
end = b - len(x)
if end == 0:
return x[len(x) - f :]
else:
return x[len(x) - f : end]
[docs]def remove_zeros_frames(x, eps=1e-7):
"""Remove zeros frames.
Given a feature matrix, remove all zeros frames as well as trailing ones.
Args:
x (numpy.ndarray): 2d feature matrix, shape (``T x D``)
eps (float): Values smaller than ``eps`` considered as zeros.
Returns:
numpy.ndarray: Zeros-removed 2d feature matrix, shape (``T' x D``).
Examples:
>>> import numpy as np
>>> from nnmnkwii.preprocessing import remove_zeros_frames
>>> x = np.random.rand(100,10)
>>> y = remove_zeros_frames(x)
"""
T, D = x.shape
s = np.sum(np.abs(x), axis=1)
s[s < eps] = 0.0
return x[s > eps]
[docs]def adjust_frame_length(x, pad=True, divisible_by=1, **kwargs):
"""Adjust frame length given a feature vector or matrix.
This adjust the number of frames of a given feature vector or matrix to be
divisible by ``divisible_by`` by padding to the end or removing the last
few frames. Default uses zero-padding.
Args:
x (numpy.ndarray): Input 1d or 2d array, shape (``T,`` or ``T x D``).
pad (bool) : If True, pads values to the end, otherwise removes last few
frames to ensure same frame lengths.
divisible_by (int) : If ``divisible_by`` > 0, number of frames will be
adjusted to be divisible by ``divisible_by``.
kwargs (dict): Keyword argments passed to :func:`numpy.pad`. Default is
mode = ``constant``, which means zero padding.
Returns:
numpy.ndarray: adjusted array, of each shape (``T`` or ``T' x D``).
Examples:
>>> from nnmnkwii.preprocessing import adjust_frame_length
>>> import numpy as np
>>> x = np.zeros((10, 1))
>>> x = adjust_frame_length(x, pad=True, divisible_by=3)
>>> assert x.shape[0] == 12
See also:
:func:`nnmnkwii.preprocessing.adjust_frame_lengths`
"""
kwargs.setdefault("mode", "constant")
assert x.ndim == 2 or x.ndim == 1
Tx = x.shape[0]
if divisible_by > 1:
rem = Tx % divisible_by
if rem == 0:
T = Tx
else:
if pad:
T = Tx + divisible_by - rem
else:
T = Tx - rem
else:
T = Tx
if Tx != T:
if T > Tx:
if x.ndim == 1:
x = np.pad(x, (0, T - Tx), **kwargs)
elif x.ndim == 2:
x = np.pad(x, [(0, T - Tx), (0, 0)], **kwargs)
else:
x = x[:T]
return x
[docs]def adjust_frame_lengths(x, y, pad=True, ensure_even=False, divisible_by=1, **kwargs):
"""Adjust frame lengths given two feature vectors or matrices.
This ensures that two feature vectors or matrices have same number of
frames, by padding to the end or removing the last few frames.
Default uses zero-padding.
.. warning::
``ensure_even`` is deprecated and will be removed in v0.1.0.
Use ``divisible_by=2`` instead.
Args:
x (numpy.ndarray): Input 2d feature matrix, shape (``T^1 x D``).
y (numpy.ndarray): Input 2d feature matrix, shape (``T^2 x D``).
pad (bool) : If True, pads values to the end, otherwise removes last few
frames to ensure same frame lengths.
divisible_by (int) : If ``divisible_by`` > 0, number of frames will be
adjusted to be divisible by ``divisible_by``.
kwargs (dict): Keyword argments passed to :func:`numpy.pad`. Default is
mode = ``constant``, which means zero padding.
Returns:
Tuple: Pair of adjusted feature matrices, of each shape (``T x D``).
Examples:
>>> from nnmnkwii.preprocessing import adjust_frame_lengths
>>> import numpy as np
>>> x = np.zeros((10, 1))
>>> y = np.zeros((11, 1))
>>> x, y = adjust_frame_lengths(x, y)
>>> assert len(x) == len(y)
See also:
:func:`nnmnkwii.preprocessing.adjust_frame_length`
"""
assert x.ndim in [1, 2] and y.ndim in [1, 2]
kwargs.setdefault("mode", "constant")
Tx = x.shape[0]
Ty = y.shape[0]
if x.ndim == 2:
assert x.shape[-1] == y.shape[-1]
if ensure_even:
divisible_by = 2
if pad:
T = max(Tx, Ty)
if divisible_by > 1:
rem = T % divisible_by
if rem != 0:
T = T + divisible_by - rem
else:
T = min(Tx, Ty)
if divisible_by > 1:
rem = T % divisible_by
T = T - rem
if Tx != T:
if Tx < T:
if x.ndim == 1:
x = np.pad(x, (0, T - Tx), **kwargs)
elif x.ndim == 2:
x = np.pad(x, [(0, T - Tx), (0, 0)], **kwargs)
else:
x = x[:T]
if Ty != T:
if Ty < T:
if y.ndim == 1:
y = np.pad(y, (0, T - Ty), **kwargs)
elif y.ndim == 2:
y = np.pad(y, [(0, T - Ty), (0, 0)], **kwargs)
else:
y = y[:T]
return x, y
[docs]def meanvar(
dataset,
lengths=None,
mean_=0.0,
var_=0.0,
last_sample_count=0,
return_last_sample_count=False,
):
r"""Mean/variance computation given a iterable dataset
Dataset can have variable length samples. In that cases, you need to
explicitly specify lengths for all the samples.
Args:
dataset (nnmnkwii.datasets.Dataset): Dataset
lengths: (list): Frame lengths for each dataset sample.
mean\_ (array or scalar): Initial value for mean vector.
var\_ (array or scaler): Initial value for variance vector.
last_sample_count (int): Last sample count. Default is 0. If you set
non-default ``mean_`` and ``var_``, you need to set
``last_sample_count`` property. Typically this will be the number of
time frames ever seen.
return_last_sample_count (bool): Return ``last_sample_count`` if True.
Returns:
tuple: Mean and variance for each dimention. If
``return_last_sample_count`` is True, returns ``last_sample_count``
as well.
See also:
:func:`nnmnkwii.preprocessing.meanstd`, :func:`nnmnkwii.preprocessing.scale`
Examples:
>>> from nnmnkwii.preprocessing import meanvar
>>> from nnmnkwii.util import example_file_data_sources_for_acoustic_model
>>> from nnmnkwii.datasets import FileSourceDataset
>>> X, Y = example_file_data_sources_for_acoustic_model()
>>> X, Y = FileSourceDataset(X), FileSourceDataset(Y)
>>> lengths = [len(y) for y in Y]
>>> data_mean, data_var = meanvar(Y, lengths)
"""
dtype = dataset[0].dtype
for idx, x in enumerate(dataset):
if lengths is not None:
x = x[: lengths[idx]]
mean_, var_, _ = _incremental_mean_and_var(x, mean_, var_, last_sample_count)
last_sample_count += len(x)
mean_, var_ = mean_.astype(dtype), var_.astype(dtype)
if return_last_sample_count:
return mean_, var_, last_sample_count
else:
return mean_, var_
[docs]def meanstd(
dataset,
lengths=None,
mean_=0.0,
var_=0.0,
last_sample_count=0,
return_last_sample_count=False,
):
r"""Mean/std-deviation computation given a iterable dataset
Dataset can have variable length samples. In that cases, you need to
explicitly specify lengths for all the samples.
Args:
dataset (nnmnkwii.datasets.Dataset): Dataset
lengths: (list): Frame lengths for each dataset sample.
mean\_ (array or scalar): Initial value for mean vector.
var\_ (array or scaler): Initial value for variance vector.
last_sample_count (int): Last sample count. Default is 0. If you set
non-default ``mean_`` and ``var_``, you need to set
``last_sample_count`` property. Typically this will be the number of
time frames ever seen.
return_last_sample_count (bool): Return ``last_sample_count`` if True.
Returns:
tuple: Mean and variance for each dimention. If
``return_last_sample_count`` is True, returns ``last_sample_count``
as well.
See also:
:func:`nnmnkwii.preprocessing.meanvar`, :func:`nnmnkwii.preprocessing.scale`
Examples:
>>> from nnmnkwii.preprocessing import meanstd
>>> from nnmnkwii.util import example_file_data_sources_for_acoustic_model
>>> from nnmnkwii.datasets import FileSourceDataset
>>> X, Y = example_file_data_sources_for_acoustic_model()
>>> X, Y = FileSourceDataset(X), FileSourceDataset(Y)
>>> lengths = [len(y) for y in Y]
>>> data_mean, data_std = meanstd(Y, lengths)
"""
ret = meanvar(
dataset, lengths, mean_, var_, last_sample_count, return_last_sample_count
)
m, v = ret[0], ret[1]
v = _handle_zeros_in_scale(np.sqrt(v))
if return_last_sample_count:
assert len(ret) == 3
return m, v, ret[2]
else:
return m, v
[docs]def minmax(dataset, lengths=None):
"""Min/max computation given a iterable dataset
Dataset can have variable length samples. In that cases, you need to
explicitly specify lengths for all the samples.
Args:
dataset (nnmnkwii.datasets.Dataset): Dataset
lengths: (list): Frame lengths for each dataset sample.
See also:
:func:`nnmnkwii.preprocessing.minmax_scale`
Examples:
>>> from nnmnkwii.preprocessing import minmax
>>> from nnmnkwii.util import example_file_data_sources_for_acoustic_model
>>> from nnmnkwii.datasets import FileSourceDataset
>>> X, Y = example_file_data_sources_for_acoustic_model()
>>> X, Y = FileSourceDataset(X), FileSourceDataset(Y)
>>> lengths = [len(x) for x in X]
>>> data_min, data_max = minmax(X, lengths)
"""
max_ = -np.inf
min_ = np.inf
for idx, x in enumerate(dataset):
if lengths is not None:
x = x[: lengths[idx]]
min_ = np.minimum(min_, np.min(x, axis=(0,)))
max_ = np.maximum(max_, np.max(x, axis=(0,)))
return min_, max_
[docs]def scale(x, data_mean, data_std):
"""Mean/variance scaling.
Given mean and variances, apply mean-variance normalization to data.
Args:
x (array): Input data
data_mean (array): Means for each feature dimention.
data_std (array): Standard deviation for each feature dimention.
Returns:
array: Scaled data.
Examples:
>>> from nnmnkwii.preprocessing import meanstd, scale
>>> from nnmnkwii.util import example_file_data_sources_for_acoustic_model
>>> from nnmnkwii.datasets import FileSourceDataset
>>> X, Y = example_file_data_sources_for_acoustic_model()
>>> X, Y = FileSourceDataset(X), FileSourceDataset(Y)
>>> lengths = [len(y) for y in Y]
>>> data_mean, data_std = meanstd(Y, lengths)
>>> scaled_y = scale(Y[0], data_mean, data_std)
See also:
:func:`nnmnkwii.preprocessing.inv_scale`
"""
return (x - data_mean) / _handle_zeros_in_scale(data_std, copy=False)
[docs]def inv_scale(x, data_mean, data_std):
"""Inverse tranform of mean/variance scaling.
Given mean and variances, apply mean-variance denormalization to data.
Args:
x (array): Input data
data_mean (array): Means for each feature dimention.
data_std (array): Standard deviation for each feature dimention.
Returns:
array: Denormalized data.
See also:
:func:`nnmnkwii.preprocessing.scale`
"""
return data_std * x + data_mean
def __minmax_scale_factor(data_min, data_max, feature_range):
data_range = data_max - data_min
scale = (feature_range[1] - feature_range[0]) / _handle_zeros_in_scale(
data_range, copy=False
)
return scale
[docs]def minmax_scale_params(data_min, data_max, feature_range=(0, 1)):
"""Compute parameters required to perform min/max scaling.
Given data min, max and feature range, computes scalining factor and
minimum value. Min/max scaling can be done as follows:
.. code-block:: python
x_scaled = x * scale_ + min_
Args:
x (array): Input data
data_min (array): Data min for each feature dimention.
data_max (array): Data max for each feature dimention.
feature_range (array like): Feature range.
Returns:
tuple: Minimum value and scaling factor for scaled data.
Examples:
>>> from nnmnkwii.preprocessing import minmax, minmax_scale
>>> from nnmnkwii.preprocessing import minmax_scale_params
>>> from nnmnkwii.util import example_file_data_sources_for_acoustic_model
>>> from nnmnkwii.datasets import FileSourceDataset
>>> X, Y = example_file_data_sources_for_acoustic_model()
>>> X, Y = FileSourceDataset(X), FileSourceDataset(Y)
>>> data_min, data_max = minmax(X)
>>> min_, scale_ = minmax_scale_params(data_min, data_max)
>>> scaled_x = minmax_scale(X[0], min_=min_, scale_=scale_)
See also:
:func:`nnmnkwii.preprocessing.minmax_scale`,
:func:`nnmnkwii.preprocessing.inv_minmax_scale`
"""
scale_ = __minmax_scale_factor(data_min, data_max, feature_range)
min_ = feature_range[0] - data_min * scale_
return min_, scale_
[docs]def minmax_scale(
x, data_min=None, data_max=None, feature_range=(0, 1), scale_=None, min_=None
):
r"""Min/max scaling for given a single data.
Given data min, max and feature range, apply min/max normalization to data.
Optionally, you can get a little performance improvement to give scaling
factor (``scale_``) and minimum value (``min_``) used in scaling explicitly.
Those values can be computed by
:func:`nnmnkwii.preprocessing.minmax_scale_params`.
.. note::
If ``scale_`` and ``min_`` are given, ``feature_range`` will be ignored.
Args:
x (array): Input data
data_min (array): Data min for each feature dimention.
data_max (array): Data max for each feature dimention.
feature_range (array like): Feature range.
scale\_ ([optional]array): Scaling factor.
min\_ ([optional]array): Minimum value for scaling.
Returns:
array: Scaled data.
Raises:
ValueError: If (``data_min``, ``data_max``) or
(``scale_`` and ``min_``) are not specified.
See also:
:func:`nnmnkwii.preprocessing.inv_minmax_scale`,
:func:`nnmnkwii.preprocessing.minmax_scale_params`
Examples:
>>> from nnmnkwii.preprocessing import minmax, minmax_scale
>>> from nnmnkwii.util import example_file_data_sources_for_acoustic_model
>>> from nnmnkwii.datasets import FileSourceDataset
>>> X, Y = example_file_data_sources_for_acoustic_model()
>>> X, Y = FileSourceDataset(X), FileSourceDataset(Y)
>>> data_min, data_max = minmax(X)
>>> scaled_x = minmax_scale(X[0], data_min, data_max)
"""
if (scale_ is None or min_ is None) and (data_min is None or data_max is None):
raise ValueError(
"""
`data_min` and `data_max` or `scale_` and `min_` must be specified to perform minmax scale"""
)
if scale_ is None:
scale_ = __minmax_scale_factor(data_min, data_max, feature_range)
if min_ is None:
min_ = feature_range[0] - data_min * scale_
return x * scale_ + min_
[docs]def inv_minmax_scale(
x, data_min=None, data_max=None, feature_range=(0, 1), scale_=None, min_=None
):
r"""Inverse transform of min/max scaling for given a single data.
Given data min, max and feature range, apply min/max denormalization to data.
.. note::
If ``scale_`` and ``min_`` are given, ``feature_range`` will be ignored.
Args:
x (array): Input data
data_min (array): Data min for each feature dimention.
data_max (array): Data max for each feature dimention.
feature_range (array like): Feature range.
scale\_ ([optional]array): Scaling factor.
min\_ ([optional]array): Minimum value for scaling.
Returns:
array: Scaled data.
Raises:
ValueError: If (``data_min``, ``data_max``) or
(``scale_`` and ``min_``) are not specified.
See also:
:func:`nnmnkwii.preprocessing.minmax_scale`,
:func:`nnmnkwii.preprocessing.minmax_scale_params`
"""
if (scale_ is None or min_ is None) and (data_min is None or data_max is None):
raise ValueError(
"`data_min` and `data_max` or `scale_` and `min_` must be specified "
"to perform inverse of minmax scale"
)
if scale_ is None:
scale_ = __minmax_scale_factor(data_min, data_max, feature_range)
if min_ is None:
min_ = feature_range[0] - data_min * scale_
return (x - min_) / scale_