# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# (C) British Crown Copyright 2017-2019 Met Office.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""Provides support utility for rescaling data."""
import numpy as np
[docs]def rescale(data, data_range=None, scale_range=(0., 1.),
clip=False):
"""
Rescale data array so that data_min => scale_min
and data_max => scale max.
All adjustments are linear
Args:
data (numpy.ndarray):
Source values
data_range (list):
List containing two floats
Lowest and highest source value to rescale.
Default value of None is converted to [min(data), max(data)]
scale_range (list):
List containing two floats
Lowest and highest value after rescaling.
Defaults to (0., 1.)
clip (bool):
If True, points where data were outside the scaling range
will be set to the scale min or max appropriately.
Default is False which continues the scaling beyond min and
max.
Returns:
numpy.ndarray:
Output array of scaled data. Has same shape as data.
"""
data_min = np.min(data) if data_range is None else data_range[0]
data_max = np.max(data) if data_range is None else data_range[1]
scale_min = scale_range[0]
scale_max = scale_range[1]
# Range check
if data_min == data_max:
raise ValueError("Cannot rescale a zero input range " +
"({} -> {})".format(data_min, data_max))
if scale_min == scale_max:
raise ValueError("Cannot rescale a zero output range " +
"({} -> {})".format(scale_min, scale_max))
result = ((data - data_min) * (scale_max - scale_min) /
(data_max - data_min)) + scale_min
if clip:
result = np.clip(result, scale_min, scale_max)
return result
[docs]def apply_double_scaling(data_cube, scaled_cube,
data_vals, scaling_vals,
combine_function=np.minimum):
"""
From data_cube, an array of limiting values is created based on a linear
rescaling from three data_vals to three scaling_vals.
The three values refer to a lower-bound, a mid-point and an upper-bound.
This rescaled data_cube is combined with scaled_cube to produce an array
containing either the higher or lower value as needed.
Args:
data_cube (iris.cube.Cube):
Data from which to create a rescaled data array
scaled_cube (iris.cube.Cube):
Data already in the rescaled frame of reference which will be
combined with the rescaled data_cube using the combine_function.
data_vals (tuple of three values):
Lower, mid and upper points to rescale data_cube from
scaling_vals (tuple of three values):
Lower, mid and upper points to rescale data_cube to
combine_function (Callable[[numpy.ndarray, numpy.ndarray],
numpy.ndarray]):
Function that takes two arrays of the same shape and returns
one array of the same shape.
Expected to be numpy.minimum (default) or numpy.maximum.
Returns:
numpy.ndarray:
Output data from data_cube after rescaling and combining with
scaled_cube.
This array will have the same dimensions as scaled_cube.
"""
# Where data are below the specified mid-point (data_vals[1]):
# Set rescaled_data to be a rescaled value between the first and mid-point
# Elsewhere
# Set rescaled_data to be a rescaled value between the mid- and last point
rescaled_data = np.where(
data_cube.data < data_vals[1],
rescale(data_cube.data,
data_range=(data_vals[0], data_vals[1]),
scale_range=(scaling_vals[0], scaling_vals[1]),
clip=True),
rescale(data_cube.data,
data_range=(data_vals[1], data_vals[2]),
scale_range=(scaling_vals[1], scaling_vals[2]),
clip=True))
# Ensure scaled_cube is no larger or smaller than the rescaled_data:
return combine_function(scaled_cube.data, rescaled_data)