Source code for eva.plotting.emcpy.plot_tools.dynamic_config
# (C) Copyright 2021-2022 NOAA/NWS/EMC
#
# (C) Copyright 2021-2022 United States Government as represented by the Administrator of the
# National Aeronautics and Space Administration. All Rights Reserved.
#
# This software is licensed under the terms of the Apache Licence Version 2.0
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
# --------------------------------------------------------------------------------------------------
import math
import numpy as np
from scipy.stats import skew
from eva.utilities.utils import replace_vars_dict
# --------------------------------------------------------------------------------------------------
[docs]def vminvmaxcmap(logger, option_dict, plots_dict, data_collections):
"""
Computes colormap limits and adjustments based on provided data variable.
Args:
logger (Logger): An instance of the logger for logging messages.
option_dict (dict): A dictionary containing various options for the transformation.
plots_dict (dict): A dictionary containing plot-related information.
data_collections (DataCollections): An instance of the DataCollections class containing
input data.
Returns:
dict: A dictionary with adjusted values for colormap limits and colormap.
This function computes colormap limits and adjustments based on the provided data variable. It
calculates the minimum and maximum values for colormap limits, determines whether a sequential
or diverging colormap should be used, and updates the provided plots dictionary with the
adjusted values.
Example:
::
option_dict = {
'percentage capture': 95,
'data variable': 'my_collection::my_variable',
'sequential colormap': 'plasma',
'diverging colormap': 'coolwarm'
}
plots_dict = {
'dynamic_vmax': '1.0',
'dynamic_vmin': '-1.0',
'dynamic_cmap': 'viridis'
}
adjusted_plots_dict = vminvmaxcmap(logger, option_dict, plots_dict,
data_collections)
"""
# Get percentage of data to use in computing limits. Code will sort the data
# by size and then trim the minimum and maximum until this percentage of data
# is kept
percentage_capture = option_dict.get('percentage capture', 100)
# Optionally the data might have a channel.
channel = option_dict.get('channel', None)
# Get the data variable to use for determining cbar limits
varname = option_dict.get('data variable')
varname_cgv = varname.split('::')
datavar = data_collections.get_variable_data(varname_cgv[0], varname_cgv[1],
varname_cgv[2], channel)
# Reorder the data array
datavar = np.sort(datavar)
# Decide how many values to throw out on each end of the dataset
n = datavar.size
n_throw_out = ((100-percentage_capture) * n / 100) / 2
# The value needs to be an integer
n_throw_out = np.floor(n_throw_out)
# Create new array with only the data to consider
if n_throw_out == 0:
datavar_check = datavar
else:
datavar_check = datavar[n_throw_out:-n_throw_out]
# Find minimum and maximum values
cmap = option_dict.get('sequential colormap', 'viridis')
# If everything is nan plot some large min/max (plotting code should do the same)
if np.isnan(datavar_check).all():
vmax = 1.0e38
vmin = 1.0e38
else:
vmax = np.nanmax(datavar_check)
vmin = np.nanmin(datavar_check)
# If positive and negative values are present then a diverging colormap centered on zero should
# be used.
if vmin < 0.0 and vmax > 0.0:
vmax = np.nanmax(np.abs(datavar_check))
vmin = -vmax
cmap = option_dict.get('diverging colormap', 'seismic')
# Check for nan
if np.isnan(vmax) or np.isnan(vmin):
vmax = 0.0
vmin = 0.0
cmap = option_dict.get('diverging colormap', 'seismic')
# Prepare dictionary with values to be overwritten in other dictionaries
overwrite_dict = {}
overwrite_dict['dynamic_vmax'] = str(vmax)
overwrite_dict['dynamic_vmin'] = str(vmin)
overwrite_dict['dynamic_cmap'] = cmap
# Perform the overwrite of the plots_dict dictionary and return new dictionary
return replace_vars_dict(plots_dict, **overwrite_dict)
# --------------------------------------------------------------------------------------------------
[docs]def histogram_bins(logger, option_dict, plots_dict, data_collections):
"""
Computes the number of bins for a histogram based on the provided data variable.
Args:
logger (Logger): An instance of the logger for logging messages.
option_dict (dict): A dictionary containing various options for the transformation.
plots_dict (dict): A dictionary containing plot-related information.
data_collections (DataCollections): An instance of the DataCollections class containing
input data.
Returns:
dict: A dictionary with the number of bins for the histogram.
This function computes the number of bins for a histogram based on the provided data variable.
It applies various rules to calculate the appropriate number of bins and updates the provided
plots dictionary with the calculated value.
Example:
::
option_dict = {
'data variable': 'my_collection::my_variable',
'number of bins rule': 'sturges'
}
plots_dict = {
'dynamic_bins': '10'
}
adjusted_plots_dict = histogram_bins(logger, option_dict, plots_dict,
data_collections)
"""
# Optionally the data might have a channel.
channel = option_dict.get('channel', None)
# Get the data variable to use for determining cbar limits
varname = option_dict.get('data variable')
varname_cgv = varname.split('::')
datavar = data_collections.get_variable_data(varname_cgv[0], varname_cgv[1],
varname_cgv[2], channel)
# Compute size of the array of data
n = np.count_nonzero(~np.isnan(datavar))
# Check for zero data, set to 3 as a reasonable minimum
if n == 0:
n = 3
# Compute number of bins using standard rule
rule = option_dict.get('number of bins rule', 'sturges')
rule = rule.lower() # User might capitalize names so convert to lowercase
# Allow for some standard rules for computing the number of bins for the histogram
if rule == 'square root':
nbins = math.sqrt(n)
elif rule == 'sturges':
nbins = 1 + math.log2(n)
elif rule == 'rice':
nbins = 2 * math.pow(n, 1/3)
elif rule == 'doane':
if n < 3:
logger.abort(f'Rule \'doane\' is not valid for data with fewer than 3 samples.')
g1 = skew(datavar, nan_policy='omit')
sig_g1 = math.sqrt(6*(n-2)/((n+1)*(n+3)))
nbins = 1 + math.log2(n) + math.log2(1 + abs(g1)/sig_g1)
else:
logger.abort(f'Rule {rule} for computing the histogram bins is not valid.')
# Prepare dictionary with values to be overwritten in other dictionaries
overwrite_dict = {}
overwrite_dict['dynamic_bins'] = str(round(nbins))
# Perform the overwrite of the plots_dict dictionary and return new dictionary
return replace_vars_dict(plots_dict, **overwrite_dict)
# --------------------------------------------------------------------------------------------------