From 886ed9db8ad33ea57c44d3f51a6e5f85e9809308 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Tue, 11 Dec 2018 13:30:37 -0500 Subject: [PATCH 1/5] correct PLOTLY_SCALES in utils.py and ensure gantt/scatter work with new scales being referenced --- plotly/colors.py | 19 ++-- plotly/figure_factory/_scatterplot.py | 16 +++ plotly/figure_factory/utils.py | 156 ++++++++++++++++++++++---- 3 files changed, 159 insertions(+), 32 deletions(-) diff --git a/plotly/colors.py b/plotly/colors.py index 2ca872bc99a..5fc954fb9cc 100644 --- a/plotly/colors.py +++ b/plotly/colors.py @@ -203,19 +203,17 @@ [0.8784313725490196, '#addc30'], [0.9411764705882353, '#d8e219'], [1, '#fde725'] ], - 'Cividis': [ - [0.000000, 'rgb(0,32,76)'], [0.058824, 'rgb(0,42,102)'], - [0.117647, 'rgb(0,52,110)'], [0.176471, 'rgb(39,63,108)'], - [0.235294, 'rgb(60,74,107)'], [0.294118, 'rgb(76,85,107)'], - [0.352941, 'rgb(91,95,109)'], [0.411765, 'rgb(104,106,112)'], - [0.470588, 'rgb(117,117,117)'], [0.529412, 'rgb(131,129,120)'], - [0.588235, 'rgb(146,140,120)'], [0.647059, 'rgb(161,152,118)'], - [0.705882, 'rgb(176,165,114)'], [0.764706, 'rgb(192,177,109)'], - [0.823529, 'rgb(209,191,102)'], [0.882353, 'rgb(225,204,92)'], + [0.000000, 'rgb(0,32,76)'], [0.058824, 'rgb(0,42,102)'], + [0.117647, 'rgb(0,52,110)'], [0.176471, 'rgb(39,63,108)'], + [0.235294, 'rgb(60,74,107)'], [0.294118, 'rgb(76,85,107)'], + [0.352941, 'rgb(91,95,109)'], [0.411765, 'rgb(104,106,112)'], + [0.470588, 'rgb(117,117,117)'], [0.529412, 'rgb(131,129,120)'], + [0.588235, 'rgb(146,140,120)'], [0.647059, 'rgb(161,152,118)'], + [0.705882, 'rgb(176,165,114)'], [0.764706, 'rgb(192,177,109)'], + [0.823529, 'rgb(209,191,102)'], [0.882353, 'rgb(225,204,92)'], [0.941176, 'rgb(243,219,79)'], [1.000000, 'rgb(255,233,69)'] ] - } @@ -324,7 +322,6 @@ def convert_colors_to_same_type(colors, colortype='rgb', scale=None, :rtype (tuple) (colors_list, scale) if scale is None in the function call, then scale will remain None in the returned tuple """ - #if colors_list is None: colors_list = [] if colors is None and return_default_colors is True: diff --git a/plotly/figure_factory/_scatterplot.py b/plotly/figure_factory/_scatterplot.py index 3c2de2452f7..76e46f4e380 100644 --- a/plotly/figure_factory/_scatterplot.py +++ b/plotly/figure_factory/_scatterplot.py @@ -1,5 +1,7 @@ from __future__ import absolute_import +import six + from plotly import colors, exceptions, optional_imports from plotly.figure_factory import utils from plotly.graph_objs import graph_objs @@ -1080,9 +1082,23 @@ def create_scatterplotmatrix(df, index=None, endpts=None, diag='scatter', # Validate colormap if isinstance(colormap, dict): colormap = utils.validate_colors_dict(colormap, 'rgb') + elif isinstance(colormap, six.string_types) and 'rgb' not in colormap and '#' not in colormap: + if colormap not in utils.PLOTLY_SCALES.keys(): + raise exceptions.PlotlyError( + "If 'colormap' is a string, it must be the name " + "of a Plotly Colorscale. The available colorscale " + "names are {}".format(utils.PLOTLY_SCALES.keys()) + ) + else: + # TODO change below to allow the correct Plotly colorscale + colormap = utils.colorscale_to_colors(utils.PLOTLY_SCALES[colormap]) + # keep only first and last item - fix later + colormap = [colormap[0]] + [colormap[-1]] + colormap = utils.validate_colors(colormap, 'rgb') else: colormap = utils.validate_colors(colormap, 'rgb') + if not index: for name in df: headers.append(name) diff --git a/plotly/figure_factory/utils.py b/plotly/figure_factory/utils.py index ee19e17cef9..885433d44fc 100644 --- a/plotly/figure_factory/utils.py +++ b/plotly/figure_factory/utils.py @@ -2,6 +2,7 @@ import collections import decimal +import six from plotly import exceptions @@ -12,26 +13,134 @@ 'rgb(227, 119, 194)', 'rgb(127, 127, 127)', 'rgb(188, 189, 34)', 'rgb(23, 190, 207)'] -# TODO: make PLOTLY_SCALES below like version in plotly.colors -# requires rewritting scatterplot_matrix code PLOTLY_SCALES = { - 'Greys': ['rgb(0,0,0)', 'rgb(255,255,255)'], - 'YlGnBu': ['rgb(8,29,88)', 'rgb(255,255,217)'], - 'Greens': ['rgb(0,68,27)', 'rgb(247,252,245)'], - 'YlOrRd': ['rgb(128,0,38)', 'rgb(255,255,204)'], - 'Bluered': ['rgb(0,0,255)', 'rgb(255,0,0)'], - 'RdBu': ['rgb(5,10,172)', 'rgb(178,10,28)'], - 'Reds': ['rgb(220,220,220)', 'rgb(178,10,28)'], - 'Blues': ['rgb(5,10,172)', 'rgb(220,220,220)'], - 'Picnic': ['rgb(0,0,255)', 'rgb(255,0,0)'], - 'Rainbow': ['rgb(150,0,90)', 'rgb(255,0,0)'], - 'Portland': ['rgb(12,51,131)', 'rgb(217,30,30)'], - 'Jet': ['rgb(0,0,131)', 'rgb(128,0,0)'], - 'Hot': ['rgb(0,0,0)', 'rgb(255,255,255)'], - 'Blackbody': ['rgb(0,0,0)', 'rgb(160,200,255)'], - 'Earth': ['rgb(0,0,130)', 'rgb(255,255,255)'], - 'Electric': ['rgb(0,0,0)', 'rgb(255,250,220)'], - 'Viridis': ['#440154', '#fde725'] + 'Greys': [ + [0, 'rgb(0,0,0)'], [1, 'rgb(255,255,255)'] + ], + + 'YlGnBu': [ + [0, 'rgb(8,29,88)'], [0.125, 'rgb(37,52,148)'], + [0.25, 'rgb(34,94,168)'], [0.375, 'rgb(29,145,192)'], + [0.5, 'rgb(65,182,196)'], [0.625, 'rgb(127,205,187)'], + [0.75, 'rgb(199,233,180)'], [0.875, 'rgb(237,248,217)'], + [1, 'rgb(255,255,217)'] + ], + + 'Greens': [ + [0, 'rgb(0,68,27)'], [0.125, 'rgb(0,109,44)'], + [0.25, 'rgb(35,139,69)'], [0.375, 'rgb(65,171,93)'], + [0.5, 'rgb(116,196,118)'], [0.625, 'rgb(161,217,155)'], + [0.75, 'rgb(199,233,192)'], [0.875, 'rgb(229,245,224)'], + [1, 'rgb(247,252,245)'] + ], + + 'YlOrRd': [ + [0, 'rgb(128,0,38)'], [0.125, 'rgb(189,0,38)'], + [0.25, 'rgb(227,26,28)'], [0.375, 'rgb(252,78,42)'], + [0.5, 'rgb(253,141,60)'], [0.625, 'rgb(254,178,76)'], + [0.75, 'rgb(254,217,118)'], [0.875, 'rgb(255,237,160)'], + [1, 'rgb(255,255,204)'] + ], + + 'Bluered': [ + [0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)'] + ], + + # modified RdBu based on + # www.sandia.gov/~kmorel/documents/ColorMaps/ColorMapsExpanded.pdf + 'RdBu': [ + [0, 'rgb(5,10,172)'], [0.35, 'rgb(106,137,247)'], + [0.5, 'rgb(190,190,190)'], [0.6, 'rgb(220,170,132)'], + [0.7, 'rgb(230,145,90)'], [1, 'rgb(178,10,28)'] + ], + + # Scale for non-negative numeric values + 'Reds': [ + [0, 'rgb(220,220,220)'], [0.2, 'rgb(245,195,157)'], + [0.4, 'rgb(245,160,105)'], [1, 'rgb(178,10,28)'] + ], + + # Scale for non-positive numeric values + 'Blues': [ + [0, 'rgb(5,10,172)'], [0.35, 'rgb(40,60,190)'], + [0.5, 'rgb(70,100,245)'], [0.6, 'rgb(90,120,245)'], + [0.7, 'rgb(106,137,247)'], [1, 'rgb(220,220,220)'] + ], + + 'Picnic': [ + [0, 'rgb(0,0,255)'], [0.1, 'rgb(51,153,255)'], + [0.2, 'rgb(102,204,255)'], [0.3, 'rgb(153,204,255)'], + [0.4, 'rgb(204,204,255)'], [0.5, 'rgb(255,255,255)'], + [0.6, 'rgb(255,204,255)'], [0.7, 'rgb(255,153,255)'], + [0.8, 'rgb(255,102,204)'], [0.9, 'rgb(255,102,102)'], + [1, 'rgb(255,0,0)'] + ], + + 'Rainbow': [ + [0, 'rgb(150,0,90)'], [0.125, 'rgb(0,0,200)'], + [0.25, 'rgb(0,25,255)'], [0.375, 'rgb(0,152,255)'], + [0.5, 'rgb(44,255,150)'], [0.625, 'rgb(151,255,0)'], + [0.75, 'rgb(255,234,0)'], [0.875, 'rgb(255,111,0)'], + [1, 'rgb(255,0,0)'] + ], + + 'Portland': [ + [0, 'rgb(12,51,131)'], [0.25, 'rgb(10,136,186)'], + [0.5, 'rgb(242,211,56)'], [0.75, 'rgb(242,143,56)'], + [1, 'rgb(217,30,30)'] + ], + + 'Jet': [ + [0, 'rgb(0,0,131)'], [0.125, 'rgb(0,60,170)'], + [0.375, 'rgb(5,255,255)'], [0.625, 'rgb(255,255,0)'], + [0.875, 'rgb(250,0,0)'], [1, 'rgb(128,0,0)'] + ], + + 'Hot': [ + [0, 'rgb(0,0,0)'], [0.3, 'rgb(230,0,0)'], + [0.6, 'rgb(255,210,0)'], [1, 'rgb(255,255,255)'] + ], + + 'Blackbody': [ + [0, 'rgb(0,0,0)'], [0.2, 'rgb(230,0,0)'], + [0.4, 'rgb(230,210,0)'], [0.7, 'rgb(255,255,255)'], + [1, 'rgb(160,200,255)'] + ], + + 'Earth': [ + [0, 'rgb(0,0,130)'], [0.1, 'rgb(0,180,180)'], + [0.2, 'rgb(40,210,40)'], [0.4, 'rgb(230,230,50)'], + [0.6, 'rgb(120,70,20)'], [1, 'rgb(255,255,255)'] + ], + + 'Electric': [ + [0, 'rgb(0,0,0)'], [0.15, 'rgb(30,0,100)'], + [0.4, 'rgb(120,0,100)'], [0.6, 'rgb(160,90,0)'], + [0.8, 'rgb(230,200,0)'], [1, 'rgb(255,250,220)'] + ], + + 'Viridis': [ + [0, '#440154'], [0.06274509803921569, '#48186a'], + [0.12549019607843137, '#472d7b'], [0.18823529411764706, '#424086'], + [0.25098039215686274, '#3b528b'], [0.3137254901960784, '#33638d'], + [0.3764705882352941, '#2c728e'], [0.4392156862745098, '#26828e'], + [0.5019607843137255, '#21918c'], [0.5647058823529412, '#1fa088'], + [0.6274509803921569, '#28ae80'], [0.6901960784313725, '#3fbc73'], + [0.7529411764705882, '#5ec962'], [0.8156862745098039, '#84d44b'], + [0.8784313725490196, '#addc30'], [0.9411764705882353, '#d8e219'], + [1, '#fde725'] + ], + 'Cividis': [ + [0.000000, 'rgb(0,32,76)'], [0.058824, 'rgb(0,42,102)'], + [0.117647, 'rgb(0,52,110)'], [0.176471, 'rgb(39,63,108)'], + [0.235294, 'rgb(60,74,107)'], [0.294118, 'rgb(76,85,107)'], + [0.352941, 'rgb(91,95,109)'], [0.411765, 'rgb(104,106,112)'], + [0.470588, 'rgb(117,117,117)'], [0.529412, 'rgb(131,129,120)'], + [0.588235, 'rgb(146,140,120)'], [0.647059, 'rgb(161,152,118)'], + [0.705882, 'rgb(176,165,114)'], [0.764706, 'rgb(192,177,109)'], + [0.823529, 'rgb(209,191,102)'], [0.882353, 'rgb(225,204,92)'], + [0.941176, 'rgb(243,219,79)'], [1.000000, 'rgb(255,233,69)'] + ] } @@ -301,7 +410,12 @@ def validate_colors(colors, colortype='tuple'): if isinstance(colors, str): if colors in PLOTLY_SCALES: - colors = PLOTLY_SCALES[colors] + colors_list = colorscale_to_colors(PLOTLY_SCALES[colors]) + # TODO: fix _gantt.py/_scatter.py so that they can accept the + # actual colorscale and not just a list of the first and last + # color in the plotly colorscale. In resolving this issue we + # will be removing the immediate line below + colors = [colors_list[0]] + [colors_list[-1]] elif 'rgb' in colors or '#' in colors: colors = [colors] else: @@ -343,7 +457,7 @@ def validate_colors(colors, colortype='tuple'): ) colors[j] = each_color - if colortype == 'rgb': + if colortype == 'rgb' and not isinstance(colors, six.string_types): for j, each_color in enumerate(colors): rgb_color = color_parser(each_color, convert_to_RGB_255) colors[j] = color_parser(rgb_color, label_rgb) From 4b2b83beacf833264bbff197f5783a7d56f94ef6 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Tue, 11 Dec 2018 15:24:21 -0500 Subject: [PATCH 2/5] move all colors functions stricktly into colors.py and leave the rest in figure_factory.utils.py --- plotly/colors.py | 112 +++-- plotly/figure_factory/_2d_density.py | 7 +- plotly/figure_factory/_annotated_heatmap.py | 3 +- plotly/figure_factory/_bullet.py | 11 +- plotly/figure_factory/_county_choropleth.py | 25 +- plotly/figure_factory/_facet_grid.py | 13 +- plotly/figure_factory/_gantt.py | 15 +- plotly/figure_factory/_scatterplot.py | 27 +- plotly/figure_factory/_trisurf.py | 39 +- plotly/figure_factory/_violin.py | 11 +- plotly/figure_factory/utils.py | 462 -------------------- 11 files changed, 158 insertions(+), 567 deletions(-) diff --git a/plotly/colors.py b/plotly/colors.py index 5fc954fb9cc..62390f6e74b 100644 --- a/plotly/colors.py +++ b/plotly/colors.py @@ -77,6 +77,7 @@ import decimal from numbers import Number +import six from plotly import exceptions @@ -243,63 +244,106 @@ def color_parser(colors, function): return new_color_list -def validate_colors(colors): +def validate_colors(colors, colortype='tuple'): """ - Validates color(s) and returns an error for invalid color(s) - - :param (str|tuple|list) colors: either a plotly scale name, an rgb or hex - color, a color tuple or a list/tuple of colors + Validates color(s) and returns a list of color(s) of a specified type """ - colors_list = [] + from numbers import Number + if colors is None: + colors = DEFAULT_PLOTLY_COLORS - # if colors is a single color, put into colors_list if isinstance(colors, str): if colors in PLOTLY_SCALES: - return + colors_list = colorscale_to_colors(PLOTLY_SCALES[colors]) + # TODO: fix _gantt.py/_scatter.py so that they can accept the + # actual colorscale and not just a list of the first and last + # color in the plotly colorscale. In resolving this issue we + # will be removing the immediate line below + colors = [colors_list[0]] + [colors_list[-1]] elif 'rgb' in colors or '#' in colors: - colors_list.append(colors) + colors = [colors] else: raise exceptions.PlotlyError( - 'If your colors variable is a string, it must be a ' - 'Plotly scale, an rgb color or a hex color.' - ) + "If your colors variable is a string, it must be a " + "Plotly scale, an rgb color or a hex color.") elif isinstance(colors, tuple): if isinstance(colors[0], Number): - colors_list = [colors] + colors = [colors] else: - colors_list = list(colors) - - if isinstance(colors, dict): - colors_list.extend(colors.values()) + colors = list(colors) - elif isinstance(colors, list): - colors_list = colors - - # Validate colors in colors_list - for j, each_color in enumerate(colors_list): + # convert color elements in list to tuple color + for j, each_color in enumerate(colors): if 'rgb' in each_color: - each_color = color_parser( - each_color, unlabel_rgb - ) + each_color = color_parser(each_color, unlabel_rgb) for value in each_color: if value > 255.0: raise exceptions.PlotlyError( - 'Whoops! The elements in your rgb colors ' - 'tuples cannot exceed 255.0.' + "Whoops! The elements in your rgb colors " + "tuples cannot exceed 255.0." ) - elif '#' in each_color: - each_color = color_parser( - each_color, hex_to_rgb - ) - elif isinstance(each_color, tuple): + each_color = color_parser(each_color, unconvert_from_RGB_255) + colors[j] = each_color + + if '#' in each_color: + each_color = color_parser(each_color, hex_to_rgb) + each_color = color_parser(each_color, unconvert_from_RGB_255) + + colors[j] = each_color + + if isinstance(each_color, tuple): for value in each_color: if value > 1.0: raise exceptions.PlotlyError( - 'Whoops! The elements in your colors tuples ' - 'cannot exceed 1.0.' + "Whoops! The elements in your colors tuples " + "cannot exceed 1.0." + ) + colors[j] = each_color + + if colortype == 'rgb' and not isinstance(colors, six.string_types): + for j, each_color in enumerate(colors): + rgb_color = color_parser(each_color, convert_to_RGB_255) + colors[j] = color_parser(rgb_color, label_rgb) + + return colors + + +def validate_colors_dict(colors, colortype='tuple'): + """ + Validates dictioanry of color(s) + """ + # validate each color element in the dictionary + for key in colors: + if 'rgb' in colors[key]: + colors[key] = color_parser(colors[key], unlabel_rgb) + for value in colors[key]: + if value > 255.0: + raise exceptions.PlotlyError( + "Whoops! The elements in your rgb colors " + "tuples cannot exceed 255.0." + ) + colors[key] = color_parser(colors[key], unconvert_from_RGB_255) + + if '#' in colors[key]: + colors[key] = color_parser(colors[key], hex_to_rgb) + colors[key] = color_parser(colors[key], unconvert_from_RGB_255) + + if isinstance(colors[key], tuple): + for value in colors[key]: + if value > 1.0: + raise exceptions.PlotlyError( + "Whoops! The elements in your colors tuples " + "cannot exceed 1.0." ) + if colortype == 'rgb': + for key in colors: + colors[key] = color_parser(colors[key], convert_to_RGB_255) + colors[key] = color_parser(colors[key], label_rgb) + + return colors + def convert_colors_to_same_type(colors, colortype='rgb', scale=None, return_default_colors=False, diff --git a/plotly/figure_factory/_2d_density.py b/plotly/figure_factory/_2d_density.py index 8d004bff147..34f7a95900b 100644 --- a/plotly/figure_factory/_2d_density.py +++ b/plotly/figure_factory/_2d_density.py @@ -3,6 +3,7 @@ from numbers import Number from plotly import exceptions +import plotly.colors as clrs from plotly.figure_factory import utils from plotly.graph_objs import graph_objs @@ -100,12 +101,12 @@ def create_2d_density(x, y, colorscale='Earth', ncontours=20, "Both lists 'x' and 'y' must be the same length." ) - colorscale = utils.validate_colors(colorscale, 'rgb') + colorscale = clrs.validate_colors(colorscale, 'rgb') colorscale = make_linear_colorscale(colorscale) # validate hist_color and point_color - hist_color = utils.validate_colors(hist_color, 'rgb') - point_color = utils.validate_colors(point_color, 'rgb') + hist_color = clrs.validate_colors(hist_color, 'rgb') + point_color = clrs.validate_colors(point_color, 'rgb') trace1 = graph_objs.Scatter( x=x, y=y, mode='markers', name='points', diff --git a/plotly/figure_factory/_annotated_heatmap.py b/plotly/figure_factory/_annotated_heatmap.py index 3400aba0e48..f40d7f26ba9 100644 --- a/plotly/figure_factory/_annotated_heatmap.py +++ b/plotly/figure_factory/_annotated_heatmap.py @@ -1,6 +1,7 @@ from __future__ import absolute_import from plotly import exceptions, optional_imports +import plotly.colors as clrs from plotly.figure_factory import utils from plotly.graph_objs import graph_objs from plotly.validators.heatmap import ColorscaleValidator @@ -123,7 +124,7 @@ def to_rgb_color_list(color_str, default): if 'rgb' in color_str: return [int(v) for v in color_str.strip('rgb()').split(',')] elif '#' in color_str: - return utils.hex_to_rgb(color_str) + return clrs.hex_to_rgb(color_str) else: return default diff --git a/plotly/figure_factory/_bullet.py b/plotly/figure_factory/_bullet.py index bcb5b2541d1..1bdb69641b5 100644 --- a/plotly/figure_factory/_bullet.py +++ b/plotly/figure_factory/_bullet.py @@ -3,7 +3,8 @@ import collections import math -from plotly import colors, exceptions, optional_imports +from plotly import exceptions, optional_imports +import plotly.colors as clrs from plotly.figure_factory import utils import plotly @@ -78,7 +79,7 @@ def _bullet(df, markers, measures, ranges, subtitles, titles, orientation, for row in range(num_of_lanes): # ranges bars for idx in range(len(df.iloc[row]['ranges'])): - inter_colors = colors.n_colors( + inter_colors = clrs.n_colors( range_colors[0], range_colors[1], len(df.iloc[row]['ranges']), 'rgb' ) @@ -104,7 +105,7 @@ def _bullet(df, markers, measures, ranges, subtitles, titles, orientation, # measures bars for idx in range(len(df.iloc[row]['measures'])): - inter_colors = colors.n_colors( + inter_colors = clrs.n_colors( measure_colors[0], measure_colors[1], len(df.iloc[row]['measures']), 'rgb' ) @@ -317,8 +318,8 @@ def create_bullet(data, markers=None, measures=None, ranges=None, "Both 'range_colors' or 'measure_colors' must be a list " "of two valid colors." ) - colors.validate_colors(colors_list) - colors_list = colors.convert_colors_to_same_type(colors_list, + clrs.validate_colors(colors_list) + colors_list = clrs.convert_colors_to_same_type(colors_list, 'rgb')[0] # default scatter options diff --git a/plotly/figure_factory/_county_choropleth.py b/plotly/figure_factory/_county_choropleth.py index 8ee9fef7c2c..4e0f33462da 100644 --- a/plotly/figure_factory/_county_choropleth.py +++ b/plotly/figure_factory/_county_choropleth.py @@ -1,4 +1,5 @@ -from plotly import colors, exceptions, optional_imports +from plotly import exceptions, optional_imports +import plotly.colors as clrs from plotly.figure_factory import utils @@ -641,14 +642,14 @@ def create_choropleth(fips, values, scope=['usa'], binning_endpoints=None, if not colorscale: colorscale = [] - viridis_colors = colors.colorscale_to_colors( - colors.PLOTLY_SCALES['Viridis'] + viridis_colors = clrs.colorscale_to_colors( + clrs.PLOTLY_SCALES['Viridis'] ) - viridis_colors = colors.color_parser( - viridis_colors, colors.hex_to_rgb + viridis_colors = clrs.color_parser( + viridis_colors, clrs.hex_to_rgb ) - viridis_colors = colors.color_parser( - viridis_colors, colors.label_rgb + viridis_colors = clrs.color_parser( + viridis_colors, clrs.label_rgb ) viri_len = len(viridis_colors) + 1 viri_intervals = utils.endpts_to_intervals( @@ -665,7 +666,7 @@ def create_choropleth(fips, values, scope=['usa'], binning_endpoints=None, intermed = ((L - viri_intervals[idx][0]) / (viri_intervals[idx][1] - viri_intervals[idx][0])) - float_color = colors.find_intermediate_color( + float_color = clrs.find_intermediate_color( viridis_colors[idx], viridis_colors[idx], intermed, @@ -673,10 +674,10 @@ def create_choropleth(fips, values, scope=['usa'], binning_endpoints=None, ) # make R,G,B into int values - float_color = colors.unlabel_rgb(float_color) - float_color = colors.unconvert_from_RGB_255(float_color) - int_rgb = colors.convert_to_RGB_255(float_color) - int_rgb = colors.label_rgb(int_rgb) + float_color = clrs.unlabel_rgb(float_color) + float_color = clrs.unconvert_from_RGB_255(float_color) + int_rgb = clrs.convert_to_RGB_255(float_color) + int_rgb = clrs.label_rgb(int_rgb) colorscale.append(int_rgb) diff --git a/plotly/figure_factory/_facet_grid.py b/plotly/figure_factory/_facet_grid.py index a202599d747..542730747ff 100644 --- a/plotly/figure_factory/_facet_grid.py +++ b/plotly/figure_factory/_facet_grid.py @@ -1,6 +1,7 @@ from __future__ import absolute_import -from plotly import colors, exceptions, optional_imports +from plotly import exceptions, optional_imports +import plotly.colors as clrs from plotly.figure_factory import utils from plotly.tools import make_subplots @@ -888,7 +889,7 @@ def create_facet_grid(df, x=None, y=None, facet_row=None, facet_col=None, ) else: # use default plotly colors for dictionary - default_colors = utils.DEFAULT_PLOTLY_COLORS + default_colors = clrs.DEFAULT_PLOTLY_COLORS colormap = {} j = 0 for val in df[color_name].unique(): @@ -935,13 +936,13 @@ def create_facet_grid(df, x=None, y=None, facet_row=None, facet_col=None, marker_color, kwargs_trace, kwargs_marker ) elif isinstance(colormap, str): - if colormap in colors.PLOTLY_SCALES.keys(): - colorscale_list = colors.PLOTLY_SCALES[colormap] + if colormap in clrs.PLOTLY_SCALES.keys(): + colorscale_list = clrs.PLOTLY_SCALES[colormap] else: raise exceptions.PlotlyError( "If 'colormap' is a string, it must be the name " "of a Plotly Colorscale. The available colorscale " - "names are {}".format(colors.PLOTLY_SCALES.keys()) + "names are {}".format(clrs.PLOTLY_SCALES.keys()) ) fig, annotations = _facet_grid_color_numerical( df, x, y, facet_row, facet_col, color_name, @@ -951,7 +952,7 @@ def create_facet_grid(df, x=None, y=None, facet_row=None, facet_col=None, marker_color, kwargs_trace, kwargs_marker ) else: - colorscale_list = colors.PLOTLY_SCALES['Reds'] + colorscale_list = clrs.PLOTLY_SCALES['Reds'] fig, annotations = _facet_grid_color_numerical( df, x, y, facet_row, facet_col, color_name, colorscale_list, num_of_rows, num_of_cols, diff --git a/plotly/figure_factory/_gantt.py b/plotly/figure_factory/_gantt.py index 36024e6c1b5..e341655b9ce 100644 --- a/plotly/figure_factory/_gantt.py +++ b/plotly/figure_factory/_gantt.py @@ -3,6 +3,7 @@ from numbers import Number from plotly import exceptions, optional_imports +import plotly.colors as clrs from plotly.figure_factory import utils from plotly.graph_objs import graph_objs @@ -245,20 +246,20 @@ def gantt_colorscale(chart, colors, title, index_col, show_colorbar, bar_width, tasks[index]['y1'] = groupID + bar_width # unlabel color - colors = utils.color_parser(colors, utils.unlabel_rgb) + colors = clrs.color_parser(colors, clrs.unlabel_rgb) lowcolor = colors[0] highcolor = colors[1] intermed = (chart[index][index_col]) / 100.0 - intermed_color = utils.find_intermediate_color( + intermed_color = clrs.find_intermediate_color( lowcolor, highcolor, intermed ) - intermed_color = utils.color_parser( - intermed_color, utils.label_rgb + intermed_color = clrs.color_parser( + intermed_color, clrs.label_rgb ) tasks[index]['fillcolor'] = intermed_color # relabel colors with 'rgb' - colors = utils.color_parser(colors, utils.label_rgb) + colors = clrs.color_parser(colors, clrs.label_rgb) # add a line for hover text and autorange entry = dict( @@ -743,9 +744,9 @@ def create_gantt(df, colors=None, index_col=None, show_colorbar=False, # Validate colors if isinstance(colors, dict): - colors = utils.validate_colors_dict(colors, 'rgb') + colors = clrs.validate_colors_dict(colors, 'rgb') else: - colors = utils.validate_colors(colors, 'rgb') + colors = clrs.validate_colors(colors, 'rgb') if reverse_colors is True: colors.reverse() diff --git a/plotly/figure_factory/_scatterplot.py b/plotly/figure_factory/_scatterplot.py index 76e46f4e380..7ae9627ab16 100644 --- a/plotly/figure_factory/_scatterplot.py +++ b/plotly/figure_factory/_scatterplot.py @@ -2,7 +2,8 @@ import six -from plotly import colors, exceptions, optional_imports +from plotly import exceptions, optional_imports +import plotly.colors as clrs from plotly.figure_factory import utils from plotly.graph_objs import graph_objs from plotly.tools import make_subplots @@ -388,9 +389,9 @@ def scatterplot_theme(dataframe, headers, diag, size, height, width, title, # Convert colormap to list of n RGB tuples if colormap_type == 'seq': - foo = colors.color_parser(colormap, colors.unlabel_rgb) - foo = utils.n_colors(foo[0], foo[1], n_colors_len) - theme = colors.color_parser(foo, colors.label_rgb) + foo = clrs.color_parser(colormap, clrs.unlabel_rgb) + foo = clrs.n_colors(foo[0], foo[1], n_colors_len) + theme = clrs.color_parser(foo, clrs.label_rgb) if colormap_type == 'cat': # leave list of colors the same way @@ -558,9 +559,9 @@ def scatterplot_theme(dataframe, headers, diag, size, height, width, title, # Convert colormap to list of n RGB tuples if colormap_type == 'seq': - foo = colors.color_parser(colormap, colors.unlabel_rgb) - foo = utils.n_colors(foo[0], foo[1], len(intervals)) - theme = colors.color_parser(foo, colors.label_rgb) + foo = clrs.color_parser(colormap, clrs.unlabel_rgb) + foo = clrs.n_colors(foo[0], foo[1], len(intervals)) + theme = clrs.color_parser(foo, clrs.label_rgb) if colormap_type == 'cat': # leave list of colors the same way @@ -1081,22 +1082,22 @@ def create_scatterplotmatrix(df, index=None, endpts=None, diag='scatter', # Validate colormap if isinstance(colormap, dict): - colormap = utils.validate_colors_dict(colormap, 'rgb') + colormap = clrs.validate_colors_dict(colormap, 'rgb') elif isinstance(colormap, six.string_types) and 'rgb' not in colormap and '#' not in colormap: - if colormap not in utils.PLOTLY_SCALES.keys(): + if colormap not in clrs.PLOTLY_SCALES.keys(): raise exceptions.PlotlyError( "If 'colormap' is a string, it must be the name " "of a Plotly Colorscale. The available colorscale " - "names are {}".format(utils.PLOTLY_SCALES.keys()) + "names are {}".format(clrs.PLOTLY_SCALES.keys()) ) else: # TODO change below to allow the correct Plotly colorscale - colormap = utils.colorscale_to_colors(utils.PLOTLY_SCALES[colormap]) + colormap = clrs.colorscale_to_colors(clrs.PLOTLY_SCALES[colormap]) # keep only first and last item - fix later colormap = [colormap[0]] + [colormap[-1]] - colormap = utils.validate_colors(colormap, 'rgb') + colormap = clrs.validate_colors(colormap, 'rgb') else: - colormap = utils.validate_colors(colormap, 'rgb') + colormap = clrs.validate_colors(colormap, 'rgb') if not index: diff --git a/plotly/figure_factory/_trisurf.py b/plotly/figure_factory/_trisurf.py index 06e9cc04fee..3c49cf59665 100644 --- a/plotly/figure_factory/_trisurf.py +++ b/plotly/figure_factory/_trisurf.py @@ -1,6 +1,7 @@ from __future__ import absolute_import -from plotly import colors, exceptions, optional_imports +from plotly import exceptions, optional_imports +import plotly.colors as clrs from plotly.graph_objs import graph_objs np = optional_imports.get_module('numpy') @@ -24,14 +25,14 @@ def map_face2color(face, colormap, scale, vmin, vmax): if len(colormap) == 1: # color each triangle face with the same color in colormap face_color = colormap[0] - face_color = colors.convert_to_RGB_255(face_color) - face_color = colors.label_rgb(face_color) + face_color = clrs.convert_to_RGB_255(face_color) + face_color = clrs.label_rgb(face_color) return face_color if face == vmax: # pick last color in colormap face_color = colormap[-1] - face_color = colors.convert_to_RGB_255(face_color) - face_color = colors.label_rgb(face_color) + face_color = clrs.convert_to_RGB_255(face_color) + face_color = clrs.label_rgb(face_color) return face_color else: if scale is None: @@ -40,14 +41,14 @@ def map_face2color(face, colormap, scale, vmin, vmax): t = (face - vmin) / float((vmax - vmin)) low_color_index = int(t / (1./(len(colormap) - 1))) - face_color = colors.find_intermediate_color( + face_color = clrs.find_intermediate_color( colormap[low_color_index], colormap[low_color_index + 1], t * (len(colormap) - 1) - low_color_index ) - face_color = colors.convert_to_RGB_255(face_color) - face_color = colors.label_rgb(face_color) + face_color = clrs.convert_to_RGB_255(face_color) + face_color = clrs.label_rgb(face_color) else: # find the face color for a non-linearly interpolated scale t = (face - vmin) / float((vmax - vmin)) @@ -61,14 +62,14 @@ def map_face2color(face, colormap, scale, vmin, vmax): low_scale_val = scale[low_color_index] high_scale_val = scale[low_color_index + 1] - face_color = colors.find_intermediate_color( + face_color = clrs.find_intermediate_color( colormap[low_color_index], colormap[low_color_index + 1], (t - low_scale_val)/(high_scale_val - low_scale_val) ) - face_color = colors.convert_to_RGB_255(face_color) - face_color = colors.label_rgb(face_color) + face_color = clrs.convert_to_RGB_255(face_color) + face_color = clrs.label_rgb(face_color) return face_color @@ -102,12 +103,12 @@ def trisurf(x, y, z, simplices, show_colorbar, edges_color, scale, for index in range(len(color_func)): if isinstance(color_func[index], str): if '#' in color_func[index]: - foo = colors.hex_to_rgb(color_func[index]) - color_func[index] = colors.label_rgb(foo) + foo = clrs.hex_to_rgb(color_func[index]) + color_func[index] = clrs.label_rgb(foo) if isinstance(color_func[index], tuple): - foo = colors.convert_to_RGB_255(color_func[index]) - color_func[index] = colors.label_rgb(foo) + foo = clrs.convert_to_RGB_255(color_func[index]) + color_func[index] = clrs.label_rgb(foo) mean_dists = np.asarray(color_func) else: @@ -147,8 +148,8 @@ def trisurf(x, y, z, simplices, show_colorbar, edges_color, scale, if mean_dists_are_numbers and show_colorbar is True: # make a colorscale from the colors - colorscale = colors.make_colorscale(colormap, scale) - colorscale = colors.convert_colorscale_to_rgb(colorscale) + colorscale = clrs.make_colorscale(colormap, scale) + colorscale = clrs.convert_colorscale_to_rgb(colorscale) colorbar = graph_objs.Scatter3d( x=x[:1], @@ -454,8 +455,8 @@ def dist_origin(x, y, z): aspectratio = {'x': 1, 'y': 1, 'z': 1} # Validate colormap - colors.validate_colors(colormap) - colormap, scale = colors.convert_colors_to_same_type( + clrs.validate_colors(colormap) + colormap, scale = clrs.convert_colors_to_same_type( colormap, colortype='tuple', return_default_colors=True, scale=scale ) diff --git a/plotly/figure_factory/_violin.py b/plotly/figure_factory/_violin.py index 384f3497a15..9aaf74f8628 100644 --- a/plotly/figure_factory/_violin.py +++ b/plotly/figure_factory/_violin.py @@ -3,6 +3,7 @@ from numbers import Number from plotly import exceptions, optional_imports +import plotly.colors as clrs from plotly.figure_factory import utils from plotly.graph_objs import graph_objs from plotly.tools import make_subplots @@ -285,8 +286,8 @@ def violin_colorscale(data, data_header, group_header, colors, use_colorscale, print_grid=False) # prepare low and high color for colorscale - lowcolor = utils.color_parser(colors[0], utils.unlabel_rgb) - highcolor = utils.color_parser(colors[1], utils.unlabel_rgb) + lowcolor = clrs.color_parser(colors[0], clrs.unlabel_rgb) + highcolor = clrs.color_parser(colors[1], clrs.unlabel_rgb) # find min and max values in group_stats group_stats_values = [] @@ -301,7 +302,7 @@ def violin_colorscale(data, data_header, group_header, colors, use_colorscale, # find intermediate color from colorscale intermed = (group_stats[gr] - min_value) / (max_value - min_value) - intermed_color = utils.find_intermediate_color( + intermed_color = clrs.find_intermediate_color( lowcolor, highcolor, intermed ) @@ -545,9 +546,9 @@ def create_violin(data, data_header=None, group_header=None, colors=None, # Validate colors if isinstance(colors, dict): - valid_colors = utils.validate_colors_dict(colors, 'rgb') + valid_colors = clrs.validate_colors_dict(colors, 'rgb') else: - valid_colors = utils.validate_colors(colors, 'rgb') + valid_colors = clrs.validate_colors(colors, 'rgb') # validate data and choose plot type if group_header is None: diff --git a/plotly/figure_factory/utils.py b/plotly/figure_factory/utils.py index 885433d44fc..e6d6b2f05da 100644 --- a/plotly/figure_factory/utils.py +++ b/plotly/figure_factory/utils.py @@ -7,143 +7,6 @@ from plotly import exceptions -DEFAULT_PLOTLY_COLORS = ['rgb(31, 119, 180)', 'rgb(255, 127, 14)', - 'rgb(44, 160, 44)', 'rgb(214, 39, 40)', - 'rgb(148, 103, 189)', 'rgb(140, 86, 75)', - 'rgb(227, 119, 194)', 'rgb(127, 127, 127)', - 'rgb(188, 189, 34)', 'rgb(23, 190, 207)'] - -PLOTLY_SCALES = { - 'Greys': [ - [0, 'rgb(0,0,0)'], [1, 'rgb(255,255,255)'] - ], - - 'YlGnBu': [ - [0, 'rgb(8,29,88)'], [0.125, 'rgb(37,52,148)'], - [0.25, 'rgb(34,94,168)'], [0.375, 'rgb(29,145,192)'], - [0.5, 'rgb(65,182,196)'], [0.625, 'rgb(127,205,187)'], - [0.75, 'rgb(199,233,180)'], [0.875, 'rgb(237,248,217)'], - [1, 'rgb(255,255,217)'] - ], - - 'Greens': [ - [0, 'rgb(0,68,27)'], [0.125, 'rgb(0,109,44)'], - [0.25, 'rgb(35,139,69)'], [0.375, 'rgb(65,171,93)'], - [0.5, 'rgb(116,196,118)'], [0.625, 'rgb(161,217,155)'], - [0.75, 'rgb(199,233,192)'], [0.875, 'rgb(229,245,224)'], - [1, 'rgb(247,252,245)'] - ], - - 'YlOrRd': [ - [0, 'rgb(128,0,38)'], [0.125, 'rgb(189,0,38)'], - [0.25, 'rgb(227,26,28)'], [0.375, 'rgb(252,78,42)'], - [0.5, 'rgb(253,141,60)'], [0.625, 'rgb(254,178,76)'], - [0.75, 'rgb(254,217,118)'], [0.875, 'rgb(255,237,160)'], - [1, 'rgb(255,255,204)'] - ], - - 'Bluered': [ - [0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)'] - ], - - # modified RdBu based on - # www.sandia.gov/~kmorel/documents/ColorMaps/ColorMapsExpanded.pdf - 'RdBu': [ - [0, 'rgb(5,10,172)'], [0.35, 'rgb(106,137,247)'], - [0.5, 'rgb(190,190,190)'], [0.6, 'rgb(220,170,132)'], - [0.7, 'rgb(230,145,90)'], [1, 'rgb(178,10,28)'] - ], - - # Scale for non-negative numeric values - 'Reds': [ - [0, 'rgb(220,220,220)'], [0.2, 'rgb(245,195,157)'], - [0.4, 'rgb(245,160,105)'], [1, 'rgb(178,10,28)'] - ], - - # Scale for non-positive numeric values - 'Blues': [ - [0, 'rgb(5,10,172)'], [0.35, 'rgb(40,60,190)'], - [0.5, 'rgb(70,100,245)'], [0.6, 'rgb(90,120,245)'], - [0.7, 'rgb(106,137,247)'], [1, 'rgb(220,220,220)'] - ], - - 'Picnic': [ - [0, 'rgb(0,0,255)'], [0.1, 'rgb(51,153,255)'], - [0.2, 'rgb(102,204,255)'], [0.3, 'rgb(153,204,255)'], - [0.4, 'rgb(204,204,255)'], [0.5, 'rgb(255,255,255)'], - [0.6, 'rgb(255,204,255)'], [0.7, 'rgb(255,153,255)'], - [0.8, 'rgb(255,102,204)'], [0.9, 'rgb(255,102,102)'], - [1, 'rgb(255,0,0)'] - ], - - 'Rainbow': [ - [0, 'rgb(150,0,90)'], [0.125, 'rgb(0,0,200)'], - [0.25, 'rgb(0,25,255)'], [0.375, 'rgb(0,152,255)'], - [0.5, 'rgb(44,255,150)'], [0.625, 'rgb(151,255,0)'], - [0.75, 'rgb(255,234,0)'], [0.875, 'rgb(255,111,0)'], - [1, 'rgb(255,0,0)'] - ], - - 'Portland': [ - [0, 'rgb(12,51,131)'], [0.25, 'rgb(10,136,186)'], - [0.5, 'rgb(242,211,56)'], [0.75, 'rgb(242,143,56)'], - [1, 'rgb(217,30,30)'] - ], - - 'Jet': [ - [0, 'rgb(0,0,131)'], [0.125, 'rgb(0,60,170)'], - [0.375, 'rgb(5,255,255)'], [0.625, 'rgb(255,255,0)'], - [0.875, 'rgb(250,0,0)'], [1, 'rgb(128,0,0)'] - ], - - 'Hot': [ - [0, 'rgb(0,0,0)'], [0.3, 'rgb(230,0,0)'], - [0.6, 'rgb(255,210,0)'], [1, 'rgb(255,255,255)'] - ], - - 'Blackbody': [ - [0, 'rgb(0,0,0)'], [0.2, 'rgb(230,0,0)'], - [0.4, 'rgb(230,210,0)'], [0.7, 'rgb(255,255,255)'], - [1, 'rgb(160,200,255)'] - ], - - 'Earth': [ - [0, 'rgb(0,0,130)'], [0.1, 'rgb(0,180,180)'], - [0.2, 'rgb(40,210,40)'], [0.4, 'rgb(230,230,50)'], - [0.6, 'rgb(120,70,20)'], [1, 'rgb(255,255,255)'] - ], - - 'Electric': [ - [0, 'rgb(0,0,0)'], [0.15, 'rgb(30,0,100)'], - [0.4, 'rgb(120,0,100)'], [0.6, 'rgb(160,90,0)'], - [0.8, 'rgb(230,200,0)'], [1, 'rgb(255,250,220)'] - ], - - 'Viridis': [ - [0, '#440154'], [0.06274509803921569, '#48186a'], - [0.12549019607843137, '#472d7b'], [0.18823529411764706, '#424086'], - [0.25098039215686274, '#3b528b'], [0.3137254901960784, '#33638d'], - [0.3764705882352941, '#2c728e'], [0.4392156862745098, '#26828e'], - [0.5019607843137255, '#21918c'], [0.5647058823529412, '#1fa088'], - [0.6274509803921569, '#28ae80'], [0.6901960784313725, '#3fbc73'], - [0.7529411764705882, '#5ec962'], [0.8156862745098039, '#84d44b'], - [0.8784313725490196, '#addc30'], [0.9411764705882353, '#d8e219'], - [1, '#fde725'] - ], - 'Cividis': [ - [0.000000, 'rgb(0,32,76)'], [0.058824, 'rgb(0,42,102)'], - [0.117647, 'rgb(0,52,110)'], [0.176471, 'rgb(39,63,108)'], - [0.235294, 'rgb(60,74,107)'], [0.294118, 'rgb(76,85,107)'], - [0.352941, 'rgb(91,95,109)'], [0.411765, 'rgb(104,106,112)'], - [0.470588, 'rgb(117,117,117)'], [0.529412, 'rgb(131,129,120)'], - [0.588235, 'rgb(146,140,120)'], [0.647059, 'rgb(161,152,118)'], - [0.705882, 'rgb(176,165,114)'], [0.764706, 'rgb(192,177,109)'], - [0.823529, 'rgb(209,191,102)'], [0.882353, 'rgb(225,204,92)'], - [0.941176, 'rgb(243,219,79)'], [1.000000, 'rgb(255,233,69)'] - ] -} - - def is_sequence(obj): return (isinstance(obj, collections.Sequence) and not isinstance(obj, str)) @@ -239,331 +102,6 @@ def flatten(array): "flattened! Make sure your data is " "entered as lists or ndarrays!") - -def find_intermediate_color(lowcolor, highcolor, intermed): - """ - Returns the color at a given distance between two colors - - This function takes two color tuples, where each element is between 0 - and 1, along with a value 0 < intermed < 1 and returns a color that is - intermed-percent from lowcolor to highcolor - - """ - diff_0 = float(highcolor[0] - lowcolor[0]) - diff_1 = float(highcolor[1] - lowcolor[1]) - diff_2 = float(highcolor[2] - lowcolor[2]) - - return (lowcolor[0] + intermed * diff_0, - lowcolor[1] + intermed * diff_1, - lowcolor[2] + intermed * diff_2) - - -def n_colors(lowcolor, highcolor, n_colors): - """ - Splits a low and high color into a list of n_colors colors in it - - Accepts two color tuples and returns a list of n_colors colors - which form the intermediate colors between lowcolor and highcolor - from linearly interpolating through RGB space - - """ - diff_0 = float(highcolor[0] - lowcolor[0]) - incr_0 = diff_0/(n_colors - 1) - diff_1 = float(highcolor[1] - lowcolor[1]) - incr_1 = diff_1/(n_colors - 1) - diff_2 = float(highcolor[2] - lowcolor[2]) - incr_2 = diff_2/(n_colors - 1) - color_tuples = [] - - for index in range(n_colors): - new_tuple = (lowcolor[0] + (index * incr_0), - lowcolor[1] + (index * incr_1), - lowcolor[2] + (index * incr_2)) - color_tuples.append(new_tuple) - - return color_tuples - - -def label_rgb(colors): - """ - Takes tuple (a, b, c) and returns an rgb color 'rgb(a, b, c)' - """ - return ('rgb(%s, %s, %s)' % (colors[0], colors[1], colors[2])) - - -def unlabel_rgb(colors): - """ - Takes rgb color(s) 'rgb(a, b, c)' and returns tuple(s) (a, b, c) - - This function takes either an 'rgb(a, b, c)' color or a list of - such colors and returns the color tuples in tuple(s) (a, b, c) - - """ - str_vals = '' - for index in range(len(colors)): - try: - float(colors[index]) - str_vals = str_vals + colors[index] - except ValueError: - if colors[index] == ',' or colors[index] == '.': - str_vals = str_vals + colors[index] - - str_vals = str_vals + ',' - numbers = [] - str_num = '' - for char in str_vals: - if char != ',': - str_num = str_num + char - else: - numbers.append(float(str_num)) - str_num = '' - return (numbers[0], numbers[1], numbers[2]) - - -def unconvert_from_RGB_255(colors): - """ - Return a tuple where each element gets divided by 255 - - Takes a (list of) color tuple(s) where each element is between 0 and - 255. Returns the same tuples where each tuple element is normalized to - a value between 0 and 1 - - """ - return (colors[0]/(255.0), - colors[1]/(255.0), - colors[2]/(255.0)) - - -def convert_to_RGB_255(colors): - """ - Multiplies each element of a triplet by 255 - - Each coordinate of the color tuple is rounded to the nearest float and - then is turned into an integer. If a number is of the form x.5, then - if x is odd, the number rounds up to (x+1). Otherwise, it rounds down - to just x. This is the way rounding works in Python 3 and in current - statistical analysis to avoid rounding bias - """ - rgb_components = [] - - for component in colors: - rounded_num = decimal.Decimal(str(component*255.0)).quantize( - decimal.Decimal('1'), rounding=decimal.ROUND_HALF_EVEN - ) - # convert rounded number to an integer from 'Decimal' form - rounded_num = int(rounded_num) - rgb_components.append(rounded_num) - - return (rgb_components[0], rgb_components[1], rgb_components[2]) - - -def hex_to_rgb(value): - """ - Calculates rgb values from a hex color code. - - :param (string) value: Hex color string - - :rtype (tuple) (r_value, g_value, b_value): tuple of rgb values - """ - value = value.lstrip('#') - hex_total_length = len(value) - rgb_section_length = hex_total_length // 3 - return tuple(int(value[i:i + rgb_section_length], 16) - for i in range(0, hex_total_length, rgb_section_length)) - - -def color_parser(colors, function): - """ - Takes color(s) and a function and applies the function on the color(s) - - In particular, this function identifies whether the given color object - is an iterable or not and applies the given color-parsing function to - the color or iterable of colors. If given an iterable, it will only be - able to work with it if all items in the iterable are of the same type - - rgb string, hex string or tuple - - """ - from numbers import Number - if isinstance(colors, str): - return function(colors) - - if isinstance(colors, tuple) and isinstance(colors[0], Number): - return function(colors) - - if hasattr(colors, '__iter__'): - if isinstance(colors, tuple): - new_color_tuple = tuple(function(item) for item in colors) - return new_color_tuple - - else: - new_color_list = [function(item) for item in colors] - return new_color_list - - -def validate_colors(colors, colortype='tuple'): - """ - Validates color(s) and returns a list of color(s) of a specified type - """ - from numbers import Number - if colors is None: - colors = DEFAULT_PLOTLY_COLORS - - if isinstance(colors, str): - if colors in PLOTLY_SCALES: - colors_list = colorscale_to_colors(PLOTLY_SCALES[colors]) - # TODO: fix _gantt.py/_scatter.py so that they can accept the - # actual colorscale and not just a list of the first and last - # color in the plotly colorscale. In resolving this issue we - # will be removing the immediate line below - colors = [colors_list[0]] + [colors_list[-1]] - elif 'rgb' in colors or '#' in colors: - colors = [colors] - else: - raise exceptions.PlotlyError( - "If your colors variable is a string, it must be a " - "Plotly scale, an rgb color or a hex color.") - - elif isinstance(colors, tuple): - if isinstance(colors[0], Number): - colors = [colors] - else: - colors = list(colors) - - # convert color elements in list to tuple color - for j, each_color in enumerate(colors): - if 'rgb' in each_color: - each_color = color_parser(each_color, unlabel_rgb) - for value in each_color: - if value > 255.0: - raise exceptions.PlotlyError( - "Whoops! The elements in your rgb colors " - "tuples cannot exceed 255.0." - ) - each_color = color_parser(each_color, unconvert_from_RGB_255) - colors[j] = each_color - - if '#' in each_color: - each_color = color_parser(each_color, hex_to_rgb) - each_color = color_parser(each_color, unconvert_from_RGB_255) - - colors[j] = each_color - - if isinstance(each_color, tuple): - for value in each_color: - if value > 1.0: - raise exceptions.PlotlyError( - "Whoops! The elements in your colors tuples " - "cannot exceed 1.0." - ) - colors[j] = each_color - - if colortype == 'rgb' and not isinstance(colors, six.string_types): - for j, each_color in enumerate(colors): - rgb_color = color_parser(each_color, convert_to_RGB_255) - colors[j] = color_parser(rgb_color, label_rgb) - - return colors - - -def validate_colors_dict(colors, colortype='tuple'): - """ - Validates dictioanry of color(s) - """ - # validate each color element in the dictionary - for key in colors: - if 'rgb' in colors[key]: - colors[key] = color_parser(colors[key], unlabel_rgb) - for value in colors[key]: - if value > 255.0: - raise exceptions.PlotlyError( - "Whoops! The elements in your rgb colors " - "tuples cannot exceed 255.0." - ) - colors[key] = color_parser(colors[key], unconvert_from_RGB_255) - - if '#' in colors[key]: - colors[key] = color_parser(colors[key], hex_to_rgb) - colors[key] = color_parser(colors[key], unconvert_from_RGB_255) - - if isinstance(colors[key], tuple): - for value in colors[key]: - if value > 1.0: - raise exceptions.PlotlyError( - "Whoops! The elements in your colors tuples " - "cannot exceed 1.0." - ) - - if colortype == 'rgb': - for key in colors: - colors[key] = color_parser(colors[key], convert_to_RGB_255) - colors[key] = color_parser(colors[key], label_rgb) - - return colors - -def colorscale_to_colors(colorscale): - """ - Extracts the colors from colorscale as a list - """ - color_list = [] - for item in colorscale: - color_list.append(item[1]) - return color_list - - -def colorscale_to_scale(colorscale): - """ - Extracts the interpolation scale values from colorscale as a list - """ - scale_list = [] - for item in colorscale: - scale_list.append(item[0]) - return scale_list - - -def validate_scale_values(scale): - """ - Validates scale values from a colorscale - - :param (list) scale: a strictly increasing list of floats that begins - with 0 and ends with 1. Its usage derives from a colorscale which is - a list of two-lists (a list with two elements) of the form - [value, color] which are used to determine how interpolation weighting - works between the colors in the colorscale. Therefore scale is just - the extraction of these values from the two-lists in order - """ - if len(scale) < 2: - raise exceptions.PlotlyError('You must input a list of scale values ' - 'that has at least two values.') - - if (scale[0] != 0) or (scale[-1] != 1): - raise exceptions.PlotlyError( - 'The first and last number in your scale must be 0.0 and 1.0 ' - 'respectively.' - ) - - if not all(x < y for x, y in zip(scale, scale[1:])): - raise exceptions.PlotlyError( - "'scale' must be a list that contains a strictly increasing " - "sequence of numbers." - ) - - -def validate_colorscale(colorscale): - """Validate the structure, scale values and colors of colorscale.""" - if not isinstance(colorscale, list): - #TODO Write tests for these exceptions - raise exceptions.PlotlyError("A valid colorscale must be a list.") - if not all(isinstance(innerlist, list) for innerlist in colorscale): - raise exceptions.PlotlyError( - "A valid colorscale must be a list of lists." - ) - colorscale_colors = colorscale_to_colors(colorscale) - scale_values = colorscale_to_scale(colorscale) - - validate_scale_values(scale_values) - validate_colors(colorscale_colors) - - def endpts_to_intervals(endpts): """ Returns a list of intervals for categorical colormaps From 57ec804aeb89db1c24dd937bd14f3ee18cf95321 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 12 Dec 2018 08:25:47 -0500 Subject: [PATCH 3/5] change utils.validate_colorsc_dict to clrs.validate_colors_dict --- plotly/figure_factory/_facet_grid.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plotly/figure_factory/_facet_grid.py b/plotly/figure_factory/_facet_grid.py index 542730747ff..5ac1a260045 100644 --- a/plotly/figure_factory/_facet_grid.py +++ b/plotly/figure_factory/_facet_grid.py @@ -878,7 +878,7 @@ def create_facet_grid(df, x=None, y=None, facet_row=None, facet_col=None, if isinstance(df[color_name].iloc[0], str) or color_is_cat: show_legend = True if isinstance(colormap, dict): - utils.validate_colors_dict(colormap, 'rgb') + clrs.validate_colors_dict(colormap, 'rgb') for val in df[color_name].unique(): if val not in colormap.keys(): @@ -907,7 +907,7 @@ def create_facet_grid(df, x=None, y=None, facet_row=None, facet_col=None, elif isinstance(df[color_name].iloc[0], Number): if isinstance(colormap, dict): show_legend = True - utils.validate_colors_dict(colormap, 'rgb') + clrs.validate_colors_dict(colormap, 'rgb') for val in df[color_name].unique(): if val not in colormap.keys(): @@ -926,7 +926,7 @@ def create_facet_grid(df, x=None, y=None, facet_row=None, facet_col=None, elif isinstance(colormap, list): colorscale_list = colormap - utils.validate_colorscale(colorscale_list) + clrs.validate_colorscale(colorscale_list) fig, annotations = _facet_grid_color_numerical( df, x, y, facet_row, facet_col, color_name, From 3ac74cb44663c527e9aa3d4f8271f57e05970e05 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 12 Dec 2018 08:28:40 -0500 Subject: [PATCH 4/5] reference is_sequence in _bullet.py to already existing function in figure_factory/utils.py --- plotly/figure_factory/_bullet.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/plotly/figure_factory/_bullet.py b/plotly/figure_factory/_bullet.py index 1bdb69641b5..f37b0f0e3f4 100644 --- a/plotly/figure_factory/_bullet.py +++ b/plotly/figure_factory/_bullet.py @@ -13,15 +13,9 @@ pd = optional_imports.get_module('pandas') -def is_sequence(obj): - return (isinstance(obj, collections.Sequence) and - not isinstance(obj, str)) - - def _bullet(df, markers, measures, ranges, subtitles, titles, orientation, range_colors, measure_colors, horizontal_spacing, vertical_spacing, scatter_options, layout_options): - num_of_lanes = len(df) num_of_rows = num_of_lanes if orientation == 'h' else 1 num_of_cols = 1 if orientation == 'h' else num_of_lanes @@ -262,7 +256,7 @@ def create_bullet(data, markers=None, measures=None, ranges=None, "'pandas' must be installed for this figure factory." ) - if is_sequence(data): + if utils.is_sequence(data): if not all(isinstance(item, dict) for item in data): raise exceptions.PlotlyError( 'Every entry of the data argument list, tuple, etc must ' @@ -276,7 +270,7 @@ def create_bullet(data, markers=None, measures=None, ranges=None, # make DataFrame from data with correct column headers col_names = ['titles', 'subtitle', 'markers', 'measures', 'ranges'] - if is_sequence(data): + if utils.is_sequence(data): df = pd.DataFrame( [ [d[titles] for d in data] if titles else [''] * len(data), From 7032b676f2155fea6677a991947de5c564b0f67a Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 17 Dec 2018 13:05:50 -0500 Subject: [PATCH 5/5] add validate_colorscale back to colors.py (accidentally deleted) and add imports to functions that got moved from utils to colors --- plotly/colors.py | 16 ++++++++++++++++ plotly/figure_factory/utils.py | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/plotly/colors.py b/plotly/colors.py index 62390f6e74b..8ec66f78cb8 100644 --- a/plotly/colors.py +++ b/plotly/colors.py @@ -503,6 +503,22 @@ def validate_scale_values(scale): ) +def validate_colorscale(colorscale): + """Validate the structure, scale values and colors of colorscale.""" + if not isinstance(colorscale, list): + # TODO Write tests for these exceptions + raise exceptions.PlotlyError("A valid colorscale must be a list.") + if not all(isinstance(innerlist, list) for innerlist in colorscale): + raise exceptions.PlotlyError( + "A valid colorscale must be a list of lists." + ) + colorscale_colors = colorscale_to_colors(colorscale) + scale_values = colorscale_to_scale(colorscale) + + validate_scale_values(scale_values) + validate_colors(colorscale_colors) + + def make_colorscale(colors, scale=None): """ Makes a colorscale from a list of colors and a scale diff --git a/plotly/figure_factory/utils.py b/plotly/figure_factory/utils.py index e6d6b2f05da..2226d738709 100644 --- a/plotly/figure_factory/utils.py +++ b/plotly/figure_factory/utils.py @@ -5,6 +5,13 @@ import six 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):