wg-backend-django/dell-env/lib/python3.11/site-packages/plotly/figure_factory/utils.py
2023-10-30 14:40:43 +07:00

268 lines
8.3 KiB
Python

from collections.abc import Sequence
from plotly import exceptions
from plotly.colors import (
DEFAULT_PLOTLY_COLORS,
PLOTLY_SCALES,
color_parser,
colorscale_to_colors,
colorscale_to_scale,
convert_to_RGB_255,
find_intermediate_color,
hex_to_rgb,
label_rgb,
n_colors,
unconvert_from_RGB_255,
unlabel_rgb,
validate_colors,
validate_colors_dict,
validate_colorscale,
validate_scale_values,
)
def is_sequence(obj):
return isinstance(obj, Sequence) and not isinstance(obj, str)
def validate_index(index_vals):
"""
Validates if a list contains all numbers or all strings
:raises: (PlotlyError) If there are any two items in the list whose
types differ
"""
from numbers import Number
if isinstance(index_vals[0], Number):
if not all(isinstance(item, Number) for item in index_vals):
raise exceptions.PlotlyError(
"Error in indexing column. "
"Make sure all entries of each "
"column are all numbers or "
"all strings."
)
elif isinstance(index_vals[0], str):
if not all(isinstance(item, str) for item in index_vals):
raise exceptions.PlotlyError(
"Error in indexing column. "
"Make sure all entries of each "
"column are all numbers or "
"all strings."
)
def validate_dataframe(array):
"""
Validates all strings or numbers in each dataframe column
:raises: (PlotlyError) If there are any two items in any list whose
types differ
"""
from numbers import Number
for vector in array:
if isinstance(vector[0], Number):
if not all(isinstance(item, Number) for item in vector):
raise exceptions.PlotlyError(
"Error in dataframe. "
"Make sure all entries of "
"each column are either "
"numbers or strings."
)
elif isinstance(vector[0], str):
if not all(isinstance(item, str) for item in vector):
raise exceptions.PlotlyError(
"Error in dataframe. "
"Make sure all entries of "
"each column are either "
"numbers or strings."
)
def validate_equal_length(*args):
"""
Validates that data lists or ndarrays are the same length.
:raises: (PlotlyError) If any data lists are not the same length.
"""
length = len(args[0])
if any(len(lst) != length for lst in args):
raise exceptions.PlotlyError(
"Oops! Your data lists or ndarrays " "should be the same length."
)
def validate_positive_scalars(**kwargs):
"""
Validates that all values given in key/val pairs are positive.
Accepts kwargs to improve Exception messages.
:raises: (PlotlyError) If any value is < 0 or raises.
"""
for key, val in kwargs.items():
try:
if val <= 0:
raise ValueError("{} must be > 0, got {}".format(key, val))
except TypeError:
raise exceptions.PlotlyError("{} must be a number, got {}".format(key, val))
def flatten(array):
"""
Uses list comprehension to flatten array
:param (array): An iterable to flatten
:raises (PlotlyError): If iterable is not nested.
:rtype (list): The flattened list.
"""
try:
return [item for sublist in array for item in sublist]
except TypeError:
raise exceptions.PlotlyError(
"Your data array could not be "
"flattened! Make sure your data is "
"entered as lists or ndarrays!"
)
def endpts_to_intervals(endpts):
"""
Returns a list of intervals for categorical colormaps
Accepts a list or tuple of sequentially increasing numbers and returns
a list representation of the mathematical intervals with these numbers
as endpoints. For example, [1, 6] returns [[-inf, 1], [1, 6], [6, inf]]
:raises: (PlotlyError) If input is not a list or tuple
:raises: (PlotlyError) If the input contains a string
:raises: (PlotlyError) If any number does not increase after the
previous one in the sequence
"""
length = len(endpts)
# Check if endpts is a list or tuple
if not (isinstance(endpts, (tuple)) or isinstance(endpts, (list))):
raise exceptions.PlotlyError(
"The intervals_endpts argument must "
"be a list or tuple of a sequence "
"of increasing numbers."
)
# Check if endpts contains only numbers
for item in endpts:
if isinstance(item, str):
raise exceptions.PlotlyError(
"The intervals_endpts argument "
"must be a list or tuple of a "
"sequence of increasing "
"numbers."
)
# Check if numbers in endpts are increasing
for k in range(length - 1):
if endpts[k] >= endpts[k + 1]:
raise exceptions.PlotlyError(
"The intervals_endpts argument "
"must be a list or tuple of a "
"sequence of increasing "
"numbers."
)
else:
intervals = []
# add -inf to intervals
intervals.append([float("-inf"), endpts[0]])
for k in range(length - 1):
interval = []
interval.append(endpts[k])
interval.append(endpts[k + 1])
intervals.append(interval)
# add +inf to intervals
intervals.append([endpts[length - 1], float("inf")])
return intervals
def annotation_dict_for_label(
text,
lane,
num_of_lanes,
subplot_spacing,
row_col="col",
flipped=True,
right_side=True,
text_color="#0f0f0f",
):
"""
Returns annotation dict for label of n labels of a 1xn or nx1 subplot.
:param (str) text: the text for a label.
:param (int) lane: the label number for text. From 1 to n inclusive.
:param (int) num_of_lanes: the number 'n' of rows or columns in subplot.
:param (float) subplot_spacing: the value for the horizontal_spacing and
vertical_spacing params in your plotly.tools.make_subplots() call.
:param (str) row_col: choose whether labels are placed along rows or
columns.
:param (bool) flipped: flips text by 90 degrees. Text is printed
horizontally if set to True and row_col='row', or if False and
row_col='col'.
:param (bool) right_side: only applicable if row_col is set to 'row'.
:param (str) text_color: color of the text.
"""
l = (1 - (num_of_lanes - 1) * subplot_spacing) / (num_of_lanes)
if not flipped:
xanchor = "center"
yanchor = "middle"
if row_col == "col":
x = (lane - 1) * (l + subplot_spacing) + 0.5 * l
y = 1.03
textangle = 0
elif row_col == "row":
y = (lane - 1) * (l + subplot_spacing) + 0.5 * l
x = 1.03
textangle = 90
else:
if row_col == "col":
xanchor = "center"
yanchor = "bottom"
x = (lane - 1) * (l + subplot_spacing) + 0.5 * l
y = 1.0
textangle = 270
elif row_col == "row":
yanchor = "middle"
y = (lane - 1) * (l + subplot_spacing) + 0.5 * l
if right_side:
x = 1.0
xanchor = "left"
else:
x = -0.01
xanchor = "right"
textangle = 0
annotation_dict = dict(
textangle=textangle,
xanchor=xanchor,
yanchor=yanchor,
x=x,
y=y,
showarrow=False,
xref="paper",
yref="paper",
text=text,
font=dict(size=13, color=text_color),
)
return annotation_dict
def list_of_options(iterable, conj="and", period=True):
"""
Returns an English listing of objects seperated by commas ','
For example, ['foo', 'bar', 'baz'] becomes 'foo, bar and baz'
if the conjunction 'and' is selected.
"""
if len(iterable) < 2:
raise exceptions.PlotlyError(
"Your list or tuple must contain at least 2 items."
)
template = (len(iterable) - 2) * "{}, " + "{} " + conj + " {}" + period * "."
return template.format(*iterable)