From 651e7255f60d59cf963c0622a98abafc75bb1590 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 18 May 2016 13:22:06 -0400 Subject: [PATCH 01/27] Changed tools.py --- plotly/tools.py | 212 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 0a109d08c37..711158f05fd 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1450,6 +1450,218 @@ class FigureFactory(object): more information and examples of a specific chart type. """ + @staticmethod + def _calc_stats(data): + import numpy as np + + x = np.asarray(data, np.float) + vals_min = np.min(x) + vals_max = np.max(x) + q2 = np.percentile(x, 50, interpolation='linear') + q1 = np.percentile(x, 25, interpolation='lower') + q3 = np.percentile(x, 75, interpolation='higher') + IQR = q3 - q1 + whisker_dist = 1.5 * IQR + + # in order to prevent drawing whiskers outside the interval + # of data one defines the whisker positions as: + d1 = np.min(x[x >= (q1 - whisker_dist)]) + d2 = np.max(x[x <= (q3 + whisker_dist)]) + return vals_min, vals_max, q1, q2, q3, d1, d2 + + @staticmethod + def _make_half_violin(x, y, fillcolor='#1f77b4', linecolor='rgb(50,50,50)'): + from plotly.graph_objs import graph_objs + + text = ['(pdf(y), y)=(' + '{:0.2f}'.format(x[i]) + ', ' + '{:0.2f}'.format(y[i]) + ')' + for i in range(len(x))] + return graph_objs.Scatter( + x=x, + y=y, mode='lines', + name='', + text=text, + fill='tonextx', + fillcolor=fillcolor, + line=graph_objs.Line(width=0.5, color=linecolor, shape='spline'), + hoverinfo='text', + opacity=0.5 + ) + + @staticmethod + def _make_rugplot(vals, pdf_max, distance, color='#1f77b4'): + from plotly.graph_objs import graph_objs + + return graph_objs.Scatter( + y=vals, + x=[-pdf_max-distance]*len(vals), + marker=graph_objs.Marker( + color=color, + symbol='line-ew-open' + ), + mode='markers', + name='', + showlegend=False, + hoverinfo='y' + ) + + @staticmethod + def _make_quartiles(q1, q3): + from plotly.graph_objs import graph_objs + + return graph_objs.Scatter( + x=[0, 0], + y=[q1, q3], + text=['lower-quartile: ' + '{:0.2f}'.format(q1), + 'upper-quartile: ' + '{:0.2f}'.format(q3)], + mode='lines', + line=graph_objs.Line( + width=4, + color='rgb(0,0,0)' + ), + hoverinfo='text' + ) + + @staticmethod + def _make_median(q2): + from plotly.graph_objs import graph_objs + + return graph_objs.Scatter( + x=[0], + y=[q2], + text=['median: ' + '{:0.2f}'.format(q2)], + mode='markers', + marker=dict(symbol='square', + color='rgb(255,255,255)'), + hoverinfo='text' + ) + + @staticmethod + def _make_non_outlier_interval(d1, d2): + from plotly.graph_objs import graph_objs + + return graph_objs.Scatter( + x=[0, 0], + y=[d1, d2], + name='', + mode='lines', + line=graph_objs.Line(width=1.5, + color='rgb(0,0,0)') + ) + + @staticmethod + def _make_XAxis(xaxis_title, xaxis_range): + from plotly.graph_objs import graph_objs + + xaxis = graph_objs.XAxis(title=xaxis_title, + range=xaxis_range, + showgrid=False, + zeroline=False, + showline=False, + mirror=False, + ticks='', + showticklabels=False, + ) + return xaxis + + @staticmethod + def _make_YAxis(yaxis_title): + from plotly.graph_objs import graph_objs + + yaxis = graph_objs.YAxis(title=yaxis_title, + showticklabels=True, + autorange=True, + ticklen=4, + showline=True, + zeroline=False, + showgrid=False, + mirror=False) + return yaxis + + @staticmethod + def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): + import numpy as np + from scipy import stats + + vals = np.asarray(vals, np.float) + # summary statistics + vals_min, vals_max, q1, q2, q3, d1, d2 = FigureFactory._calc_stats(vals) + # kernel density estimation of pdf + pdf = stats.gaussian_kde(vals) + # grid over the data interval + xx = np.linspace(vals_min, vals_max, 100) + # evaluate the pdf at the grid xx + yy = pdf(xx) + max_pdf = np.max(yy) + # distance from the violin plot to rugplot + distance = 2.0*max_pdf/10 if rugplot else 0 + # range for x values in the plot + plot_xrange = [-max_pdf-distance-0.1, max_pdf+0.1] + plot_data = [FigureFactory._make_half_violin(-yy, xx, fillcolor=fillcolor), + FigureFactory._make_half_violin(yy, xx, fillcolor=fillcolor), + FigureFactory._make_non_outlier_interval(d1, d2), + FigureFactory._make_quartiles(q1, q3), + FigureFactory._make_median(q2)] + if rugplot: + plot_data.append(FigureFactory._make_rugplot(vals, + max_pdf, + distance=distance, + color=fillcolor)) + return plot_data, plot_xrange + + @staticmethod + def create_violin(data, color=None, colorscale=None, + title='Violin and Rug Plot'): + """ + Doc String Goes Here + """ + from plotly.graph_objs import graph_objs + 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': ['rgb(68,1,84)', 'rgb(253,231,37)']} + + if isinstance(data, list): + + if color is None: + color = DEFAULT_PLOTLY_COLORS[0] + + plot_data, plot_xrange = FigureFactory._violinplot(data, fillcolor=color) + + layout = graph_objs.Layout( + title=title, + autosize=False, + font=graph_objs.Font(size=11), + height=450, + showlegend=False, + width=350, + xaxis=FigureFactory._make_XAxis('', plot_xrange), + yaxis=FigureFactory._make_YAxis(''), + hovermode='closest' + ) + layout['yaxis'].update(dict(showline=False, + showticklabels=False, + ticks='')) + + fig = graph_objs.Figure(data=graph_objs.Data(plot_data), + layout=layout) + return fig + + else: + pass + @staticmethod def _find_intermediate_color(lowcolor, highcolor, intermed): """ From b4eb690fa24df1629d0d50762ce8c87c3895f0d3 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 18 May 2016 14:55:11 -0400 Subject: [PATCH 02/27] Updated tools.py again --- plotly/tools.py | 57 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 711158f05fd..cfb60d46ee4 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1609,7 +1609,7 @@ def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): return plot_data, plot_xrange @staticmethod - def create_violin(data, color=None, colorscale=None, + def create_violin(data, colors=None, use_colorscale=False, title='Violin and Rug Plot'): """ Doc String Goes Here @@ -1633,12 +1633,60 @@ def create_violin(data, color=None, colorscale=None, 'Electric': ['rgb(0,0,0)', 'rgb(255,250,220)'], 'Viridis': ['rgb(68,1,84)', 'rgb(253,231,37)']} + if use_colorscale is False: + # accepts rgb-strings, hex-strings, plotly_colorscale names + # all in addition to lists containing colors + if isinstance(colors, str): + if colors in plotly_scales: + colors = plotly_scales[colors] + else: + if ('rgb' not in colors) and ('#' not in colors): + raise exceptions.PlotlyError("If you input a string " + "for 'colors', it must " + "either be a Plotly " + "colorscale, an 'rgb' " + "color, or a Hex color.") + colors_list = [] + colors_list.append(colors) + colors = colors_list + + if not isinstance(colors, list): + raise exceptions.PlotlyError("'colors' must be either a " + "string or a list of colors.") + if len(colors) <= 0: + raise exceptions.PlotlyError("Empty list of colors.") + + else: + # must receive either a plotly colorscale name or a list with + # at least 2 colors. Will use first 2 in list for colorscale + if not isinstance(colors, list): + raise exceptions.PlotlyError("'colors' needs to be either a " + "plotly colorscale string or a " + "list of at least two colors if " + "use_colorscale is True.") + if len(colors) < 2: + raise exceptions.PlotlyError("'colors' needs to be either a " + "plotly colorscale string or a " + "list of at least two colors if " + "use_colorscale is True.") + + # now validate data + if isinstance(data, pd.core.frame.DataFrame): + + + + + + + + if isinstance(data, list): if color is None: color = DEFAULT_PLOTLY_COLORS[0] - plot_data, plot_xrange = FigureFactory._violinplot(data, fillcolor=color) + plot_data, plot_xrange = FigureFactory._violinplot(data, + fillcolor=color) layout = graph_objs.Layout( title=title, @@ -1659,8 +1707,9 @@ def create_violin(data, color=None, colorscale=None, layout=layout) return fig - else: - pass + if isinstance(data, pd.core.frame.DataFrame): + if 'Group' not in data: + raise exceptions.PlotlyError("") @staticmethod def _find_intermediate_color(lowcolor, highcolor, intermed): From 253052791d3b4acfc4e92eacc399b7a4a1dfc040 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 18 May 2016 15:49:35 -0400 Subject: [PATCH 03/27] ... --- plotly/tools.py | 76 ++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index cfb60d46ee4..c6d84c99a8c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1632,6 +1632,8 @@ def create_violin(data, colors=None, use_colorscale=False, 'Earth': ['rgb(0,0,130)', 'rgb(255,255,255)'], 'Electric': ['rgb(0,0,0)', 'rgb(255,250,220)'], 'Viridis': ['rgb(68,1,84)', 'rgb(253,231,37)']} + if colors is None: + colors = DEFAULT_PLOTLY_COLORS if use_colorscale is False: # accepts rgb-strings, hex-strings, plotly_colorscale names @@ -1646,6 +1648,7 @@ def create_violin(data, colors=None, use_colorscale=False, "either be a Plotly " "colorscale, an 'rgb' " "color, or a Hex color.") + # put single-color 'colors' into a list colors_list = [] colors_list.append(colors) colors = colors_list @@ -1672,44 +1675,47 @@ def create_violin(data, colors=None, use_colorscale=False, # now validate data if isinstance(data, pd.core.frame.DataFrame): - + if len(data.columns) <= 1: + data = data['Score'].values.tolist() + plot_data, plot_xrange = FigureFactory._violinplot( + data, fillcolor=colors[0] + ) + layout = graph_objs.Layout( + title=title, + autosize=False, + font=graph_objs.Font(size=11), + height=450, + showlegend=False, + width=350, + xaxis=FigureFactory._make_XAxis('', plot_xrange), + yaxis=FigureFactory._make_YAxis(''), + hovermode='closest' + ) + layout['yaxis'].update(dict(showline=False, + showticklabels=False, + ticks='')) + fig = graph_objs.Figure(data=graph_objs.Data(plot_data), + layout=layout) + return fig - - - - - if isinstance(data, list): - - if color is None: - color = DEFAULT_PLOTLY_COLORS[0] - - plot_data, plot_xrange = FigureFactory._violinplot(data, - fillcolor=color) - - layout = graph_objs.Layout( - title=title, - autosize=False, - font=graph_objs.Font(size=11), - height=450, - showlegend=False, - width=350, - xaxis=FigureFactory._make_XAxis('', plot_xrange), - yaxis=FigureFactory._make_YAxis(''), - hovermode='closest' - ) - layout['yaxis'].update(dict(showline=False, - showticklabels=False, - ticks='')) - - fig = graph_objs.Figure(data=graph_objs.Data(plot_data), - layout=layout) - return fig - - if isinstance(data, pd.core.frame.DataFrame): - if 'Group' not in data: - raise exceptions.PlotlyError("") + else: + #import pandas as pd + # collect all group names + group_name = [] + for name in data['Group']: + if name not in group_name: + group_name.append(name) + + gb = data.groupby(['Group']) + L = len(group_name) + + fig = py.plotly.tools.make_subplots(rows=1, cols=L, + shared_yaxes=True, + horizontal_spacing=0.025, + print_grid=True) + @staticmethod def _find_intermediate_color(lowcolor, highcolor, intermed): From 85710a61a1ac82e92e3c79da12edd9652fa9b591 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 20 May 2016 08:40:23 -0400 Subject: [PATCH 04/27] Improved tools.py --- plotly/tools.py | 424 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 331 insertions(+), 93 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index c6d84c99a8c..bff1dcf46bd 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1452,6 +1452,9 @@ class FigureFactory(object): @staticmethod def _calc_stats(data): + """ + Calculate statistics for use in violin plot. + """ import numpy as np x = np.asarray(data, np.float) @@ -1470,10 +1473,16 @@ def _calc_stats(data): return vals_min, vals_max, q1, q2, q3, d1, d2 @staticmethod - def _make_half_violin(x, y, fillcolor='#1f77b4', linecolor='rgb(50,50,50)'): + def _make_half_violin(x, y, fillcolor='#1f77b4', linecolor='rgb(0,0,0)'): + """ + Produces a sideways probability distribution fig violin plot. + """ + + from plotly.graph_objs import graph_objs - text = ['(pdf(y), y)=(' + '{:0.2f}'.format(x[i]) + ', ' + '{:0.2f}'.format(y[i]) + ')' + text = ['(pdf(y), y)=(' + '{:0.2f}'.format(x[i]) + + ', ' + '{:0.2f}'.format(y[i]) + ')' for i in range(len(x))] return graph_objs.Scatter( x=x, @@ -1489,6 +1498,9 @@ def _make_half_violin(x, y, fillcolor='#1f77b4', linecolor='rgb(50,50,50)'): @staticmethod def _make_rugplot(vals, pdf_max, distance, color='#1f77b4'): + """ + Returns a rugplot fig for a violin plot. + """ from plotly.graph_objs import graph_objs return graph_objs.Scatter( @@ -1506,6 +1518,9 @@ def _make_rugplot(vals, pdf_max, distance, color='#1f77b4'): @staticmethod def _make_quartiles(q1, q3): + """ + Makes the upper and lower quartiles for a violin plot. + """ from plotly.graph_objs import graph_objs return graph_objs.Scatter( @@ -1523,6 +1538,9 @@ def _make_quartiles(q1, q3): @staticmethod def _make_median(q2): + """ + Formats the 'median' hovertext for a violin plot. + """ from plotly.graph_objs import graph_objs return graph_objs.Scatter( @@ -1537,6 +1555,10 @@ def _make_median(q2): @staticmethod def _make_non_outlier_interval(d1, d2): + """ + Returns the scatterplot fig of most of a violin plot. + + """ from plotly.graph_objs import graph_objs return graph_objs.Scatter( @@ -1550,6 +1572,10 @@ def _make_non_outlier_interval(d1, d2): @staticmethod def _make_XAxis(xaxis_title, xaxis_range): + """ + Makes the x-axis for a violin plot. + + """ from plotly.graph_objs import graph_objs xaxis = graph_objs.XAxis(title=xaxis_title, @@ -1565,6 +1591,10 @@ def _make_XAxis(xaxis_title, xaxis_range): @staticmethod def _make_YAxis(yaxis_title): + """ + Makes the y-axis for a violin plot. + + """ from plotly.graph_objs import graph_objs yaxis = graph_objs.YAxis(title=yaxis_title, @@ -1579,6 +1609,9 @@ def _make_YAxis(yaxis_title): @staticmethod def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): + """ + Refer to FigureFactory.create_violin() for docstring. + """ import numpy as np from scipy import stats @@ -1593,9 +1626,9 @@ def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): yy = pdf(xx) max_pdf = np.max(yy) # distance from the violin plot to rugplot - distance = 2.0*max_pdf/10 if rugplot else 0 + distance = (2.0 * max_pdf)/10 if rugplot else 0 # range for x values in the plot - plot_xrange = [-max_pdf-distance-0.1, max_pdf+0.1] + plot_xrange = [-max_pdf - distance - 0.1, max_pdf + 0.1] plot_data = [FigureFactory._make_half_violin(-yy, xx, fillcolor=fillcolor), FigureFactory._make_half_violin(yy, xx, fillcolor=fillcolor), FigureFactory._make_non_outlier_interval(d1, d2), @@ -1609,12 +1642,162 @@ def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): return plot_data, plot_xrange @staticmethod - def create_violin(data, colors=None, use_colorscale=False, + def _violin_no_colorscale(data, data_header, colors, use_colorscale, + group_header, height, width, title): + """ + Refer to FigureFactory.create_violin() for docstring. + + Returns fig for violin plot without colorscale. + + """ + + from plotly.graph_objs import graph_objs + import numpy as np + + # collect all group names + group_name = [] + for name in data[group_header]: + if name not in group_name: + group_name.append(name) + group_name.sort() + + gb = data.groupby([group_header]) + L = len(group_name) + + fig = make_subplots(rows=1, cols=L, + shared_yaxes=True, + horizontal_spacing=0.025, + print_grid=True) + color_index = 0 + for k, gr in enumerate(group_name): + vals = np.asarray(gb.get_group(gr)[data_header], np.float) + if color_index >= len(colors): + color_index = 0 + plot_data, plot_xrange = FigureFactory._violinplot( + vals, + fillcolor=colors[color_index] + ) + layout = graph_objs.Layout() + + for item in plot_data: + fig.append_trace(item, 1, k + 1) + color_index += 1 + # set the sharey axis style + fig['layout'].update( + {'yaxis{}'.format(1): FigureFactory._make_YAxis('')} + ) + fig['layout'].update( + title=title, + showlegend=False, + hovermode='closest', + autosize=False, + height=height, + width=width + ) + + return fig + + def _violin_colorscale(data, data_header, colors, use_colorscale, + group_header, height, width, title): + """ + Refer to FigureFactory.create_violin() for docstring. + + Returns fig for violin plot with colorscale. + + """ + + from plotly.graph_objs import graph_objs + import numpy as np + + # collect all group names + group_name = [] + for name in data[group_header]: + if name not in group_name: + group_name.append(name) + group_name.sort() + + gb = data.groupby([group_header]) + L = len(group_name) + + fig = make_subplots(rows=1, cols=L, + shared_yaxes=True, + horizontal_spacing=0.025, + print_grid=True) + for k, gr in enumerate(group_name): + vals = np.asarray(gb.get_group(gr)[data_header], np.float) + # find colorscale color + if '#' in colors[0]: + foo1 = FigureFactory._hex_to_rgb(colors[0]) + else: + foo1 = FigureFactory._unlabel_rgb(colors[0]) + if '#' in colors[1]: + foo2 = FigureFactory._hex_to_rgb(colors[1]) + else: + foo2 = FigureFactory._unlabel_rgb(colors[1]) + + foo_color = FigureFactory._find_intermediate_color( + foo1, foo2, k/float(L) + ) + intermed_color = 'rgb{}'.format(foo_color) + + plot_data, plot_xrange = FigureFactory._violinplot( + vals, + fillcolor=intermed_color + ) + layout = graph_objs.Layout() + + for item in plot_data: + fig.append_trace(item, 1, k + 1) + fig['layout'].update({'xaxis{}'.format(k + 1): + FigureFactory._make_XAxis(group_name[k], + plot_xrange)}) + # set the sharey axis style + fig['layout'].update( + {'yaxis{}'.format(1): FigureFactory._make_YAxis('')} + ) + fig['layout'].update( + title=title, + showlegend=False, + hovermode='closest', + autosize=False, + height=height, + width=width + ) + + return fig + + @staticmethod + def create_violin(data, data_header=None, colors=None, + use_colorscale=False, group_header=None, + height=450, width=600, title='Violin and Rug Plot'): """ - Doc String Goes Here + Returns figure for a violin plot + + :param (list|array) data: accepts either a list of numerical values, + a list of dictionaries all with identical keys and at least one + column of numeric values, or a pandas dataframe with at least one + column of numbers + :param (str) data_header: the header of the data column to be used + from an inputted pandas dataframe. Not applicable if 'data' is + a list of numeric values + :param (str|list) colors: either a rgb or hex color-string or a list + of color-strings + :param (bool) use_colorscale: will implement a colorscale based on the + first 2 color strings of 'colors' if a list. Only applicable if + grouping by another variable + :param (str) group_header: applicable if grouping data by a variable. + 'group_header' must be set to the name of the grouping variable. + :param (float) height: the height of the violin plot + :param (float) width: the width of the violin plot + :param (str) title: the title of the violin plot """ + # numpy import check + if _numpy_imported is False: + raise ImportError("FigureFactory.create_violin() requires " + "numpy to be imported.") from plotly.graph_objs import graph_objs + from numbers import Number 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)'], @@ -1632,90 +1815,119 @@ def create_violin(data, colors=None, use_colorscale=False, 'Earth': ['rgb(0,0,130)', 'rgb(255,255,255)'], 'Electric': ['rgb(0,0,0)', 'rgb(255,250,220)'], 'Viridis': ['rgb(68,1,84)', 'rgb(253,231,37)']} + + # validate colors if colors is None: colors = DEFAULT_PLOTLY_COLORS - if use_colorscale is False: - # accepts rgb-strings, hex-strings, plotly_colorscale names - # all in addition to lists containing colors - if isinstance(colors, str): - if colors in plotly_scales: - colors = plotly_scales[colors] - else: - if ('rgb' not in colors) and ('#' not in colors): - raise exceptions.PlotlyError("If you input a string " - "for 'colors', it must " - "either be a Plotly " - "colorscale, an 'rgb' " - "color, or a Hex color.") - # put single-color 'colors' into a list - colors_list = [] - colors_list.append(colors) - colors = colors_list - - if not isinstance(colors, list): - raise exceptions.PlotlyError("'colors' must be either a " - "string or a list of colors.") - if len(colors) <= 0: - raise exceptions.PlotlyError("Empty list of colors.") + if isinstance(colors, str): + if colors in plotly_scales: + colors = plotly_scales[colors] + else: + if ('rgb' not in colors) and ('#' not in colors): + raise exceptions.PlotlyError("If you input a string " + "for 'colors', it must " + "either be a Plotly " + "colorscale, an 'rgb' " + "color, or a hex color.") + colors_list = [] + colors_list.append(colors) + colors = colors_list + + if not isinstance(colors, list): + raise exceptions.PlotlyError("'colors' needs to be either a " + "plotly colorscale string or a " + "list of at least two colors if " + "use_colorscale is True.") + if len(colors) <= 0: + raise exceptions.PlotlyError("Empty list of colors.") + + if (not isinstance(data, list)) and data_header is None: + raise exceptions.PlotlyError("Please enter a string name for " + "data_header") + + # validate data and choose plot type + if group_header is None: + if isinstance(data, list): + if len(data) <= 0: + raise exceptions.PlotlyError("If data is a list, it must be " + "nonempty and contain either " + "numbers or dictionaries.") + + if isinstance(data[0], dict): + data_list = [] + for dictionary in data: + data_list.append(dictionary[data_header]) + data = data_list + + if not isinstance(data[0], Number): + raise exceptions.PlotlyError("If data is a list, it must " + "contain either numbers or " + "dictionaries.") + + if isinstance(data, pd.core.frame.DataFrame): + data = data[data_header].values.tolist() + + # call the plotting functions + plot_data, plot_xrange = FigureFactory._violinplot( + data, fillcolor=colors[0] + ) + layout = graph_objs.Layout( + title=title, + autosize=False, + font=graph_objs.Font(size=11), + height=height, + showlegend=False, + width=width, + xaxis=FigureFactory._make_XAxis('', plot_xrange), + yaxis=FigureFactory._make_YAxis(''), + hovermode='closest' + ) + layout['yaxis'].update(dict(showline=False, + showticklabels=False, + ticks='')) - else: - # must receive either a plotly colorscale name or a list with - # at least 2 colors. Will use first 2 in list for colorscale - if not isinstance(colors, list): - raise exceptions.PlotlyError("'colors' needs to be either a " - "plotly colorscale string or a " - "list of at least two colors if " - "use_colorscale is True.") - if len(colors) < 2: - raise exceptions.PlotlyError("'colors' needs to be either a " - "plotly colorscale string or a " - "list of at least two colors if " - "use_colorscale is True.") - - # now validate data - if isinstance(data, pd.core.frame.DataFrame): - if len(data.columns) <= 1: - data = data['Score'].values.tolist() - - plot_data, plot_xrange = FigureFactory._violinplot( - data, fillcolor=colors[0] - ) - layout = graph_objs.Layout( - title=title, - autosize=False, - font=graph_objs.Font(size=11), - height=450, - showlegend=False, - width=350, - xaxis=FigureFactory._make_XAxis('', plot_xrange), - yaxis=FigureFactory._make_YAxis(''), - hovermode='closest' - ) - layout['yaxis'].update(dict(showline=False, - showticklabels=False, - ticks='')) + fig = graph_objs.Figure(data=graph_objs.Data(plot_data), + layout=layout) + return fig - fig = graph_objs.Figure(data=graph_objs.Data(plot_data), - layout=layout) - return fig + else: + if isinstance(data, list): + if len(data) <= 0: + raise exceptions.PlotlyError("If data is a list and " + "group_by is not None, then " + "data must be either a list " + "of dictionaries or a data" + "frame.") + + if isinstance(data, pd.core.frame.DataFrame): + if use_colorscale is False: + fig = FigureFactory._violin_no_colorscale(data, + data_header, + colors, + use_colorscale, + group_header, + height, + width, + title) + return fig - else: - #import pandas as pd - # collect all group names - group_name = [] - for name in data['Group']: - if name not in group_name: - group_name.append(name) - - gb = data.groupby(['Group']) - L = len(group_name) - - fig = py.plotly.tools.make_subplots(rows=1, cols=L, - shared_yaxes=True, - horizontal_spacing=0.025, - print_grid=True) - + else: + # check if colors is a list of at least two colors + if len(colors) < 2: + raise exceptions.PlotlyError("If you are using color" + "scale, 'colors' must " + "be a list of at least " + "2 color-strings.") + fig = FigureFactory._violin_colorscale(data, + data_header, + colors, + use_colorscale, + group_header, + height, + width, + title) + return fig @staticmethod def _find_intermediate_color(lowcolor, highcolor, intermed): @@ -3166,16 +3378,17 @@ def _unlabel_rgb(colors): (a, b, c) """ - unlabelled_colors = [] - for character in colors: + KEY_INDICES = [',', '.'] + + if not isinstance(colors, list): str_vals = '' - for index in range(len(character)): + for index in range(len(colors)): try: - float(character[index]) - str_vals = str_vals + character[index] + float(colors[index]) + str_vals = str_vals + colors[index] except ValueError: - if (character[index] == ',') or (character[index] == '.'): - str_vals = str_vals + character[index] + if colors[index] in KEY_INDICES: + str_vals = str_vals + colors[index] str_vals = str_vals + ',' numbers = [] @@ -3187,9 +3400,34 @@ def _unlabel_rgb(colors): numbers.append(float(str_num)) str_num = '' unlabelled_tuple = (numbers[0], numbers[1], numbers[2]) - unlabelled_colors.append(unlabelled_tuple) - return unlabelled_colors + return unlabelled_tuple + + else: + unlabelled_colors = [] + for character in colors: + str_vals = '' + for index in range(len(character)): + try: + float(character[index]) + str_vals = str_vals + character[index] + except ValueError: + if character[index] in KEY_INDICES: + str_vals = str_vals + character[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 = '' + unlabelled_tuple = (numbers[0], numbers[1], numbers[2]) + unlabelled_colors.append(unlabelled_tuple) + + return unlabelled_colors @staticmethod def create_scatterplotmatrix(df, dataframe=None, headers=None, From 352d23368c02e4dddb96d370a857f7bba701de96 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 20 May 2016 09:11:06 -0400 Subject: [PATCH 05/27] First PR for Violin: colorscaling, visible colorscale, all color-types accepted --- plotly/tools.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index bff1dcf46bd..381161ed7c5 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1697,6 +1697,7 @@ def _violin_no_colorscale(data, data_header, colors, use_colorscale, return fig + @staticmethod def _violin_colorscale(data, data_header, colors, use_colorscale, group_header, height, width, title): """ @@ -1705,7 +1706,6 @@ def _violin_colorscale(data, data_header, colors, use_colorscale, Returns fig for violin plot with colorscale. """ - from plotly.graph_objs import graph_objs import numpy as np @@ -1751,6 +1751,22 @@ def _violin_colorscale(data, data_header, colors, use_colorscale, fig['layout'].update({'xaxis{}'.format(k + 1): FigureFactory._make_XAxis(group_name[k], plot_xrange)}) + # add colorbar to plot + trace_dummy = graph_objs.Scatter( + x=[24], + y=[10], + mode='markers', + marker=dict( + size=2, + cmin=0, + cmax=100, + colorscale=[[0, colors[0]], + [1, colors[1]]], + showscale=True), + showlegend=False, + ) + fig.append_trace(trace_dummy, 1, L) + # set the sharey axis style fig['layout'].update( {'yaxis{}'.format(1): FigureFactory._make_YAxis('')} @@ -1791,6 +1807,56 @@ def create_violin(data, data_header=None, colors=None, :param (float) height: the height of the violin plot :param (float) width: the width of the violin plot :param (str) title: the title of the violin plot + + Example 1: Single Violin Plot + ``` + import numpy as np + from scipy import stats + + import plotly.plotly as py + from plotly.tools import FigureFactory as FF + from plotly.graph_objs import graph_objs + + # create list of random values + data_list = np.random.randn(100) + data_list.tolist() + + # create violin fig + fig = FF.create_violin(data_list, colors='#1f77b4') + + # plot + py.iplot(fig, filename='Violin Plot') + ``` + + Example 2: Multiple Violin Plots with Colorscale + ``` + import numpy as np + import pandas as pd + from scipy import stats + + import plotly.plotly as py + from plotly.tools import FigureFactory as FF + from plotly.graph_objs import graph_objs + + # create dataframe + np.random.seed(619517) + Nr=250 + y = np.random.randn(Nr) + gr = np.random.choice(list("ABCDE"), Nr) + norm_params=[(0, 1.2), (0.7, 1), (-0.5, 1.4), (0.3, 1), (0.8, 0.9)] + + for i, letter in enumerate("ABCDE"): + y[gr == letter] *=norm_params[i][1]+ norm_params[i][0] + df = pd.DataFrame(dict(Score=y, Group=gr)) + + # create violin fig + fig = FF.create_violin(df, data_header='Score', group_header='Group', + use_colorscale=True, colors='Blues', + height=600, width=1000) + + # plot + py.iplot(fig, filename='Violin Plot with Colorscale') + ``` """ # numpy import check if _numpy_imported is False: From e29e7d3dcd401e331c34ee90b461c64680e2fc17 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Thu, 2 Jun 2016 13:53:58 -0400 Subject: [PATCH 06/27] Updated default-schema --- plotly/graph_reference/default-schema.json | 217 ++++++++++++++++++++- 1 file changed, 212 insertions(+), 5 deletions(-) diff --git a/plotly/graph_reference/default-schema.json b/plotly/graph_reference/default-schema.json index b3a0640193b..27cdf82abf6 100644 --- a/plotly/graph_reference/default-schema.json +++ b/plotly/graph_reference/default-schema.json @@ -712,7 +712,8 @@ "mollweide", "hammer", "transverse mercator", - "albers usa" + "albers usa", + "winkel tripel" ] } }, @@ -838,6 +839,114 @@ false ] }, + "images": { + "items": { + "image": { + "layer": { + "description": "Specifies whether images are drawn below or above traces. When `xref` and `yref` are both set to `paper`, image is drawn below the entire plot area.", + "dflt": "above", + "role": "info", + "valType": "enumerated", + "values": [ + "below", + "above" + ] + }, + "opacity": { + "description": "Sets the opacity of the image.", + "dflt": 1, + "max": 1, + "min": 0, + "role": "info", + "valType": "number" + }, + "role": "object", + "sizex": { + "description": "Sets the image container size horizontally. The image will be sized based on the `position` value. When `xref` is set to `paper`, units are sized relative to the plot width.", + "dflt": 0, + "role": "info", + "valType": "number" + }, + "sizey": { + "description": "Sets the image container size vertically. The image will be sized based on the `position` value. When `yref` is set to `paper`, units are sized relative to the plot height.", + "dflt": 0, + "role": "info", + "valType": "number" + }, + "sizing": { + "description": "Specifies which dimension of the image to constrain.", + "dflt": "contain", + "role": "info", + "valType": "enumerated", + "values": [ + "fill", + "contain", + "stretch" + ] + }, + "source": { + "description": "Specifies the URL of the image to be used. The URL must be accessible from the domain where the plot code is run, and can be either relative or absolute.", + "role": "info", + "valType": "string" + }, + "x": { + "description": "Sets the image's x position. When `xref` is set to `paper`, units are sized relative to the plot height. See `xref` for more info", + "dflt": 0, + "role": "info", + "valType": "number" + }, + "xanchor": { + "description": "Sets the anchor for the x position", + "dflt": "left", + "role": "info", + "valType": "enumerated", + "values": [ + "left", + "center", + "right" + ] + }, + "xref": { + "description": "Sets the images's x coordinate axis. If set to a x axis id (e.g. *x* or *x2*), the `x` position refers to an x data coordinate If set to *paper*, the `x` position refers to the distance from the left of plot in normalized coordinates where *0* (*1*) corresponds to the left (right).", + "dflt": "paper", + "role": "info", + "valType": "enumerated", + "values": [ + "paper", + "/^x([2-9]|[1-9][0-9]+)?$/" + ] + }, + "y": { + "description": "Sets the image's y position. When `yref` is set to `paper`, units are sized relative to the plot height. See `yref` for more info", + "dflt": 0, + "role": "info", + "valType": "number" + }, + "yanchor": { + "description": "Sets the anchor for the y position.", + "dflt": "top", + "role": "info", + "valType": "enumerated", + "values": [ + "top", + "middle", + "bottom" + ] + }, + "yref": { + "description": "Sets the images's y coordinate axis. If set to a y axis id (e.g. *y* or *y2*), the `y` position refers to a y data coordinate. If set to *paper*, the `y` position refers to the distance from the bottom of the plot in normalized coordinates where *0* (*1*) corresponds to the bottom (top).", + "dflt": "paper", + "role": "info", + "valType": "enumerated", + "values": [ + "paper", + "/^y([2-9]|[1-9][0-9]+)?$/" + ] + } + } + }, + "role": "object" + }, "legend": { "bgcolor": { "description": "Sets the legend background color.", @@ -877,6 +986,16 @@ "valType": "number" } }, + "orientation": { + "description": "Sets the orientation of the legend.", + "dflt": "v", + "role": "info", + "valType": "enumerated", + "values": [ + "v", + "h" + ] + }, "role": "object", "tracegroupgap": { "description": "Sets the amount of vertical space (in px) between legend groups.", @@ -6018,14 +6137,15 @@ "valType": "number" }, "barmode": { - "description": "Determines how bars at the same location coordinate are displayed on the graph. With *stack*, the bars are stacked on top of one another With *group*, the bars are plotted next to one another centered around the shared location. With *overlay*, the bars are plotted over one another, you might need to an *opacity* to see multiple bars.", + "description": "Determines how bars at the same location coordinate are displayed on the graph. With *stack*, the bars are stacked on top of one another With *relative*, the bars are stacked on top of one another, with negative values below the axis, positive values above With *group*, the bars are plotted next to one another centered around the shared location. With *overlay*, the bars are plotted over one another, you might need to an *opacity* to see multiple bars.", "dflt": "group", "role": "info", "valType": "enumerated", "values": [ "stack", "group", - "overlay" + "overlay", + "relative" ] }, "barnorm": { @@ -9298,14 +9418,15 @@ "valType": "number" }, "barmode": { - "description": "Determines how bars at the same location coordinate are displayed on the graph. With *stack*, the bars are stacked on top of one another With *group*, the bars are plotted next to one another centered around the shared location. With *overlay*, the bars are plotted over one another, you might need to an *opacity* to see multiple bars.", + "description": "Determines how bars at the same location coordinate are displayed on the graph. With *stack*, the bars are stacked on top of one another With *relative*, the bars are stacked on top of one another, with negative values below the axis, positive values above With *group*, the bars are plotted next to one another centered around the shared location. With *overlay*, the bars are plotted over one another, you might need to an *opacity* to see multiple bars.", "dflt": "group", "role": "info", "valType": "enumerated", "values": [ "stack", "group", - "overlay" + "overlay", + "relative" ] }, "barnorm": { @@ -11109,6 +11230,7 @@ }, "lighting": { "ambient": { + "description": "Ambient light increases overall color visibility but can wash out the image.", "dflt": 0.8, "max": 1, "min": 0, @@ -11116,13 +11238,23 @@ "valType": "number" }, "diffuse": { + "description": "Represents the extent that incident rays are reflected in a range of angles.", "dflt": 0.8, "max": 1, "min": 0, "role": "style", "valType": "number" }, + "facenormalsepsilon": { + "description": "Epsilon for face normals calculation avoids math issues arising from degenerate geometry.", + "dflt": 1e-06, + "max": 1, + "min": 0, + "role": "style", + "valType": "number" + }, "fresnel": { + "description": "Represents the reflectance as a dependency of the viewing angle; e.g. paper is reflective when viewing it from the edge of the paper (almost 90 degrees), causing shine.", "dflt": 0.2, "max": 5, "min": 0, @@ -11131,6 +11263,7 @@ }, "role": "object", "roughness": { + "description": "Alters specular reflection; the rougher the surface, the wider and less contrasty the shine.", "dflt": 0.5, "max": 1, "min": 0, @@ -11138,11 +11271,47 @@ "valType": "number" }, "specular": { + "description": "Represents the level that incident rays are reflected in a single direction, causing shine.", "dflt": 0.05, "max": 2, "min": 0, "role": "style", "valType": "number" + }, + "vertexnormalsepsilon": { + "description": "Epsilon for vertex normals calculation avoids math issues arising from degenerate geometry.", + "dflt": 1e-12, + "max": 1, + "min": 0, + "role": "style", + "valType": "number" + } + }, + "lightposition": { + "role": "object", + "x": { + "description": "Numeric vector, representing the X coordinate for each vertex.", + "dflt": 100000, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" + }, + "y": { + "description": "Numeric vector, representing the Y coordinate for each vertex.", + "dflt": 100000, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" + }, + "z": { + "description": "Numeric vector, representing the Z coordinate for each vertex.", + "dflt": 0, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" } }, "name": { @@ -15156,6 +15325,12 @@ }, "scattergl": { "attributes": { + "connectgaps": { + "description": "Determines whether or not gaps (i.e. {nan} or missing values) in the provided data arrays are connected.", + "dflt": false, + "role": "info", + "valType": "boolean" + }, "dx": { "description": "Sets the x coordinate step. See `x0` for more info.", "dflt": 1, @@ -17810,6 +17985,7 @@ }, "lighting": { "ambient": { + "description": "Ambient light increases overall color visibility but can wash out the image.", "dflt": 0.8, "max": 1, "min": 0, @@ -17817,6 +17993,7 @@ "valType": "number" }, "diffuse": { + "description": "Represents the extent that incident rays are reflected in a range of angles.", "dflt": 0.8, "max": 1, "min": 0, @@ -17824,6 +18001,7 @@ "valType": "number" }, "fresnel": { + "description": "Represents the reflectance as a dependency of the viewing angle; e.g. paper is reflective when viewing it from the edge of the paper (almost 90 degrees), causing shine.", "dflt": 0.2, "max": 5, "min": 0, @@ -17832,6 +18010,7 @@ }, "role": "object", "roughness": { + "description": "Alters specular reflection; the rougher the surface, the wider and less contrasty the shine.", "dflt": 0.5, "max": 1, "min": 0, @@ -17839,6 +18018,7 @@ "valType": "number" }, "specular": { + "description": "Represents the level that incident rays are reflected in a single direction, causing shine.", "dflt": 0.05, "max": 2, "min": 0, @@ -17846,6 +18026,33 @@ "valType": "number" } }, + "lightposition": { + "role": "object", + "x": { + "description": "Numeric vector, representing the X coordinate for each vertex.", + "dflt": 10, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" + }, + "y": { + "description": "Numeric vector, representing the Y coordinate for each vertex.", + "dflt": 100000, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" + }, + "z": { + "description": "Numeric vector, representing the Z coordinate for each vertex.", + "dflt": 0, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" + } + }, "name": { "description": "Sets the trace name. The trace name appear as the legend item and on hover.", "role": "info", From bde9a7c42ce6c90253329d74d78998c0b62a6478 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Thu, 2 Jun 2016 15:33:37 -0400 Subject: [PATCH 07/27] Updated color parsing functions --- plotly/tools.py | 79 ++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 381161ed7c5..ce5b40637b7 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -3379,26 +3379,31 @@ def _endpts_to_intervals(endpts): @staticmethod def _convert_to_RGB_255(colors): """ - Return a list of tuples where each element gets multiplied by 255 + Return a (list of) tuple(s) where each element is multiplied by 255 - Takes a list of color tuples where each element is between 0 and 1 - and returns the same list where each tuple element is normalized to be - between 0 and 255 + Takes a tuple or a list of tuples where each element of each tuple is + between 0 and 1. Returns the same tuple(s) where each tuple element is + multiplied by 255 """ - colors_255 = [] - for color in colors: - rgb_color = (color[0]*255.0, color[1]*255.0, color[2]*255.0) - colors_255.append(rgb_color) - return colors_255 + if isinstance(colors, tuple): + return (colors[0]*255.0, colors[1]*255.0, colors[2]*255.0) + + else: + colors_255 = [] + for color in colors: + rgb_color = (color[0]*255.0, color[1]*255.0, color[2]*255.0) + colors_255.append(rgb_color) + return colors_255 @staticmethod def _n_colors(lowcolor, highcolor, n_colors): """ - Splits a low and high color into a list of #n_colors 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]) @@ -3420,40 +3425,42 @@ def _n_colors(lowcolor, highcolor, n_colors): @staticmethod def _label_rgb(colors): """ - Takes colors (a, b, c) and returns tuples 'rgb(a, b, c)' + Takes tuple(s) (a, b, c) and returns rgb color(s) 'rgb(a, b, c)' - Takes a list of two color tuples of the form (a, b, c) and returns the - same list with each tuple replaced by a string 'rgb(a, b, c)' + Takes either a list or a single color tuple of the form (a, b, c) and + returns the same color(s) with each tuple replaced by a string + 'rgb(a, b, c)' """ - colors_label = [] - for color in colors: - color_label = 'rgb{}'.format(color) - colors_label.append(color_label) + if isinstance(colors, tuple): + return ('rgb(%s, %s, %s)' % (colors[0], colors[1], colors[2])) + else: + colors_label = [] + for color in colors: + color_label = ('rgb(%s, %s, %s)' % (color[0], + color[1], + color[2])) + colors_label.append(color_label) - return colors_label + return colors_label @staticmethod def _unlabel_rgb(colors): """ - Takes rgb colors 'rgb(a, b, c)' and returns the tuples (a, b, c) + Takes rgb color(s) 'rgb(a, b, c)' and returns tuple(s) (a, b, c) - This function takes a list of two 'rgb(a, b, c)' color strings and - returns a list of the color tuples in tuple form without the 'rgb' - label. In particular, the output is a list of two tuples of the form - (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) """ - KEY_INDICES = [',', '.'] - - if not isinstance(colors, list): + if isinstance(colors, str): str_vals = '' for index in range(len(colors)): try: float(colors[index]) str_vals = str_vals + colors[index] except ValueError: - if colors[index] in KEY_INDICES: + if (colors[index] == ',') or (colors[index] == '.'): str_vals = str_vals + colors[index] str_vals = str_vals + ',' @@ -3465,21 +3472,19 @@ def _unlabel_rgb(colors): else: numbers.append(float(str_num)) str_num = '' - unlabelled_tuple = (numbers[0], numbers[1], numbers[2]) + return (numbers[0], numbers[1], numbers[2]) - return unlabelled_tuple - - else: + if isinstance(colors, list): unlabelled_colors = [] - for character in colors: + for color in colors: str_vals = '' - for index in range(len(character)): + for index in range(len(color)): try: - float(character[index]) - str_vals = str_vals + character[index] + float(color[index]) + str_vals = str_vals + color[index] except ValueError: - if character[index] in KEY_INDICES: - str_vals = str_vals + character[index] + if (color[index] == ',') or (color[index] == '.'): + str_vals = str_vals + color[index] str_vals = str_vals + ',' numbers = [] From 4257ed66e1ba3ee23d57ed8f8d69345ef479e3ff Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Thu, 2 Jun 2016 16:31:25 -0400 Subject: [PATCH 08/27] Added changes --- plotly/tools.py | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index ce5b40637b7..82d8077ecb9 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1785,8 +1785,7 @@ def _violin_colorscale(data, data_header, colors, use_colorscale, @staticmethod def create_violin(data, data_header=None, colors=None, use_colorscale=False, group_header=None, - height=450, width=600, - title='Violin and Rug Plot'): + height=450, width=600, title='Violin and Rug Plot'): """ Returns figure for a violin plot @@ -2009,31 +2008,39 @@ def _find_intermediate_color(lowcolor, highcolor, intermed): diff_1 = float(highcolor[1] - lowcolor[1]) diff_2 = float(highcolor[2] - lowcolor[2]) - new_tuple = (lowcolor[0] + intermed*diff_0, - lowcolor[1] + intermed*diff_1, - lowcolor[2] + intermed*diff_2) - - return new_tuple + inter_colors = (lowcolor[0] + intermed * diff_0, + lowcolor[1] + intermed * diff_1, + lowcolor[2] + intermed * diff_2) + return inter_colors @staticmethod def _unconvert_from_RGB_255(colors): """ Return a tuple where each element gets divided by 255 - Takes a list of color tuples where each element is between 0 and 255 - and returns the same list where each tuple element is normalized to be - between 0 and 1 + 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 """ - un_rgb_colors = [] - for color in colors: - un_rgb_color = (color[0]/(255.0), - color[1]/(255.0), - color[2]/(255.0)) + if isinstance(colors, tuple): + + un_rgb_color = (colors[0]/(255.0), + colors[1]/(255.0), + colors[2]/(255.0)) + + return un_rgb_color + + if isinstance(colors, list): + un_rgb_colors = [] + for color in colors: + un_rgb_color = (color[0]/(255.0), + color[1]/(255.0), + color[2]/(255.0)) - un_rgb_colors.append(un_rgb_color) + un_rgb_colors.append(un_rgb_color) - return un_rgb_colors + return un_rgb_colors @staticmethod def _map_z2color(zval, colormap, vmin, vmax): From b0ff4d2020da10332b7a1b17aa3a58489bc8ab97 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 3 Jun 2016 13:52:07 -0400 Subject: [PATCH 09/27] ... --- plotly/tools.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 82d8077ecb9..e2037c2661c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1782,6 +1782,62 @@ def _violin_colorscale(data, data_header, colors, use_colorscale, return fig + @staticmethod + def _violin_dict(data, data_header, colors, use_colorscale, + group_header, height, width, title): + """ + Refer to FigureFactory.create_violin() for docstring. + + Returns fig for violin plot without colorscale. + + """ + + from plotly.graph_objs import graph_objs + import numpy as np + + # collect all group names + group_name = [] + for name in data[group_header]: + if name not in group_name: + group_name.append(name) + group_name.sort() + + gb = data.groupby([group_header]) + L = len(group_name) + + fig = make_subplots(rows=1, cols=L, + shared_yaxes=True, + horizontal_spacing=0.025, + print_grid=True) + color_index = 0 + for k, gr in enumerate(group_name): + vals = np.asarray(gb.get_group(gr)[data_header], np.float) + if color_index >= len(colors): + color_index = 0 + plot_data, plot_xrange = FigureFactory._violinplot( + vals, + fillcolor=colors[color_index] + ) + layout = graph_objs.Layout() + + for item in plot_data: + fig.append_trace(item, 1, k + 1) + color_index += 1 + # set the sharey axis style + fig['layout'].update( + {'yaxis{}'.format(1): FigureFactory._make_YAxis('')} + ) + fig['layout'].update( + title=title, + showlegend=False, + hovermode='closest', + autosize=False, + height=height, + width=width + ) + + return fig + @staticmethod def create_violin(data, data_header=None, colors=None, use_colorscale=False, group_header=None, From 06d685d0e918654d8eac48b33787bb787bd94b9c Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 3 Jun 2016 16:23:26 -0400 Subject: [PATCH 10/27] added more things --- plotly/tools.py | 202 +++++++++++++++++++++++++++++++----------------- 1 file changed, 130 insertions(+), 72 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index e2037c2661c..833b1fc6b08 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1477,8 +1477,6 @@ def _make_half_violin(x, y, fillcolor='#1f77b4', linecolor='rgb(0,0,0)'): """ Produces a sideways probability distribution fig violin plot. """ - - from plotly.graph_objs import graph_objs text = ['(pdf(y), y)=(' + '{:0.2f}'.format(x[i]) + @@ -1650,7 +1648,6 @@ def _violin_no_colorscale(data, data_header, colors, use_colorscale, Returns fig for violin plot without colorscale. """ - from plotly.graph_objs import graph_objs import numpy as np @@ -1791,7 +1788,6 @@ def _violin_dict(data, data_header, colors, use_colorscale, Returns fig for violin plot without colorscale. """ - from plotly.graph_objs import graph_objs import numpy as np @@ -1809,20 +1805,17 @@ def _violin_dict(data, data_header, colors, use_colorscale, shared_yaxes=True, horizontal_spacing=0.025, print_grid=True) - color_index = 0 + for k, gr in enumerate(group_name): vals = np.asarray(gb.get_group(gr)[data_header], np.float) - if color_index >= len(colors): - color_index = 0 plot_data, plot_xrange = FigureFactory._violinplot( vals, - fillcolor=colors[color_index] + fillcolor=colors[gr] ) layout = graph_objs.Layout() for item in plot_data: fig.append_trace(item, 1, k + 1) - color_index += 1 # set the sharey axis style fig['layout'].update( {'yaxis{}'.format(1): FigureFactory._make_YAxis('')} @@ -1852,8 +1845,7 @@ def create_violin(data, data_header=None, colors=None, :param (str) data_header: the header of the data column to be used from an inputted pandas dataframe. Not applicable if 'data' is a list of numeric values - :param (str|list) colors: either a rgb or hex color-string or a list - of color-strings + :param (str|list|dict) colors: **import proper doc string description** :param (bool) use_colorscale: will implement a colorscale based on the first 2 color strings of 'colors' if a list. Only applicable if grouping by another variable @@ -1937,35 +1929,106 @@ def create_violin(data, data_header=None, colors=None, 'Electric': ['rgb(0,0,0)', 'rgb(255,250,220)'], 'Viridis': ['rgb(68,1,84)', 'rgb(253,231,37)']} - # validate colors + # Validate colors if colors is None: colors = DEFAULT_PLOTLY_COLORS if isinstance(colors, str): if colors in plotly_scales: colors = plotly_scales[colors] - else: - if ('rgb' not in colors) and ('#' not in colors): - raise exceptions.PlotlyError("If you input a string " - "for 'colors', it must " - "either be a Plotly " - "colorscale, an 'rgb' " - "color, or a hex color.") + + elif 'rgb' in colors: + # validate rgb color + colors = FigureFactory._unlabel_rgb(colors) + for value in colors: + if value > 255.0: + raise exceptions.PlotlyError("Whoops! The " + "elements in your " + "rgb colors " + "tuples cannot " + "exceed 255.0.") + colors = FigureFactory._label_rgb(colors) + + # put colors in list colors_list = [] colors_list.append(colors) colors = colors_list - if not isinstance(colors, list): - raise exceptions.PlotlyError("'colors' needs to be either a " - "plotly colorscale string or a " - "list of at least two colors if " - "use_colorscale is True.") - if len(colors) <= 0: - raise exceptions.PlotlyError("Empty list of colors.") + elif '#' in colors: + colors = FigureFactory._hex_to_rgb(colors) + colors = FigureFactory._label_rgb(colors) - if (not isinstance(data, list)) and data_header is None: - raise exceptions.PlotlyError("Please enter a string name for " - "data_header") + # put colors in list + colors_list = [] + colors_list.append(colors) + colors = colors_list + + else: + scale_keys = list(plotly_scales.keys()) + raise exceptions.PlotlyError("If you input a string " + "for 'colors', it must " + "either be a Plotly " + "colorscale, an 'rgb' " + "color or a hex color." + "Valid plotly colorscale " + "names are {}".format(scale_keys)) + elif isinstance(colors, tuple): + # validate tuple color + for value in colors: + if value > 1.0: + raise exceptions.PlotlyError("Whoops! The " + "elements in " + "your colors " + "tuples cannot " + "exceed 1.0.") + + colors = FigureFactory._convert_to_RGB_255(colors) + colors = FigureFactory._label_rgb(colors) + + colors_list = [] + colors_list.append(colors) + colors = colors_list + + elif isinstance(colors, list): + new_colormap = [] + for color in colors: + if 'rgb' in color: + color = FigureFactory._unlabel_rgb(color) + for value in color: + if value > 255.0: + raise exceptions.PlotlyError("Whoops! The " + "elements in your " + "rgb colors " + "tuples cannot " + "exceed 255.0.") + + color = FigureFactory._label_rgb(color) + new_colormap.append(color) + elif '#' in color: + color = FigureFactory._hex_to_rgb(color) + color = FigureFactory._label_rgb(color) + new_colormap.append(color) + elif isinstance(color, tuple): + for value in color: + if value > 1.0: + raise exceptions.PlotlyError("Whoops! The " + "elements in " + "your colors " + "tuples cannot " + "exceed 1.0.") + new_colormap.append(color) + colors = new_colormap + + elif isinstance(colors, dict): + pass + # validate dict + + else: + raise exceptions.PlotlyError("You must input a valid colors " + "choice. Valid types include a " + "plotly scale, rgb, hex or tuple " + "color, a list of any color types " + "or a dictionary.") # validate data and choose plot type if group_header is None: @@ -1975,17 +2038,10 @@ def create_violin(data, data_header=None, colors=None, "nonempty and contain either " "numbers or dictionaries.") - if isinstance(data[0], dict): - data_list = [] - for dictionary in data: - data_list.append(dictionary[data_header]) - data = data_list - if not isinstance(data[0], Number): raise exceptions.PlotlyError("If data is a list, it must " - "contain either numbers or " - "dictionaries.") - + "contain only numbers.") + # fix up for test: add 'if pandas_imported', etc if isinstance(data, pd.core.frame.DataFrame): data = data[data_header].values.tolist() @@ -2013,42 +2069,44 @@ def create_violin(data, data_header=None, colors=None, return fig else: - if isinstance(data, list): - if len(data) <= 0: - raise exceptions.PlotlyError("If data is a list and " - "group_by is not None, then " - "data must be either a list " - "of dictionaries or a data" - "frame.") + if not isinstance(data, pd.core.frame.DataFrame): + raise exceptions.PlotlyError("Error.") - if isinstance(data, pd.core.frame.DataFrame): - if use_colorscale is False: - fig = FigureFactory._violin_no_colorscale(data, - data_header, - colors, - use_colorscale, - group_header, - height, - width, - title) - return fig + if isinstance(colors, dict): + # validate colors dict choice below - else: - # check if colors is a list of at least two colors - if len(colors) < 2: - raise exceptions.PlotlyError("If you are using color" - "scale, 'colors' must " - "be a list of at least " - "2 color-strings.") - fig = FigureFactory._violin_colorscale(data, - data_header, - colors, - use_colorscale, - group_header, - height, - width, - title) - return fig + fig = FigureFactory._violin_dict(data, data_header, colors, + use_colorscale, group_header, + height, width, title) + return fig + + if use_colorscale is False: + fig = FigureFactory._violin_no_colorscale(data, + data_header, + colors, + use_colorscale, + group_header, + height, + width, + title) + return fig + + else: + # check if colors is a list of at least two colors + if len(colors) < 2: + raise exceptions.PlotlyError("If you are using color" + "scale, 'colors' must " + "be a list of at least " + "2 color-strings.") + fig = FigureFactory._violin_colorscale(data, + data_header, + colors, + use_colorscale, + group_header, + height, + width, + title) + return fig @staticmethod def _find_intermediate_color(lowcolor, highcolor, intermed): From 13b5ba7a4739efc814d031b63f89d77ac77f9b40 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Sun, 5 Jun 2016 14:30:35 -0400 Subject: [PATCH 11/27] more --- plotly/tools.py | 92 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 26 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 833b1fc6b08..2586238be5e 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1798,6 +1798,11 @@ def _violin_dict(data, data_header, colors, use_colorscale, group_name.append(name) group_name.sort() + # check if all group names appear in colors dict + for group in group_name: + if group not in colors: + raise exceptions.PlotlyError("A") + gb = data.groupby([group_header]) L = len(group_name) @@ -1845,7 +1850,13 @@ def create_violin(data, data_header=None, colors=None, :param (str) data_header: the header of the data column to be used from an inputted pandas dataframe. Not applicable if 'data' is a list of numeric values - :param (str|list|dict) colors: **import proper doc string description** + :param (str|tuple|list|dict) colors: either a plotly scale name, + an rgb or hex color, a color tuple, a list of colors or a + dictionary. An rgb color is of the form 'rgb(x, y, z)' where + x, y and z belong to the interval [0, 255] and a color tuple is a + tuple of the form (a, b, c) where a, b and c belong to [0, 1]. + If colors is a list, it must contain valid color types as its + members. :param (bool) use_colorscale: will implement a colorscale based on the first 2 color strings of 'colors' if a list. Only applicable if grouping by another variable @@ -1905,7 +1916,6 @@ def create_violin(data, data_header=None, colors=None, py.iplot(fig, filename='Violin Plot with Colorscale') ``` """ - # numpy import check if _numpy_imported is False: raise ImportError("FigureFactory.create_violin() requires " "numpy to be imported.") @@ -2016,13 +2026,41 @@ def create_violin(data, data_header=None, colors=None, "your colors " "tuples cannot " "exceed 1.0.") + + color = FigureFactory._convert_to_RGB_255(color) + color = FigureFactory._label_rgb(color) new_colormap.append(color) colors = new_colormap elif isinstance(colors, dict): - pass - # validate dict + for name in colors: + if 'rgb' in colors[name]: + color = FigureFactory._unlabel_rgb(colors[name]) + for value in color: + if value > 255.0: + raise exceptions.PlotlyError("Whoops! The " + "elements in your " + "rgb color " + "tuples cannot " + "exceed 255.0.") + + elif '#' in colors[name]: + color = FigureFactory._hex_to_rgb(colors[name]) + color = FigureFactory._label_rgb(color) + colors[name] = color + elif isinstance(colors[name], tuple): + for value in colors[name]: + if value > 1.0: + raise exceptions.PlotlyError("Whoops! The " + "elements in " + "your color " + "tuples cannot " + "exceed 1.0.") + + color = FigureFactory._convert_to_RGB_255(colors[name]) + color = FigureFactory._label_rgb(color) + colors[name] = color else: raise exceptions.PlotlyError("You must input a valid colors " "choice. Valid types include a " @@ -2070,34 +2108,36 @@ def create_violin(data, data_header=None, colors=None, else: if not isinstance(data, pd.core.frame.DataFrame): - raise exceptions.PlotlyError("Error.") + raise exceptions.PlotlyError("Error. You must use a pandas " + "DataFrame if you are using a " + "group header.") - if isinstance(colors, dict): - # validate colors dict choice below + if use_colorscale is False: - fig = FigureFactory._violin_dict(data, data_header, colors, - use_colorscale, group_header, - height, width, title) - return fig + if isinstance(colors, dict): + # validate colors dict choice below + fig = FigureFactory._violin_dict(data, data_header, + colors, use_colorscale, + group_header, height, + width, title) + return fig - if use_colorscale is False: - fig = FigureFactory._violin_no_colorscale(data, - data_header, - colors, - use_colorscale, - group_header, - height, - width, - title) + else: + fig = FigureFactory._violin_no_colorscale(data, + data_header, + colors, + use_colorscale, + group_header, + height, width, + title) return fig else: - # check if colors is a list of at least two colors - if len(colors) < 2: - raise exceptions.PlotlyError("If you are using color" - "scale, 'colors' must " - "be a list of at least " - "2 color-strings.") + if isinstance(colors, dict): + raise exceptions.PlotlyError("You cannot use a " + "dictionary if you are " + "using a colorscale.") + fig = FigureFactory._violin_colorscale(data, data_header, colors, From a69c4bd31e3d47e96d3e810ea9c0b2145ae42586 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Sun, 5 Jun 2016 16:19:33 -0400 Subject: [PATCH 12/27] Violin with qualitative colors//updated tests --- .../test_tools/test_figure_factory.py | 84 +++++++++++++++++++ .../test_optional/test_figure_factory.py | 21 +++++ plotly/tools.py | 39 ++++----- 3 files changed, 121 insertions(+), 23 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_figure_factory.py b/plotly/tests/test_core/test_tools/test_figure_factory.py index c79bde4eb2a..7c3d2d22fdb 100644 --- a/plotly/tests/test_core/test_tools/test_figure_factory.py +++ b/plotly/tests/test_core/test_tools/test_figure_factory.py @@ -1126,6 +1126,90 @@ def test_table_with_index(self): self.assertEqual(index_table, exp_index_table) +class TestViolin(TestCase): + + def test_colors_validation(self): + + # check: colors is in an acceptable form + data = [1, 5, 8] + + pattern = ("Whoops! The elements in your rgb colors tuples cannot " + "exceed 255.0.") + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, + data, colors='rgb(300, 2, 3)') + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, + data, colors=['rgb(300, 2, 3)']) + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, + data, colors={'apple': 'rgb(300, 2, 3)'}) + + pattern2 = ("Whoops! The elements in your colors tuples cannot " + "exceed 1.0.") + + self.assertRaisesRegexp(PlotlyError, pattern2, + tls.FigureFactory.create_violin, + data, colors=(1.1, 1, 1)) + + self.assertRaisesRegexp(PlotlyError, pattern2, + tls.FigureFactory.create_violin, + data, colors=[(1.1, 1, 1)]) + + self.assertRaisesRegexp(PlotlyError, pattern2, + tls.FigureFactory.create_violin, + data, colors={'apple': (1.1, 1, 1)}) + + # check: if valid string color is inputted + self.assertRaises(PlotlyError, tls.FigureFactory.create_violin, + data, colors='foo') + + # error message if colors is no good + pattern3 = ("You must input a valid colors choice. Valid types " + "include a plotly scale, rgb, hex or tuple color, a list " + "of any color types or a dictionary.") + + self.assertRaisesRegexp(PlotlyError, pattern3, + tls.FigureFactory.create_violin, + data, colors=5) + + def test_data_as_list(self): + + # check: data is a non empty list of numbers + + data = [] + + pattern = ("If data is a list, it must be nonempty and contain " + "either numbers or dictionaries.") + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, + data) + + data = [1, 'foo'] + + pattern2 = ("If data is a list, it must contain only numbers.") + + self.assertRaisesRegexp(PlotlyError, pattern2, + tls.FigureFactory.create_violin, data) + + def test_dataframe_input(self): + + # check: dataframe is entered if group_header is True + + data = [1, 2, 3] + + pattern = ("Error. You must use a pandas DataFrame if you are using " + "a group header.") + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, data, + group_header=True) + + # class TestDistplot(TestCase): # def test_scipy_import_error(self): diff --git a/plotly/tests/test_optional/test_figure_factory.py b/plotly/tests/test_optional/test_figure_factory.py index f39c5c8f04e..256eb39456e 100644 --- a/plotly/tests/test_optional/test_figure_factory.py +++ b/plotly/tests/test_optional/test_figure_factory.py @@ -1012,3 +1012,24 @@ def test_scatter_plot_matrix_kwargs(self): self.assert_dict_equal(test_scatter_plot_matrix['layout'], exp_scatter_plot_matrix['layout']) + + +class TestViolin(NumpyTestUtilsMixin, TestCase): + + def test_colors_dict(self): + + # check: if colorscale is True, make sure colors is not a dictionary + + data = pd.DataFrame([['apple', 2], ['pear', 4]], + columns=['a', 'b']) + + pattern = ("You cannot use a dictionary if you are using a " + "colorscale.") + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, data, + group_header='a', use_colorscale=True, + colors={'a': 'rgb(1, 2, 3)'}) + + + diff --git a/plotly/tools.py b/plotly/tools.py index 2586238be5e..a93efc7b590 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1720,26 +1720,19 @@ def _violin_colorscale(data, data_header, colors, use_colorscale, shared_yaxes=True, horizontal_spacing=0.025, print_grid=True) + for k, gr in enumerate(group_name): vals = np.asarray(gb.get_group(gr)[data_header], np.float) # find colorscale color - if '#' in colors[0]: - foo1 = FigureFactory._hex_to_rgb(colors[0]) - else: - foo1 = FigureFactory._unlabel_rgb(colors[0]) - if '#' in colors[1]: - foo2 = FigureFactory._hex_to_rgb(colors[1]) - else: - foo2 = FigureFactory._unlabel_rgb(colors[1]) + lowcolor = FigureFactory._unlabel_rgb(colors[0]) + highcolor = FigureFactory._unlabel_rgb(colors[1]) - foo_color = FigureFactory._find_intermediate_color( - foo1, foo2, k/float(L) + intermed_color = FigureFactory._find_intermediate_color( + lowcolor, highcolor, k/float(L) ) - intermed_color = 'rgb{}'.format(foo_color) - plot_data, plot_xrange = FigureFactory._violinplot( vals, - fillcolor=intermed_color + fillcolor='rgb{}'.format(intermed_color) ) layout = graph_objs.Layout() @@ -1919,6 +1912,11 @@ def create_violin(data, data_header=None, colors=None, if _numpy_imported is False: raise ImportError("FigureFactory.create_violin() requires " "numpy to be imported.") + + if _scipy_imported is False: + raise ImportError("FigureFactory.create_violin() requires " + "scipy to be imported.") + from plotly.graph_objs import graph_objs from numbers import Number plotly_scales = {'Greys': ['rgb(0,0,0)', 'rgb(255,255,255)'], @@ -2040,7 +2038,7 @@ def create_violin(data, data_header=None, colors=None, if value > 255.0: raise exceptions.PlotlyError("Whoops! The " "elements in your " - "rgb color " + "rgb colors " "tuples cannot " "exceed 255.0.") @@ -2054,7 +2052,7 @@ def create_violin(data, data_header=None, colors=None, if value > 1.0: raise exceptions.PlotlyError("Whoops! The " "elements in " - "your color " + "your colors " "tuples cannot " "exceed 1.0.") @@ -2076,11 +2074,11 @@ def create_violin(data, data_header=None, colors=None, "nonempty and contain either " "numbers or dictionaries.") - if not isinstance(data[0], Number): + if not all(isinstance(element, Number) for element in data): raise exceptions.PlotlyError("If data is a list, it must " "contain only numbers.") - # fix up for test: add 'if pandas_imported', etc - if isinstance(data, pd.core.frame.DataFrame): + + if _pandas_imported and isinstance(data, pd.core.frame.DataFrame): data = data[data_header].values.tolist() # call the plotting functions @@ -2111,9 +2109,7 @@ def create_violin(data, data_header=None, colors=None, raise exceptions.PlotlyError("Error. You must use a pandas " "DataFrame if you are using a " "group header.") - if use_colorscale is False: - if isinstance(colors, dict): # validate colors dict choice below fig = FigureFactory._violin_dict(data, data_header, @@ -2121,7 +2117,6 @@ def create_violin(data, data_header=None, colors=None, group_header, height, width, title) return fig - else: fig = FigureFactory._violin_no_colorscale(data, data_header, @@ -2131,13 +2126,11 @@ def create_violin(data, data_header=None, colors=None, height, width, title) return fig - else: if isinstance(colors, dict): raise exceptions.PlotlyError("You cannot use a " "dictionary if you are " "using a colorscale.") - fig = FigureFactory._violin_colorscale(data, data_header, colors, From a6ce8e3ff9610e3ed28edd29bab4af2e1ff22e5a Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Sun, 5 Jun 2016 19:46:50 -0400 Subject: [PATCH 13/27] re-did tests --- .../test_tools/test_figure_factory.py | 85 --- .../test_optional/test_figure_factory.py | 509 +++++++++++++++++- 2 files changed, 507 insertions(+), 87 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_figure_factory.py b/plotly/tests/test_core/test_tools/test_figure_factory.py index 7c3d2d22fdb..2c5c0eda9f7 100644 --- a/plotly/tests/test_core/test_tools/test_figure_factory.py +++ b/plotly/tests/test_core/test_tools/test_figure_factory.py @@ -1125,91 +1125,6 @@ def test_table_with_index(self): 'zeroline': False}}} self.assertEqual(index_table, exp_index_table) - -class TestViolin(TestCase): - - def test_colors_validation(self): - - # check: colors is in an acceptable form - data = [1, 5, 8] - - pattern = ("Whoops! The elements in your rgb colors tuples cannot " - "exceed 255.0.") - - self.assertRaisesRegexp(PlotlyError, pattern, - tls.FigureFactory.create_violin, - data, colors='rgb(300, 2, 3)') - - self.assertRaisesRegexp(PlotlyError, pattern, - tls.FigureFactory.create_violin, - data, colors=['rgb(300, 2, 3)']) - - self.assertRaisesRegexp(PlotlyError, pattern, - tls.FigureFactory.create_violin, - data, colors={'apple': 'rgb(300, 2, 3)'}) - - pattern2 = ("Whoops! The elements in your colors tuples cannot " - "exceed 1.0.") - - self.assertRaisesRegexp(PlotlyError, pattern2, - tls.FigureFactory.create_violin, - data, colors=(1.1, 1, 1)) - - self.assertRaisesRegexp(PlotlyError, pattern2, - tls.FigureFactory.create_violin, - data, colors=[(1.1, 1, 1)]) - - self.assertRaisesRegexp(PlotlyError, pattern2, - tls.FigureFactory.create_violin, - data, colors={'apple': (1.1, 1, 1)}) - - # check: if valid string color is inputted - self.assertRaises(PlotlyError, tls.FigureFactory.create_violin, - data, colors='foo') - - # error message if colors is no good - pattern3 = ("You must input a valid colors choice. Valid types " - "include a plotly scale, rgb, hex or tuple color, a list " - "of any color types or a dictionary.") - - self.assertRaisesRegexp(PlotlyError, pattern3, - tls.FigureFactory.create_violin, - data, colors=5) - - def test_data_as_list(self): - - # check: data is a non empty list of numbers - - data = [] - - pattern = ("If data is a list, it must be nonempty and contain " - "either numbers or dictionaries.") - - self.assertRaisesRegexp(PlotlyError, pattern, - tls.FigureFactory.create_violin, - data) - - data = [1, 'foo'] - - pattern2 = ("If data is a list, it must contain only numbers.") - - self.assertRaisesRegexp(PlotlyError, pattern2, - tls.FigureFactory.create_violin, data) - - def test_dataframe_input(self): - - # check: dataframe is entered if group_header is True - - data = [1, 2, 3] - - pattern = ("Error. You must use a pandas DataFrame if you are using " - "a group header.") - - self.assertRaisesRegexp(PlotlyError, pattern, - tls.FigureFactory.create_violin, data, - group_header=True) - - # class TestDistplot(TestCase): # def test_scipy_import_error(self): diff --git a/plotly/tests/test_optional/test_figure_factory.py b/plotly/tests/test_optional/test_figure_factory.py index 256eb39456e..b77380e0209 100644 --- a/plotly/tests/test_optional/test_figure_factory.py +++ b/plotly/tests/test_optional/test_figure_factory.py @@ -1016,6 +1016,87 @@ def test_scatter_plot_matrix_kwargs(self): class TestViolin(NumpyTestUtilsMixin, TestCase): + def test_colors_validation(self): + + # check: colors is in an acceptable form + data = [1, 5, 8] + + pattern = ("Whoops! The elements in your rgb colors tuples cannot " + "exceed 255.0.") + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, + data, colors='rgb(300, 2, 3)') + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, + data, colors=['rgb(300, 2, 3)']) + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, + data, colors={'apple': 'rgb(300, 2, 3)'}) + + pattern2 = ("Whoops! The elements in your colors tuples cannot " + "exceed 1.0.") + + self.assertRaisesRegexp(PlotlyError, pattern2, + tls.FigureFactory.create_violin, + data, colors=(1.1, 1, 1)) + + self.assertRaisesRegexp(PlotlyError, pattern2, + tls.FigureFactory.create_violin, + data, colors=[(1.1, 1, 1)]) + + self.assertRaisesRegexp(PlotlyError, pattern2, + tls.FigureFactory.create_violin, + data, colors={'apple': (1.1, 1, 1)}) + + # check: if valid string color is inputted + self.assertRaises(PlotlyError, tls.FigureFactory.create_violin, + data, colors='foo') + + # error message if colors is no good + pattern3 = ("You must input a valid colors choice. Valid types " + "include a plotly scale, rgb, hex or tuple color, a list " + "of any color types or a dictionary.") + + self.assertRaisesRegexp(PlotlyError, pattern3, + tls.FigureFactory.create_violin, + data, colors=5) + + def test_data_as_list(self): + + # check: data is a non empty list of numbers + + data = [] + + pattern = ("If data is a list, it must be nonempty and contain " + "either numbers or dictionaries.") + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, + data) + + data = [1, 'foo'] + + pattern2 = ("If data is a list, it must contain only numbers.") + + self.assertRaisesRegexp(PlotlyError, pattern2, + tls.FigureFactory.create_violin, data) + + def test_dataframe_input(self): + + # check: dataframe is entered if group_header is True + + data = [1, 2, 3] + + pattern = ("Error. You must use a pandas DataFrame if you are using " + "a group header.") + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, data, + group_header=True) + def test_colors_dict(self): # check: if colorscale is True, make sure colors is not a dictionary @@ -1031,5 +1112,429 @@ def test_colors_dict(self): group_header='a', use_colorscale=True, colors={'a': 'rgb(1, 2, 3)'}) - - + def test_violin_fig(self): + + test_violin = tls.FigureFactory.create_violin(data=[1, 2]) + + exp_violin = { + 'data': [{'fill': 'tonextx', + 'fillcolor': 'rgb(31.0, 119.0, 180.0)', + 'hoverinfo': 'text', + 'line': {'color': 'rgb(0,0,0)', + 'shape': 'spline', + 'width': 0.5}, + 'mode': 'lines', + 'name': '', + 'opacity': 0.5, + 'text': ['(pdf(y), y)=(-0.41, 1.00)', + '(pdf(y), y)=(-0.41, 1.01)', + '(pdf(y), y)=(-0.42, 1.02)', + '(pdf(y), y)=(-0.42, 1.03)', + '(pdf(y), y)=(-0.42, 1.04)', + '(pdf(y), y)=(-0.42, 1.05)', + '(pdf(y), y)=(-0.42, 1.06)', + '(pdf(y), y)=(-0.43, 1.07)', + '(pdf(y), y)=(-0.43, 1.08)', + '(pdf(y), y)=(-0.43, 1.09)', + '(pdf(y), y)=(-0.43, 1.10)', + '(pdf(y), y)=(-0.43, 1.11)', + '(pdf(y), y)=(-0.43, 1.12)', + '(pdf(y), y)=(-0.44, 1.13)', + '(pdf(y), y)=(-0.44, 1.14)', + '(pdf(y), y)=(-0.44, 1.15)', + '(pdf(y), y)=(-0.44, 1.16)', + '(pdf(y), y)=(-0.44, 1.17)', + '(pdf(y), y)=(-0.44, 1.18)', + '(pdf(y), y)=(-0.45, 1.19)', + '(pdf(y), y)=(-0.45, 1.20)', + '(pdf(y), y)=(-0.45, 1.21)', + '(pdf(y), y)=(-0.45, 1.22)', + '(pdf(y), y)=(-0.45, 1.23)', + '(pdf(y), y)=(-0.45, 1.24)', + '(pdf(y), y)=(-0.45, 1.25)', + '(pdf(y), y)=(-0.45, 1.26)', + '(pdf(y), y)=(-0.45, 1.27)', + '(pdf(y), y)=(-0.46, 1.28)', + '(pdf(y), y)=(-0.46, 1.29)', + '(pdf(y), y)=(-0.46, 1.30)', + '(pdf(y), y)=(-0.46, 1.31)', + '(pdf(y), y)=(-0.46, 1.32)', + '(pdf(y), y)=(-0.46, 1.33)', + '(pdf(y), y)=(-0.46, 1.34)', + '(pdf(y), y)=(-0.46, 1.35)', + '(pdf(y), y)=(-0.46, 1.36)', + '(pdf(y), y)=(-0.46, 1.37)', + '(pdf(y), y)=(-0.46, 1.38)', + '(pdf(y), y)=(-0.46, 1.39)', + '(pdf(y), y)=(-0.46, 1.40)', + '(pdf(y), y)=(-0.46, 1.41)', + '(pdf(y), y)=(-0.46, 1.42)', + '(pdf(y), y)=(-0.47, 1.43)', + '(pdf(y), y)=(-0.47, 1.44)', + '(pdf(y), y)=(-0.47, 1.45)', + '(pdf(y), y)=(-0.47, 1.46)', + '(pdf(y), y)=(-0.47, 1.47)', + '(pdf(y), y)=(-0.47, 1.48)', + '(pdf(y), y)=(-0.47, 1.49)', + '(pdf(y), y)=(-0.47, 1.51)', + '(pdf(y), y)=(-0.47, 1.52)', + '(pdf(y), y)=(-0.47, 1.53)', + '(pdf(y), y)=(-0.47, 1.54)', + '(pdf(y), y)=(-0.47, 1.55)', + '(pdf(y), y)=(-0.47, 1.56)', + '(pdf(y), y)=(-0.47, 1.57)', + '(pdf(y), y)=(-0.46, 1.58)', + '(pdf(y), y)=(-0.46, 1.59)', + '(pdf(y), y)=(-0.46, 1.60)', + '(pdf(y), y)=(-0.46, 1.61)', + '(pdf(y), y)=(-0.46, 1.62)', + '(pdf(y), y)=(-0.46, 1.63)', + '(pdf(y), y)=(-0.46, 1.64)', + '(pdf(y), y)=(-0.46, 1.65)', + '(pdf(y), y)=(-0.46, 1.66)', + '(pdf(y), y)=(-0.46, 1.67)', + '(pdf(y), y)=(-0.46, 1.68)', + '(pdf(y), y)=(-0.46, 1.69)', + '(pdf(y), y)=(-0.46, 1.70)', + '(pdf(y), y)=(-0.46, 1.71)', + '(pdf(y), y)=(-0.46, 1.72)', + '(pdf(y), y)=(-0.45, 1.73)', + '(pdf(y), y)=(-0.45, 1.74)', + '(pdf(y), y)=(-0.45, 1.75)', + '(pdf(y), y)=(-0.45, 1.76)', + '(pdf(y), y)=(-0.45, 1.77)', + '(pdf(y), y)=(-0.45, 1.78)', + '(pdf(y), y)=(-0.45, 1.79)', + '(pdf(y), y)=(-0.45, 1.80)', + '(pdf(y), y)=(-0.45, 1.81)', + '(pdf(y), y)=(-0.44, 1.82)', + '(pdf(y), y)=(-0.44, 1.83)', + '(pdf(y), y)=(-0.44, 1.84)', + '(pdf(y), y)=(-0.44, 1.85)', + '(pdf(y), y)=(-0.44, 1.86)', + '(pdf(y), y)=(-0.44, 1.87)', + '(pdf(y), y)=(-0.43, 1.88)', + '(pdf(y), y)=(-0.43, 1.89)', + '(pdf(y), y)=(-0.43, 1.90)', + '(pdf(y), y)=(-0.43, 1.91)', + '(pdf(y), y)=(-0.43, 1.92)', + '(pdf(y), y)=(-0.43, 1.93)', + '(pdf(y), y)=(-0.42, 1.94)', + '(pdf(y), y)=(-0.42, 1.95)', + '(pdf(y), y)=(-0.42, 1.96)', + '(pdf(y), y)=(-0.42, 1.97)', + '(pdf(y), y)=(-0.42, 1.98)', + '(pdf(y), y)=(-0.41, 1.99)', + '(pdf(y), y)=(-0.41, 2.00)'], + 'type': 'scatter', + 'x': np.array([-0.41064744, -0.41293151, -0.41516635, + -0.41735177, -0.41948764, -0.42157385, + -0.42361031, -0.42559697, -0.42753381, + -0.42942082, -0.43125804, -0.43304552, + -0.43478334, -0.4364716, -0.4381104, + -0.4396999, -0.44124025, -0.44273162, + -0.4441742, -0.4455682, -0.44691382, + -0.44821129, -0.44946086, -0.45066275, + -0.45181723, -0.45292454, -0.45398495, + -0.45499871, -0.45596609, -0.45688735, + -0.45776275, -0.45859254, -0.45937698, + -0.46011631, -0.46081078, -0.46146061, + -0.46206603, -0.46262726, -0.46314449, + -0.46361791, -0.4640477, -0.46443404, + -0.46477705, -0.46507689, -0.46533367, + -0.46554749, -0.46571845, -0.4658466, + -0.46593201, -0.4659747, -0.4659747, + -0.46593201, -0.4658466, -0.46571845, + -0.46554749, -0.46533367, -0.46507689, + -0.46477705, -0.46443404, -0.4640477, + -0.46361791, -0.46314449, -0.46262726, + -0.46206603, -0.46146061, -0.46081078, + -0.46011631, -0.45937698, -0.45859254, + -0.45776275, -0.45688735, -0.45596609, + -0.45499871, -0.45398495, -0.45292454, + -0.45181723, -0.45066275, -0.44946086, + -0.44821129, -0.44691382, -0.4455682, + -0.4441742, -0.44273162, -0.44124025, + -0.4396999, -0.4381104, -0.4364716, + -0.43478334, -0.43304552, -0.43125804, + -0.42942082, -0.42753381, -0.42559697, + -0.42361031, -0.42157385, -0.41948764, + -0.41735177, -0.41516635, -0.41293151, + -0.41064744]), + 'y': np.array([1., 1.01010101, 1.02020202, + 1.03030303, 1.04040404, 1.05050505, + 1.06060606, 1.07070707, 1.08080808, + 1.09090909, 1.1010101, 1.11111111, + 1.12121212, 1.13131313, 1.14141414, + 1.15151515, 1.16161616, 1.17171717, + 1.18181818, 1.19191919, 1.2020202, + 1.21212121, 1.22222222, 1.23232323, + 1.24242424, 1.25252525, 1.26262626, + 1.27272727, 1.28282828, 1.29292929, + 1.3030303, 1.31313131, 1.32323232, + 1.33333333, 1.34343434, 1.35353535, + 1.36363636, 1.37373737, 1.38383838, + 1.39393939, 1.4040404, 1.41414141, + 1.42424242, 1.43434343, 1.44444444, + 1.45454545, 1.46464646, 1.47474747, + 1.48484848, 1.49494949, 1.50505051, + 1.51515152, 1.52525253, 1.53535354, + 1.54545455, 1.55555556, 1.56565657, + 1.57575758, 1.58585859, 1.5959596, + 1.60606061, 1.61616162, 1.62626263, + 1.63636364, 1.64646465, 1.65656566, + 1.66666667, 1.67676768, 1.68686869, + 1.6969697, 1.70707071, 1.71717172, + 1.72727273, 1.73737374, 1.74747475, + 1.75757576, 1.76767677, 1.77777778, + 1.78787879, 1.7979798, 1.80808081, + 1.81818182, 1.82828283, 1.83838384, + 1.84848485, 1.85858586, 1.86868687, + 1.87878788, 1.88888889, 1.8989899, + 1.90909091, 1.91919192, 1.92929293, + 1.93939394, 1.94949495, 1.95959596, + 1.96969697, 1.97979798, 1.98989899, + 2.])}, + {'fill': 'tonextx', + 'fillcolor': 'rgb(31.0, 119.0, 180.0)', + 'hoverinfo': 'text', + 'line': {'color': 'rgb(0,0,0)', + 'shape': 'spline', + 'width': 0.5}, + 'mode': 'lines', + 'name': '', + 'opacity': 0.5, + 'text': ['(pdf(y), y)=(0.41, 1.00)', + '(pdf(y), y)=(0.41, 1.01)', + '(pdf(y), y)=(0.42, 1.02)', + '(pdf(y), y)=(0.42, 1.03)', + '(pdf(y), y)=(0.42, 1.04)', + '(pdf(y), y)=(0.42, 1.05)', + '(pdf(y), y)=(0.42, 1.06)', + '(pdf(y), y)=(0.43, 1.07)', + '(pdf(y), y)=(0.43, 1.08)', + '(pdf(y), y)=(0.43, 1.09)', + '(pdf(y), y)=(0.43, 1.10)', + '(pdf(y), y)=(0.43, 1.11)', + '(pdf(y), y)=(0.43, 1.12)', + '(pdf(y), y)=(0.44, 1.13)', + '(pdf(y), y)=(0.44, 1.14)', + '(pdf(y), y)=(0.44, 1.15)', + '(pdf(y), y)=(0.44, 1.16)', + '(pdf(y), y)=(0.44, 1.17)', + '(pdf(y), y)=(0.44, 1.18)', + '(pdf(y), y)=(0.45, 1.19)', + '(pdf(y), y)=(0.45, 1.20)', + '(pdf(y), y)=(0.45, 1.21)', + '(pdf(y), y)=(0.45, 1.22)', + '(pdf(y), y)=(0.45, 1.23)', + '(pdf(y), y)=(0.45, 1.24)', + '(pdf(y), y)=(0.45, 1.25)', + '(pdf(y), y)=(0.45, 1.26)', + '(pdf(y), y)=(0.45, 1.27)', + '(pdf(y), y)=(0.46, 1.28)', + '(pdf(y), y)=(0.46, 1.29)', + '(pdf(y), y)=(0.46, 1.30)', + '(pdf(y), y)=(0.46, 1.31)', + '(pdf(y), y)=(0.46, 1.32)', + '(pdf(y), y)=(0.46, 1.33)', + '(pdf(y), y)=(0.46, 1.34)', + '(pdf(y), y)=(0.46, 1.35)', + '(pdf(y), y)=(0.46, 1.36)', + '(pdf(y), y)=(0.46, 1.37)', + '(pdf(y), y)=(0.46, 1.38)', + '(pdf(y), y)=(0.46, 1.39)', + '(pdf(y), y)=(0.46, 1.40)', + '(pdf(y), y)=(0.46, 1.41)', + '(pdf(y), y)=(0.46, 1.42)', + '(pdf(y), y)=(0.47, 1.43)', + '(pdf(y), y)=(0.47, 1.44)', + '(pdf(y), y)=(0.47, 1.45)', + '(pdf(y), y)=(0.47, 1.46)', + '(pdf(y), y)=(0.47, 1.47)', + '(pdf(y), y)=(0.47, 1.48)', + '(pdf(y), y)=(0.47, 1.49)', + '(pdf(y), y)=(0.47, 1.51)', + '(pdf(y), y)=(0.47, 1.52)', + '(pdf(y), y)=(0.47, 1.53)', + '(pdf(y), y)=(0.47, 1.54)', + '(pdf(y), y)=(0.47, 1.55)', + '(pdf(y), y)=(0.47, 1.56)', + '(pdf(y), y)=(0.47, 1.57)', + '(pdf(y), y)=(0.46, 1.58)', + '(pdf(y), y)=(0.46, 1.59)', + '(pdf(y), y)=(0.46, 1.60)', + '(pdf(y), y)=(0.46, 1.61)', + '(pdf(y), y)=(0.46, 1.62)', + '(pdf(y), y)=(0.46, 1.63)', + '(pdf(y), y)=(0.46, 1.64)', + '(pdf(y), y)=(0.46, 1.65)', + '(pdf(y), y)=(0.46, 1.66)', + '(pdf(y), y)=(0.46, 1.67)', + '(pdf(y), y)=(0.46, 1.68)', + '(pdf(y), y)=(0.46, 1.69)', + '(pdf(y), y)=(0.46, 1.70)', + '(pdf(y), y)=(0.46, 1.71)', + '(pdf(y), y)=(0.46, 1.72)', + '(pdf(y), y)=(0.45, 1.73)', + '(pdf(y), y)=(0.45, 1.74)', + '(pdf(y), y)=(0.45, 1.75)', + '(pdf(y), y)=(0.45, 1.76)', + '(pdf(y), y)=(0.45, 1.77)', + '(pdf(y), y)=(0.45, 1.78)', + '(pdf(y), y)=(0.45, 1.79)', + '(pdf(y), y)=(0.45, 1.80)', + '(pdf(y), y)=(0.45, 1.81)', + '(pdf(y), y)=(0.44, 1.82)', + '(pdf(y), y)=(0.44, 1.83)', + '(pdf(y), y)=(0.44, 1.84)', + '(pdf(y), y)=(0.44, 1.85)', + '(pdf(y), y)=(0.44, 1.86)', + '(pdf(y), y)=(0.44, 1.87)', + '(pdf(y), y)=(0.43, 1.88)', + '(pdf(y), y)=(0.43, 1.89)', + '(pdf(y), y)=(0.43, 1.90)', + '(pdf(y), y)=(0.43, 1.91)', + '(pdf(y), y)=(0.43, 1.92)', + '(pdf(y), y)=(0.43, 1.93)', + '(pdf(y), y)=(0.42, 1.94)', + '(pdf(y), y)=(0.42, 1.95)', + '(pdf(y), y)=(0.42, 1.96)', + '(pdf(y), y)=(0.42, 1.97)', + '(pdf(y), y)=(0.42, 1.98)', + '(pdf(y), y)=(0.41, 1.99)', + '(pdf(y), y)=(0.41, 2.00)'], + 'type': 'scatter', + 'x': np.array([0.41064744, 0.41293151, 0.41516635, + 0.41735177, 0.41948764, 0.42157385, + 0.42361031, 0.42559697, 0.42753381, + 0.42942082, 0.43125804, 0.43304552, + 0.43478334, 0.4364716, 0.4381104, + 0.4396999, 0.44124025, 0.44273162, + 0.4441742, 0.4455682, 0.44691382, + 0.44821129, 0.44946086, 0.45066275, + 0.45181723, 0.45292454, 0.45398495, + 0.45499871, 0.45596609, 0.45688735, + 0.45776275, 0.45859254, 0.45937698, + 0.46011631, 0.46081078, 0.46146061, + 0.46206603, 0.46262726, 0.46314449, + 0.46361791, 0.4640477, 0.46443404, + 0.46477705, 0.46507689, 0.46533367, + 0.46554749, 0.46571845, 0.4658466, + 0.46593201, 0.4659747, 0.4659747, + 0.46593201, 0.4658466, 0.46571845, + 0.46554749, 0.46533367, 0.46507689, + 0.46477705, 0.46443404, 0.4640477, + 0.46361791, 0.46314449, 0.46262726, + 0.46206603, 0.46146061, 0.46081078, + 0.46011631, 0.45937698, 0.45859254, + 0.45776275, 0.45688735, 0.45596609, + 0.45499871, 0.45398495, 0.45292454, + 0.45181723, 0.45066275, 0.44946086, + 0.44821129, 0.44691382, 0.4455682, + 0.4441742, 0.44273162, 0.44124025, + 0.4396999, 0.4381104, 0.4364716, + 0.43478334, 0.43304552, 0.43125804, + 0.42942082, 0.42753381, 0.42559697, + 0.42361031, 0.42157385, 0.41948764, + 0.41735177, 0.41516635, 0.41293151, + 0.41064744]), + 'y': np.array([1., 1.01010101, 1.02020202, + 1.03030303, 1.04040404, 1.05050505, + 1.06060606, 1.07070707, 1.08080808, + 1.09090909, 1.1010101, 1.11111111, + 1.12121212, 1.13131313, 1.14141414, + 1.15151515, 1.16161616, 1.17171717, + 1.18181818, 1.19191919, 1.2020202, + 1.21212121, 1.22222222, 1.23232323, + 1.24242424, 1.25252525, 1.26262626, + 1.27272727, 1.28282828, 1.29292929, + 1.3030303, 1.31313131, 1.32323232, + 1.33333333, 1.34343434, 1.35353535, + 1.36363636, 1.37373737, 1.38383838, + 1.39393939, 1.4040404, 1.41414141, + 1.42424242, 1.43434343, 1.44444444, + 1.45454545, 1.46464646, 1.47474747, + 1.48484848, 1.49494949, 1.50505051, + 1.51515152, 1.52525253, 1.53535354, + 1.54545455, 1.55555556, 1.56565657, + 1.57575758, 1.58585859, 1.5959596, + 1.60606061, 1.61616162, 1.62626263, + 1.63636364, 1.64646465, 1.65656566, + 1.66666667, 1.67676768, 1.68686869, + 1.6969697, 1.70707071, 1.71717172, + 1.72727273, 1.73737374, 1.74747475, + 1.75757576, 1.76767677, 1.77777778, + 1.78787879, 1.7979798, 1.80808081, + 1.81818182, 1.82828283, 1.83838384, + 1.84848485, 1.85858586, 1.86868687, + 1.87878788, 1.88888889, 1.8989899, + 1.90909091, 1.91919192, 1.92929293, + 1.93939394, 1.94949495, 1.95959596, + 1.96969697, 1.97979798, 1.98989899, + 2.])}, + {'line': {'color': 'rgb(0,0,0)', 'width': 1.5}, + 'mode': 'lines', + 'name': '', + 'type': 'scatter', + 'x': [0, 0], + 'y': [1.0, 2.0]}, + {'hoverinfo': 'text', + 'line': {'color': 'rgb(0,0,0)', 'width': 4}, + 'mode': 'lines', + 'text': ['lower-quartile: 1.00', + 'upper-quartile: 2.00'], + 'type': 'scatter', + 'x': [0, 0], + 'y': [1.0, 2.0]}, + {'hoverinfo': 'text', + 'marker': {'color': 'rgb(255,255,255)', + 'symbol': 'square'}, + 'mode': 'markers', + 'text': ['median: 1.50'], + 'type': 'scatter', + 'x': [0], + 'y': [1.5]}, + {'hoverinfo': 'y', + 'marker': {'color': 'rgb(31.0, 119.0, 180.0)', + 'symbol': 'line-ew-open'}, + 'mode': 'markers', + 'name': '', + 'showlegend': False, + 'type': 'scatter', + 'x': [-0.55916964093970667, -0.55916964093970667], + 'y': np.array([1., 2.])}], + 'layout': {'autosize': False, + 'font': {'size': 11}, + 'height': 450, + 'hovermode': 'closest', + 'showlegend': False, + 'title': 'Violin and Rug Plot', + 'width': 600, + 'xaxis': {'mirror': False, + 'range': [-0.65916964093970665, + 0.56597470078308887], + 'showgrid': False, + 'showline': False, + 'showticklabels': False, + 'ticks': '', + 'title': '', + 'zeroline': False}, + 'yaxis': {'autorange': True, + 'mirror': False, + 'showgrid': False, + 'showline': False, + 'showticklabels': False, + 'ticklen': 4, + 'ticks': '', + 'title': '', + 'zeroline': False}}} + + self.assert_dict_equal(test_violin['data'][0], + exp_violin['data'][0]) + + self.assert_dict_equal(test_violin['data'][1], + exp_violin['data'][1]) + + self.assert_dict_equal(test_violin['layout'], + exp_violin['layout']) From 9a20ece8d1ef6654a96004b8b0a03e0d1b6e6e6b Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Sun, 5 Jun 2016 19:48:03 -0400 Subject: [PATCH 14/27] Add a description line --- plotly/tests/test_optional/test_figure_factory.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plotly/tests/test_optional/test_figure_factory.py b/plotly/tests/test_optional/test_figure_factory.py index b77380e0209..cdcd4b97603 100644 --- a/plotly/tests/test_optional/test_figure_factory.py +++ b/plotly/tests/test_optional/test_figure_factory.py @@ -1019,6 +1019,7 @@ class TestViolin(NumpyTestUtilsMixin, TestCase): def test_colors_validation(self): # check: colors is in an acceptable form + data = [1, 5, 8] pattern = ("Whoops! The elements in your rgb colors tuples cannot " @@ -1114,6 +1115,8 @@ def test_colors_dict(self): def test_violin_fig(self): + # check: test violin fig matches expected fig + test_violin = tls.FigureFactory.create_violin(data=[1, 2]) exp_violin = { From d3059e01fb2a5dc977f44a036dcb631b2d37eeb7 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 6 Jun 2016 14:41:16 -0400 Subject: [PATCH 15/27] added tests//added group labels to plots//edited main doc string --- .../test_optional/test_figure_factory.py | 78 +++++++- plotly/tools.py | 175 +++++++++++++----- 2 files changed, 206 insertions(+), 47 deletions(-) diff --git a/plotly/tests/test_optional/test_figure_factory.py b/plotly/tests/test_optional/test_figure_factory.py index cdcd4b97603..3578a8c9ce0 100644 --- a/plotly/tests/test_optional/test_figure_factory.py +++ b/plotly/tests/test_optional/test_figure_factory.py @@ -1065,6 +1065,20 @@ def test_colors_validation(self): tls.FigureFactory.create_violin, data, colors=5) + def test_data_header(self): + + # make sure data_header is entered + + data = pd.DataFrame([['apple', 2], ['pear', 4]], + columns=['a', 'b']) + + pattern = ("data_header must be the column name with the desired " + "numeric data for the violin plot.") + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, data, + group_header='a', colors=['rgb(1, 2, 3)']) + def test_data_as_list(self): # check: data is a non empty list of numbers @@ -1105,14 +1119,70 @@ def test_colors_dict(self): data = pd.DataFrame([['apple', 2], ['pear', 4]], columns=['a', 'b']) - pattern = ("You cannot use a dictionary if you are using a " - "colorscale.") + pattern = ("The colors param cannot be a dictionary if you are " + "using a colorscale.") self.assertRaisesRegexp(PlotlyError, pattern, tls.FigureFactory.create_violin, data, - group_header='a', use_colorscale=True, + data_header='b', group_header='a', + use_colorscale=True, colors={'a': 'rgb(1, 2, 3)'}) + # check: colors contains all group names as keys + + pattern2 = ("If colors is a dictionary, all the group names must " + "appear as keys in colors.") + + self.assertRaisesRegexp(PlotlyError, pattern2, + tls.FigureFactory.create_violin, data, + data_header='b', group_header='a', + use_colorscale=False, + colors={'a': 'rgb(1, 2, 3)'}) + + def test_valid_colorscale(self): + + # check: if colorscale is enabled, colors is a list with 2+ items + + data = pd.DataFrame([['apple', 2], ['pear', 4]], + columns=['a', 'b']) + + pattern = ("colors must be a list with at least 2 colors. A Plotly " + "scale is allowed.") + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, data, + data_header='b', group_header='a', + use_colorscale=True, + colors='rgb(1, 2, 3)') + + def test_group_stats(self): + + # check: group_stats is a dictionary + + data = pd.DataFrame([['apple', 2], ['pear', 4]], + columns=['a', 'b']) + + pattern = ("Your group_stats param must be a dictionary.") + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_violin, data, + data_header='b', group_header='a', + use_colorscale=True, + colors=['rgb(1, 2, 3)', 'rgb(4, 5, 6)'], + group_stats=1) + + # check: all groups are represented as keys in group_stats + + pattern2 = ("All values/groups in the index column must be " + "represented as a key in group_stats.") + + self.assertRaisesRegexp(PlotlyError, pattern2, + tls.FigureFactory.create_violin, data, + data_header='b', group_header='a', + use_colorscale=True, + colors=['rgb(1, 2, 3)', 'rgb(4, 5, 6)'], + group_stats={'apple': 1}) +""" def test_violin_fig(self): # check: test violin fig matches expected fig @@ -1541,3 +1611,5 @@ def test_violin_fig(self): self.assert_dict_equal(test_violin['layout'], exp_violin['layout']) + +""" diff --git a/plotly/tools.py b/plotly/tools.py index a93efc7b590..b46509dd0f1 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -28,6 +28,10 @@ 'rgb(227, 119, 194)', 'rgb(127, 127, 127)', 'rgb(188, 189, 34)', 'rgb(23, 190, 207)'] +#color constants for violin plot +fillcolor = '#1f77b4' + + # Warning format def warning_on_one_line(message, category, filename, lineno, @@ -1463,17 +1467,25 @@ def _calc_stats(data): q2 = np.percentile(x, 50, interpolation='linear') q1 = np.percentile(x, 25, interpolation='lower') q3 = np.percentile(x, 75, interpolation='higher') - IQR = q3 - q1 - whisker_dist = 1.5 * IQR + iqr = q3 - q1 + whisker_dist = 1.5 * iqr # in order to prevent drawing whiskers outside the interval # of data one defines the whisker positions as: d1 = np.min(x[x >= (q1 - whisker_dist)]) d2 = np.max(x[x <= (q3 + whisker_dist)]) - return vals_min, vals_max, q1, q2, q3, d1, d2 + return { + 'min': vals_min, + 'max': vals_max, + 'q1': q1, + 'q2': q2, + 'q3': q3, + 'd1': d1, + 'd2': d2 + } @staticmethod - def _make_half_violin(x, y, fillcolor='#1f77b4', linecolor='rgb(0,0,0)'): + def _make_half_violin(x, y, fillcolor=fillcolor, linecolor='rgb(0, 0, 0)'): """ Produces a sideways probability distribution fig violin plot. """ @@ -1482,9 +1494,11 @@ def _make_half_violin(x, y, fillcolor='#1f77b4', linecolor='rgb(0,0,0)'): text = ['(pdf(y), y)=(' + '{:0.2f}'.format(x[i]) + ', ' + '{:0.2f}'.format(y[i]) + ')' for i in range(len(x))] + return graph_objs.Scatter( x=x, - y=y, mode='lines', + y=y, + mode='lines', name='', text=text, fill='tonextx', @@ -1495,7 +1509,7 @@ def _make_half_violin(x, y, fillcolor='#1f77b4', linecolor='rgb(0,0,0)'): ) @staticmethod - def _make_rugplot(vals, pdf_max, distance, color='#1f77b4'): + def _make_violin_rugplot(vals, pdf_max, distance, color=fillcolor): """ Returns a rugplot fig for a violin plot. """ @@ -1555,7 +1569,6 @@ def _make_median(q2): def _make_non_outlier_interval(d1, d2): """ Returns the scatterplot fig of most of a violin plot. - """ from plotly.graph_objs import graph_objs @@ -1572,7 +1585,6 @@ def _make_non_outlier_interval(d1, d2): def _make_XAxis(xaxis_title, xaxis_range): """ Makes the x-axis for a violin plot. - """ from plotly.graph_objs import graph_objs @@ -1591,7 +1603,6 @@ def _make_XAxis(xaxis_title, xaxis_range): def _make_YAxis(yaxis_title): """ Makes the y-axis for a violin plot. - """ from plotly.graph_objs import graph_objs @@ -1606,7 +1617,7 @@ def _make_YAxis(yaxis_title): return yaxis @staticmethod - def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): + def _violinplot(vals, fillcolor=fillcolor, rugplot=True): """ Refer to FigureFactory.create_violin() for docstring. """ @@ -1615,7 +1626,14 @@ def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): vals = np.asarray(vals, np.float) # summary statistics - vals_min, vals_max, q1, q2, q3, d1, d2 = FigureFactory._calc_stats(vals) + vals_min = FigureFactory._calc_stats(vals)['min'] + vals_max = FigureFactory._calc_stats(vals)['max'] + q1 = FigureFactory._calc_stats(vals)['q1'] + q2 = FigureFactory._calc_stats(vals)['q2'] + q3 = FigureFactory._calc_stats(vals)['q3'] + d1 = FigureFactory._calc_stats(vals)['d1'] + d2 = FigureFactory._calc_stats(vals)['d2'] + # kernel density estimation of pdf pdf = stats.gaussian_kde(vals) # grid over the data interval @@ -1627,21 +1645,26 @@ def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): distance = (2.0 * max_pdf)/10 if rugplot else 0 # range for x values in the plot plot_xrange = [-max_pdf - distance - 0.1, max_pdf + 0.1] - plot_data = [FigureFactory._make_half_violin(-yy, xx, fillcolor=fillcolor), - FigureFactory._make_half_violin(yy, xx, fillcolor=fillcolor), + plot_data = [FigureFactory._make_half_violin(-yy, xx, + fillcolor=fillcolor), + FigureFactory._make_half_violin(yy, xx, + fillcolor=fillcolor), FigureFactory._make_non_outlier_interval(d1, d2), FigureFactory._make_quartiles(q1, q3), FigureFactory._make_median(q2)] if rugplot: - plot_data.append(FigureFactory._make_rugplot(vals, - max_pdf, - distance=distance, - color=fillcolor)) + plot_data.append(FigureFactory._make_violin_rugplot( + vals, + max_pdf, + distance=distance, + color=fillcolor) + ) return plot_data, plot_xrange @staticmethod - def _violin_no_colorscale(data, data_header, colors, use_colorscale, - group_header, height, width, title): + def _violin_no_colorscale(data, data_header, group_header, colors, + use_colorscale, group_stats, + height, width, title): """ Refer to FigureFactory.create_violin() for docstring. @@ -1679,6 +1702,12 @@ def _violin_no_colorscale(data, data_header, colors, use_colorscale, for item in plot_data: fig.append_trace(item, 1, k + 1) color_index += 1 + + # add violin plot labels + fig['layout'].update({'xaxis{}'.format(k + 1): + FigureFactory._make_XAxis(group_name[k], + plot_xrange)}) + # set the sharey axis style fig['layout'].update( {'yaxis{}'.format(1): FigureFactory._make_YAxis('')} @@ -1695,8 +1724,8 @@ def _violin_no_colorscale(data, data_header, colors, use_colorscale, return fig @staticmethod - def _violin_colorscale(data, data_header, colors, use_colorscale, - group_header, height, width, title): + def _violin_colorscale(data, data_header, group_header, colors, + use_colorscale, group_stats, height, width, title): """ Refer to FigureFactory.create_violin() for docstring. @@ -1713,6 +1742,13 @@ def _violin_colorscale(data, data_header, colors, use_colorscale, group_name.append(name) group_name.sort() + # make sure all group names are keys in group_stats + for group in group_name: + if group not in group_stats: + raise exceptions.PlotlyError("All values/groups in the index " + "column must be represented " + "as a key in group_stats.") + gb = data.groupby([group_header]) L = len(group_name) @@ -1721,15 +1757,27 @@ def _violin_colorscale(data, data_header, colors, use_colorscale, horizontal_spacing=0.025, print_grid=True) + # prepare low and high color for colorscale + lowcolor = FigureFactory._unlabel_rgb(colors[0]) + highcolor = FigureFactory._unlabel_rgb(colors[1]) + + # find min and max values in group_stats + group_stats_values = [] + for key in group_stats: + group_stats_values.append(group_stats[key]) + + max_value = max(group_stats_values) + min_value = min(group_stats_values) + for k, gr in enumerate(group_name): vals = np.asarray(gb.get_group(gr)[data_header], np.float) - # find colorscale color - lowcolor = FigureFactory._unlabel_rgb(colors[0]) - highcolor = FigureFactory._unlabel_rgb(colors[1]) + # find intermediate color from colorscale + intermed = (group_stats[gr] - min_value) / (max_value - min_value) intermed_color = FigureFactory._find_intermediate_color( - lowcolor, highcolor, k/float(L) + lowcolor, highcolor, intermed ) + plot_data, plot_xrange = FigureFactory._violinplot( vals, fillcolor='rgb{}'.format(intermed_color) @@ -1743,8 +1791,8 @@ def _violin_colorscale(data, data_header, colors, use_colorscale, plot_xrange)}) # add colorbar to plot trace_dummy = graph_objs.Scatter( - x=[24], - y=[10], + x=[0], + y=[0], mode='markers', marker=dict( size=2, @@ -1773,8 +1821,8 @@ def _violin_colorscale(data, data_header, colors, use_colorscale, return fig @staticmethod - def _violin_dict(data, data_header, colors, use_colorscale, - group_header, height, width, title): + def _violin_dict(data, data_header, group_header, colors, use_colorscale, + group_stats, height, width, title): """ Refer to FigureFactory.create_violin() for docstring. @@ -1794,7 +1842,9 @@ def _violin_dict(data, data_header, colors, use_colorscale, # check if all group names appear in colors dict for group in group_name: if group not in colors: - raise exceptions.PlotlyError("A") + raise exceptions.PlotlyError("If colors is a dictionary, all " + "the group names must appear as " + "keys in colors.") gb = data.groupby([group_header]) L = len(group_name) @@ -1814,6 +1864,12 @@ def _violin_dict(data, data_header, colors, use_colorscale, for item in plot_data: fig.append_trace(item, 1, k + 1) + + # add violin plot labels + fig['layout'].update({'xaxis{}'.format(k + 1): + FigureFactory._make_XAxis(group_name[k], + plot_xrange)}) + # set the sharey axis style fig['layout'].update( {'yaxis{}'.format(1): FigureFactory._make_YAxis('')} @@ -1830,8 +1886,8 @@ def _violin_dict(data, data_header, colors, use_colorscale, return fig @staticmethod - def create_violin(data, data_header=None, colors=None, - use_colorscale=False, group_header=None, + def create_violin(data, data_header=None, group_header=None, + colors=None, use_colorscale=False, group_stats=None, height=450, width=600, title='Violin and Rug Plot'): """ Returns figure for a violin plot @@ -1843,6 +1899,8 @@ def create_violin(data, data_header=None, colors=None, :param (str) data_header: the header of the data column to be used from an inputted pandas dataframe. Not applicable if 'data' is a list of numeric values + :param (str) group_header: applicable if grouping data by a variable. + 'group_header' must be set to the name of the grouping variable. :param (str|tuple|list|dict) colors: either a plotly scale name, an rgb or hex color, a color tuple, a list of colors or a dictionary. An rgb color is of the form 'rgb(x, y, z)' where @@ -1850,11 +1908,15 @@ def create_violin(data, data_header=None, colors=None, tuple of the form (a, b, c) where a, b and c belong to [0, 1]. If colors is a list, it must contain valid color types as its members. - :param (bool) use_colorscale: will implement a colorscale based on the - first 2 color strings of 'colors' if a list. Only applicable if - grouping by another variable - :param (str) group_header: applicable if grouping data by a variable. - 'group_header' must be set to the name of the grouping variable. + :param (bool) use_colorscale: Only applicable if grouping by another + variable. Will implement a colorscale based on the first 2 colors + of param colors. This means colors must be a list with at least 2 + colors in it (Plotly colorscales are accepted since they map to a + list of two rgb colors) + :param (dict) group_stats: a dictioanry where each key is a unique + value from the group_header column in data. Each value must be a + number and will be used to color the violin plots if a colorscale + is being used :param (float) height: the height of the violin plot :param (float) width: the width of the violin plot :param (str) title: the title of the violin plot @@ -2079,6 +2141,12 @@ def create_violin(data, data_header=None, colors=None, "contain only numbers.") if _pandas_imported and isinstance(data, pd.core.frame.DataFrame): + if data_header is None: + raise exceptions.PlotlyError("data_header must be the " + "column name with the " + "desired numeric data for " + "the violin plot.") + data = data[data_header].values.tolist() # call the plotting functions @@ -2109,33 +2177,52 @@ def create_violin(data, data_header=None, colors=None, raise exceptions.PlotlyError("Error. You must use a pandas " "DataFrame if you are using a " "group header.") + + if data_header is None: + raise exceptions.PlotlyError("data_header must be the column " + "name with the desired numeric " + "data for the violin plot.") + if use_colorscale is False: if isinstance(colors, dict): # validate colors dict choice below fig = FigureFactory._violin_dict(data, data_header, - colors, use_colorscale, - group_header, height, - width, title) + group_header, colors, + use_colorscale, + group_stats, + height, width, title) return fig else: fig = FigureFactory._violin_no_colorscale(data, data_header, + group_header, colors, use_colorscale, - group_header, + group_stats, height, width, title) return fig else: if isinstance(colors, dict): - raise exceptions.PlotlyError("You cannot use a " - "dictionary if you are " + raise exceptions.PlotlyError("The colors param cannot be " + "a dictionary if you are " "using a colorscale.") + + if len(colors) < 2: + raise exceptions.PlotlyError("colors must be a list with " + "at least 2 colors. A " + "Plotly scale is allowed.") + + if not isinstance(group_stats, dict): + raise exceptions.PlotlyError("Your group_stats param " + "must be a dictionary.") + fig = FigureFactory._violin_colorscale(data, data_header, + group_header, colors, use_colorscale, - group_header, + group_stats, height, width, title) From ce6f83a11e72f41b08e013e250f1ed918b3b7c6f Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 6 Jun 2016 14:54:03 -0400 Subject: [PATCH 16/27] Added/updated doc string examples --- plotly/tools.py | 57 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index b46509dd0f1..745a11a260f 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1923,34 +1923,63 @@ def create_violin(data, data_header=None, group_header=None, Example 1: Single Violin Plot ``` - import numpy as np - from scipy import stats - import plotly.plotly as py from plotly.tools import FigureFactory as FF from plotly.graph_objs import graph_objs + import numpy as np + from scipy import stats + # create list of random values data_list = np.random.randn(100) data_list.tolist() # create violin fig - fig = FF.create_violin(data_list, colors='#1f77b4') + fig = FF.create_violin(data_list, colors='#604d9e') # plot py.iplot(fig, filename='Violin Plot') ``` - Example 2: Multiple Violin Plots with Colorscale + Example 2: Multiple Violin Plots with Qualitative Coloring ``` + import plotly.plotly as py + from plotly.tools import FigureFactory as FF + from plotly.graph_objs import graph_objs + import numpy as np import pandas as pd from scipy import stats + # create dataframe + np.random.seed(619517) + Nr=250 + y = np.random.randn(Nr) + gr = np.random.choice(list("ABCDE"), Nr) + norm_params=[(0, 1.2), (0.7, 1), (-0.5, 1.4), (0.3, 1), (0.8, 0.9)] + + for i, letter in enumerate("ABCDE"): + y[gr == letter] *=norm_params[i][1]+ norm_params[i][0] + df = pd.DataFrame(dict(Score=y, Group=gr)) + + # create violin fig + fig = FF.create_violin(df, data_header='Score', group_header='Group', + height=600, width=1000) + + # plot + py.iplot(fig, filename='Violin Plot with Coloring') + ``` + + Example 3: Violin Plots with Colorscale + ``` import plotly.plotly as py from plotly.tools import FigureFactory as FF from plotly.graph_objs import graph_objs + import numpy as np + import pandas as pd + from scipy import stats + # create dataframe np.random.seed(619517) Nr=250 @@ -1962,10 +1991,24 @@ def create_violin(data, data_header=None, group_header=None, y[gr == letter] *=norm_params[i][1]+ norm_params[i][0] df = pd.DataFrame(dict(Score=y, Group=gr)) + # define header params + data_header = 'Score' + group_header = 'Group' + + # make groupby object with pandas + groupby_data = df.groupby([group_header]) + + for group in "ABCDE": + data_from_group = groupby_data.get_group(group)[data_header] + # take a stat of the grouped data + stat = np.median(data_from_group) + # add to dictionary + group_stats[group] = stat + # create violin fig fig = FF.create_violin(df, data_header='Score', group_header='Group', - use_colorscale=True, colors='Blues', - height=600, width=1000) + height=600, width=1000, use_colorscale=True, + group_stats=group_stats) # plot py.iplot(fig, filename='Violin Plot with Colorscale') From 164a9c63f69699dc430f5f9e30677c1d56629ded Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 6 Jun 2016 16:16:27 -0400 Subject: [PATCH 17/27] accurate colorscale max/min labels --- plotly/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 745a11a260f..ed6239336aa 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1796,8 +1796,8 @@ def _violin_colorscale(data, data_header, group_header, colors, mode='markers', marker=dict( size=2, - cmin=0, - cmax=100, + cmin=min_value, + cmax=max_value, colorscale=[[0, colors[0]], [1, colors[1]]], showscale=True), From 4abec6e19b505cda17495c6cb7427614f3cb0560 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 8 Jun 2016 10:22:06 -0400 Subject: [PATCH 18/27] Made error messages more legible --- plotly/tools.py | 124 +++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 69 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index ed6239336aa..2451bbfe994 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -29,7 +29,7 @@ 'rgb(188, 189, 34)', 'rgb(23, 190, 207)'] #color constants for violin plot -fillcolor = '#1f77b4' +DEFAULT_FILLCOLOR = '#1f77b4' @@ -1485,7 +1485,8 @@ def _calc_stats(data): } @staticmethod - def _make_half_violin(x, y, fillcolor=fillcolor, linecolor='rgb(0, 0, 0)'): + def _make_half_violin(x, y, fillcolor=DEFAULT_FILLCOLOR, + linecolor='rgb(0, 0, 0)'): """ Produces a sideways probability distribution fig violin plot. """ @@ -1502,14 +1503,15 @@ def _make_half_violin(x, y, fillcolor=fillcolor, linecolor='rgb(0, 0, 0)'): name='', text=text, fill='tonextx', - fillcolor=fillcolor, + fillcolor=DEFAULT_FILLCOLOR, line=graph_objs.Line(width=0.5, color=linecolor, shape='spline'), hoverinfo='text', opacity=0.5 ) @staticmethod - def _make_violin_rugplot(vals, pdf_max, distance, color=fillcolor): + def _make_violin_rugplot(vals, pdf_max, distance, + color=DEFAULT_FILLCOLOR): """ Returns a rugplot fig for a violin plot. """ @@ -1617,7 +1619,7 @@ def _make_YAxis(yaxis_title): return yaxis @staticmethod - def _violinplot(vals, fillcolor=fillcolor, rugplot=True): + def _violinplot(vals, fillcolor=DEFAULT_FILLCOLOR, rugplot=True): """ Refer to FigureFactory.create_violin() for docstring. """ @@ -1645,10 +1647,10 @@ def _violinplot(vals, fillcolor=fillcolor, rugplot=True): distance = (2.0 * max_pdf)/10 if rugplot else 0 # range for x values in the plot plot_xrange = [-max_pdf - distance - 0.1, max_pdf + 0.1] - plot_data = [FigureFactory._make_half_violin(-yy, xx, - fillcolor=fillcolor), - FigureFactory._make_half_violin(yy, xx, - fillcolor=fillcolor), + plot_data = [FigureFactory._make_half_violin( + -yy, xx, fillcolor=DEFAULT_FILLCOLOR), + FigureFactory._make_half_violin( + yy, xx, fillcolor=DEFAULT_FILLCOLOR), FigureFactory._make_non_outlier_interval(d1, d2), FigureFactory._make_quartiles(q1, q3), FigureFactory._make_median(q2)] @@ -1657,7 +1659,7 @@ def _violinplot(vals, fillcolor=fillcolor, rugplot=True): vals, max_pdf, distance=distance, - color=fillcolor) + color=DEFAULT_FILLCOLOR) ) return plot_data, plot_xrange @@ -2055,11 +2057,11 @@ def create_violin(data, data_header=None, group_header=None, colors = FigureFactory._unlabel_rgb(colors) for value in colors: if value > 255.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in your " - "rgb colors " - "tuples cannot " - "exceed 255.0.") + raise exceptions.PlotlyError( + "Whoops! The elements in your rgb colors " + "tuples cannot exceed 255.0." + ) + colors = FigureFactory._label_rgb(colors) # put colors in list @@ -2078,22 +2080,20 @@ def create_violin(data, data_header=None, group_header=None, else: scale_keys = list(plotly_scales.keys()) - raise exceptions.PlotlyError("If you input a string " - "for 'colors', it must " - "either be a Plotly " - "colorscale, an 'rgb' " - "color or a hex color." - "Valid plotly colorscale " - "names are {}".format(scale_keys)) + raise exceptions.PlotlyError( + "If you input a string for 'colors', it must either be " + "a Plotly colorscale, an 'rgb' color or a hex color. " + "Valid plotly colorscale names are {}".format(scale_keys) + ) + elif isinstance(colors, tuple): # validate tuple color for value in colors: if value > 1.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in " - "your colors " - "tuples cannot " - "exceed 1.0.") + raise exceptions.PlotlyError( + "Whoops! The elements in your colors tuples cannot " + "exceed 1.0." + ) colors = FigureFactory._convert_to_RGB_255(colors) colors = FigureFactory._label_rgb(colors) @@ -2109,11 +2109,10 @@ def create_violin(data, data_header=None, group_header=None, color = FigureFactory._unlabel_rgb(color) for value in color: if value > 255.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in your " - "rgb colors " - "tuples cannot " - "exceed 255.0.") + raise exceptions.PlotlyError( + "Whoops! The elements in your rgb colors " + "tuples cannot exceed 255.0." + ) color = FigureFactory._label_rgb(color) new_colormap.append(color) @@ -2124,11 +2123,10 @@ def create_violin(data, data_header=None, group_header=None, elif isinstance(color, tuple): for value in color: if value > 1.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in " - "your colors " - "tuples cannot " - "exceed 1.0.") + raise exceptions.PlotlyError( + "Whoops! The elements in your colors " + "tuples cannot exceed 1.0." + ) color = FigureFactory._convert_to_RGB_255(color) color = FigureFactory._label_rgb(color) @@ -2141,11 +2139,10 @@ def create_violin(data, data_header=None, group_header=None, color = FigureFactory._unlabel_rgb(colors[name]) for value in color: if value > 255.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in your " - "rgb colors " - "tuples cannot " - "exceed 255.0.") + raise exceptions.PlotlyError( + "Whoops! The elements in your rgb colors " + "tuples cannot exceed 255.0." + ) elif '#' in colors[name]: color = FigureFactory._hex_to_rgb(colors[name]) @@ -2155,11 +2152,10 @@ def create_violin(data, data_header=None, group_header=None, elif isinstance(colors[name], tuple): for value in colors[name]: if value > 1.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in " - "your colors " - "tuples cannot " - "exceed 1.0.") + raise exceptions.PlotlyError( + "Whoops! The elements in your colors " + "tuples cannot exceed 1.0." + ) color = FigureFactory._convert_to_RGB_255(colors[name]) color = FigureFactory._label_rgb(color) @@ -2229,21 +2225,16 @@ def create_violin(data, data_header=None, group_header=None, if use_colorscale is False: if isinstance(colors, dict): # validate colors dict choice below - fig = FigureFactory._violin_dict(data, data_header, - group_header, colors, - use_colorscale, - group_stats, - height, width, title) + fig = FigureFactory._violin_dict( + data, data_header, group_header, colors, + use_colorscale, group_stats, height, width, title + ) return fig else: - fig = FigureFactory._violin_no_colorscale(data, - data_header, - group_header, - colors, - use_colorscale, - group_stats, - height, width, - title) + fig = FigureFactory._violin_no_colorscale( + data, data_header, group_header, colors, + use_colorscale, group_stats, height, width, title + ) return fig else: if isinstance(colors, dict): @@ -2260,15 +2251,10 @@ def create_violin(data, data_header=None, group_header=None, raise exceptions.PlotlyError("Your group_stats param " "must be a dictionary.") - fig = FigureFactory._violin_colorscale(data, - data_header, - group_header, - colors, - use_colorscale, - group_stats, - height, - width, - title) + fig = FigureFactory._violin_colorscale( + data, data_header, group_header, colors, use_colorscale, + group_stats, height, width, title + ) return fig @staticmethod From 6bd112bb54da8a6e122725391b50f6bd35bb02b3 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 8 Jun 2016 10:28:26 -0400 Subject: [PATCH 19/27] Moved PLOTLY_SCALES to top of file --- plotly/tools.py | 100 ++++++++++++++++-------------------------------- 1 file changed, 33 insertions(+), 67 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 2451bbfe994..7fbf576dd80 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -28,11 +28,28 @@ 'rgb(227, 119, 194)', 'rgb(127, 127, 127)', 'rgb(188, 189, 34)', 'rgb(23, 190, 207)'] -#color constants for violin plot +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': ['rgb(68,1,84)', 'rgb(253,231,37)']} + +# color constants for violin plot DEFAULT_FILLCOLOR = '#1f77b4' - # Warning format def warning_on_one_line(message, category, filename, lineno, file=None, line=None): @@ -2026,31 +2043,14 @@ def create_violin(data, data_header=None, group_header=None, from plotly.graph_objs import graph_objs from numbers import Number - 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': ['rgb(68,1,84)', 'rgb(253,231,37)']} # Validate colors if colors is None: colors = DEFAULT_PLOTLY_COLORS if isinstance(colors, str): - if colors in plotly_scales: - colors = plotly_scales[colors] + if colors in PLOTLY_SCALES: + colors = PLOTLY_SCALES[colors] elif 'rgb' in colors: # validate rgb color @@ -2079,7 +2079,7 @@ def create_violin(data, data_header=None, group_header=None, colors = colors_list else: - scale_keys = list(plotly_scales.keys()) + scale_keys = list(PLOTLY_SCALES.keys()) raise exceptions.PlotlyError( "If you input a string for 'colors', it must either be " "a Plotly colorscale, an 'rgb' color or a hex color. " @@ -2596,23 +2596,6 @@ def dist_origin(x, y, z): ``` """ from plotly.graph_objs import graph_objs - 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': ['rgb(68,1,84)', 'rgb(253,231,37)']} # Validate colormap if colormap is None: @@ -2622,15 +2605,15 @@ def dist_origin(x, y, z): colormap = FigureFactory._unconvert_from_RGB_255(colormap) if isinstance(colormap, str): - if colormap not in plotly_scales: - scale_keys = list(plotly_scales.keys()) + if colormap not in PLOTLY_SCALES: + scale_keys = list(PLOTLY_SCALES.keys()) raise exceptions.PlotlyError("You must pick a valid " "plotly colorscale " "name from " "{}".format(scale_keys)) - colormap = [plotly_scales[colormap][0], - plotly_scales[colormap][1]] + colormap = [PLOTLY_SCALES[colormap][0], + PLOTLY_SCALES[colormap][1]] colormap = FigureFactory._unlabel_rgb(colormap) colormap = FigureFactory._unconvert_from_RGB_255(colormap) @@ -2952,27 +2935,10 @@ def _scatterplot_theme(dataframe, headers, diag, size, height, width, :raises: (PlotlyError) If palette is not a string or list """ from plotly.graph_objs import graph_objs - 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': ['rgb(68,1,84)', 'rgb(253,231,37)']} # Validate choice of palette if isinstance(palette, str): - if palette not in plotly_scales: + if palette not in PLOTLY_SCALES: raise exceptions.PlotlyError("You must pick a valid " "plotly colorscale name.") else: @@ -2993,8 +2959,8 @@ def _scatterplot_theme(dataframe, headers, diag, size, height, width, # Convert palette to list of n RGB tuples if isinstance(palette, str): - if palette in plotly_scales: - foo = FigureFactory._unlabel_rgb(plotly_scales[palette]) + if palette in PLOTLY_SCALES: + foo = FigureFactory._unlabel_rgb(PLOTLY_SCALES[palette]) foo = FigureFactory._n_colors(foo[0], foo[1], n_colors_len) @@ -3175,9 +3141,9 @@ def _scatterplot_theme(dataframe, headers, diag, size, height, width, # Convert palette to list of n RGB tuples if isinstance(palette, str): - if palette in plotly_scales: + if palette in PLOTLY_SCALES: foo = FigureFactory._unlabel_rgb( - plotly_scales[palette] + PLOTLY_SCALES[palette] ) foo = FigureFactory._n_colors(foo[0], foo[1], @@ -3354,8 +3320,8 @@ def _scatterplot_theme(dataframe, headers, diag, size, height, width, else: # Convert palette to list of 2 RGB tuples if isinstance(palette, str): - if palette in plotly_scales: - theme = plotly_scales[palette] + if palette in PLOTLY_SCALES: + theme = PLOTLY_SCALES[palette] if isinstance(palette, list): if 'rgb' in palette[0]: From 5900590041833ad36e16ee72491dd7a720fa42a9 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 8 Jun 2016 13:32:03 -0400 Subject: [PATCH 20/27] ... --- plotly/tools.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 7fbf576dd80..2f67b47513b 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2033,14 +2033,6 @@ def create_violin(data, data_header=None, group_header=None, py.iplot(fig, filename='Violin Plot with Colorscale') ``` """ - if _numpy_imported is False: - raise ImportError("FigureFactory.create_violin() requires " - "numpy to be imported.") - - if _scipy_imported is False: - raise ImportError("FigureFactory.create_violin() requires " - "scipy to be imported.") - from plotly.graph_objs import graph_objs from numbers import Number @@ -2161,11 +2153,11 @@ def create_violin(data, data_header=None, group_header=None, color = FigureFactory._label_rgb(color) colors[name] = color else: - raise exceptions.PlotlyError("You must input a valid colors " - "choice. Valid types include a " - "plotly scale, rgb, hex or tuple " - "color, a list of any color types " - "or a dictionary.") + raise exceptions.PlotlyError( + "You must input a valid colors choice. Valid types include a " + "plotly scale, rgb, hex or tuple color, a list of any color " + "types or a dictionary." + ) # validate data and choose plot type if group_header is None: From 11da292492d0bfceb2bbcd5ce4c9b1e0d4af1410 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 8 Jun 2016 14:07:13 -0400 Subject: [PATCH 21/27] .. --- plotly/tools.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 2f67b47513b..edf1162479f 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2156,8 +2156,7 @@ def create_violin(data, data_header=None, group_header=None, raise exceptions.PlotlyError( "You must input a valid colors choice. Valid types include a " "plotly scale, rgb, hex or tuple color, a list of any color " - "types or a dictionary." - ) + "types or a dictionary.") # validate data and choose plot type if group_header is None: From 536ab3575868aae9cac3f7fe8926b599f71187ef Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Wed, 8 Jun 2016 16:13:18 -0400 Subject: [PATCH 22/27] wrote method for dealing with different color formats (eg. list, tuple, str, etc) --- plotly/tools.py | 140 +++++++++++++++++------------------------------- 1 file changed, 48 insertions(+), 92 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index edf1162479f..17acf61600d 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2267,6 +2267,30 @@ def _find_intermediate_color(lowcolor, highcolor, intermed): lowcolor[2] + intermed * diff_2) return inter_colors + @staticmethod + def _color_parser(colors, function): + """ + Takes color(s) and a function and applys 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 hasattr(colors[0], '__iter__') or isinstance(colors[0], str): + if isinstance(colors, tuple): + new_color_tuple = ( + function(colors[0]), + function(colors[1]), + function(colors[2]) + ) + return new_color_tuple + for index, color in enumerate(colors): + colors[index] = function(color) + return colors + else: + return function(colors) + @staticmethod def _unconvert_from_RGB_255(colors): """ @@ -2277,24 +2301,11 @@ def _unconvert_from_RGB_255(colors): a value between 0 and 1 """ - if isinstance(colors, tuple): + un_rgb_color = (colors[0]/(255.0), + colors[1]/(255.0), + colors[2]/(255.0)) - un_rgb_color = (colors[0]/(255.0), - colors[1]/(255.0), - colors[2]/(255.0)) - - return un_rgb_color - - if isinstance(colors, list): - un_rgb_colors = [] - for color in colors: - un_rgb_color = (color[0]/(255.0), - color[1]/(255.0), - color[2]/(255.0)) - - un_rgb_colors.append(un_rgb_color) - - return un_rgb_colors + return un_rgb_color @staticmethod def _map_z2color(zval, colormap, vmin, vmax): @@ -3082,7 +3093,6 @@ def _scatterplot_theme(dataframe, headers, diag, size, height, width, c_indx += 1 trace_list.append(unique_index_vals) legend_param += 1 - #return trace_list trace_index = 0 indices = range(1, dim + 1) @@ -3606,22 +3616,10 @@ def _endpts_to_intervals(endpts): @staticmethod def _convert_to_RGB_255(colors): """ - Return a (list of) tuple(s) where each element is multiplied by 255 - - Takes a tuple or a list of tuples where each element of each tuple is - between 0 and 1. Returns the same tuple(s) where each tuple element is - multiplied by 255 + Multiplies each element of a triplet by 255 """ - if isinstance(colors, tuple): - return (colors[0]*255.0, colors[1]*255.0, colors[2]*255.0) - - else: - colors_255 = [] - for color in colors: - rgb_color = (color[0]*255.0, color[1]*255.0, color[2]*255.0) - colors_255.append(rgb_color) - return colors_255 + return (colors[0]*255.0, colors[1]*255.0, colors[2]*255.0) @staticmethod def _n_colors(lowcolor, highcolor, n_colors): @@ -3652,24 +3650,9 @@ def _n_colors(lowcolor, highcolor, n_colors): @staticmethod def _label_rgb(colors): """ - Takes tuple(s) (a, b, c) and returns rgb color(s) 'rgb(a, b, c)' - - Takes either a list or a single color tuple of the form (a, b, c) and - returns the same color(s) with each tuple replaced by a string - 'rgb(a, b, c)' - + Takes tuple (a, b, c) and returns an rgb color 'rgb(a, b, c)' """ - if isinstance(colors, tuple): - return ('rgb(%s, %s, %s)' % (colors[0], colors[1], colors[2])) - else: - colors_label = [] - for color in colors: - color_label = ('rgb(%s, %s, %s)' % (color[0], - color[1], - color[2])) - colors_label.append(color_label) - - return colors_label + return ('rgb(%s, %s, %s)' % (colors[0], colors[1], colors[2])) @staticmethod def _unlabel_rgb(colors): @@ -3680,52 +3663,25 @@ def _unlabel_rgb(colors): such colors and returns the color tuples in tuple(s) (a, b, c) """ - if isinstance(colors, str): - str_vals = '' - for index in range(len(colors)): - try: - float(colors[index]) + 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] - 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]) - if isinstance(colors, list): - unlabelled_colors = [] - for color in colors: - str_vals = '' - for index in range(len(color)): - try: - float(color[index]) - str_vals = str_vals + color[index] - except ValueError: - if (color[index] == ',') or (color[index] == '.'): - str_vals = str_vals + color[index] - - str_vals = str_vals + ',' - numbers = [] + 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 = '' - for char in str_vals: - if char != ',': - str_num = str_num + char - else: - numbers.append(float(str_num)) - str_num = '' - unlabelled_tuple = (numbers[0], numbers[1], numbers[2]) - unlabelled_colors.append(unlabelled_tuple) - - return unlabelled_colors + return (numbers[0], numbers[1], numbers[2]) @staticmethod def create_scatterplotmatrix(df, dataframe=None, headers=None, From 4904ac58244b109dab71d1a5765e0fcda572b376 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 10 Jun 2016 14:09:03 -0400 Subject: [PATCH 23/27] added color_parser function//removed dictionary capbilities --- plotly/tools.py | 261 ++++++++++++++++++++---------------------------- 1 file changed, 111 insertions(+), 150 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 17acf61600d..de6c3ca8d83 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1471,6 +1471,76 @@ class FigureFactory(object): more information and examples of a specific chart type. """ + @staticmethod + def _validate_colors(colors, colortype='tuple'): + """ + Validates color(s) and returns a list of color(s) of a specified type + """ + # needs to be able to convert a tuple of colors to a list of colors + # currently uses index assignment, which doesn't work on a tuple + from numbers import Number + if colors is None: + colors = DEFAULT_PLOTLY_COLORS + + if isinstance(colors, str): + if colors in PLOTLY_SCALES: + colors = PLOTLY_SCALES[colors] + else: + colors = [colors] + + 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, color_type in enumerate(colors): + if 'rgb' in color_type: + color_type = FigureFactory._color_parser( + color_type, FigureFactory._unlabel_rgb + ) + for value in color_type: + if value > 255.0: + raise exceptions.PlotlyError( + "Whoops! The elements in your rgb colors " + "tuples cannot exceed 255.0." + ) + color_type = FigureFactory._color_parser( + color_type, FigureFactory._unconvert_from_RGB_255 + ) + colors[j] = color_type + + if '#' in color_type: + color_type = FigureFactory._color_parser( + color_type, FigureFactory._hex_to_rgb + ) + color_type = FigureFactory._color_parser( + color_type, FigureFactory._unconvert_from_RGB_255 + ) + + colors[j] = color_type + + if isinstance(color_type, tuple): + for value in color_type: + if value > 1.0: + raise exceptions.PlotlyError( + "Whoops! The elements in your colors tuples " + "cannot exceed 1.0." + ) + colors[j] = color_type + + if colortype == 'rgb': + for j, color_type in enumerate(colors): + rgb_color = FigureFactory._color_parser( + color_type, FigureFactory._convert_to_RGB_255 + ) + colors[j] = FigureFactory._color_parser( + rgb_color, FigureFactory._label_rgb + ) + + return colors + @staticmethod def _calc_stats(data): """ @@ -1502,7 +1572,7 @@ def _calc_stats(data): } @staticmethod - def _make_half_violin(x, y, fillcolor=DEFAULT_FILLCOLOR, + def _make_half_violin(x, y, fillcolor='#1f77b4', linecolor='rgb(0, 0, 0)'): """ Produces a sideways probability distribution fig violin plot. @@ -1520,7 +1590,7 @@ def _make_half_violin(x, y, fillcolor=DEFAULT_FILLCOLOR, name='', text=text, fill='tonextx', - fillcolor=DEFAULT_FILLCOLOR, + fillcolor='#1f77b4', line=graph_objs.Line(width=0.5, color=linecolor, shape='spline'), hoverinfo='text', opacity=0.5 @@ -1528,7 +1598,7 @@ def _make_half_violin(x, y, fillcolor=DEFAULT_FILLCOLOR, @staticmethod def _make_violin_rugplot(vals, pdf_max, distance, - color=DEFAULT_FILLCOLOR): + color='#1f77b4'): """ Returns a rugplot fig for a violin plot. """ @@ -1636,7 +1706,7 @@ def _make_YAxis(yaxis_title): return yaxis @staticmethod - def _violinplot(vals, fillcolor=DEFAULT_FILLCOLOR, rugplot=True): + def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): """ Refer to FigureFactory.create_violin() for docstring. """ @@ -1665,9 +1735,9 @@ def _violinplot(vals, fillcolor=DEFAULT_FILLCOLOR, rugplot=True): # range for x values in the plot plot_xrange = [-max_pdf - distance - 0.1, max_pdf + 0.1] plot_data = [FigureFactory._make_half_violin( - -yy, xx, fillcolor=DEFAULT_FILLCOLOR), + -yy, xx, fillcolor='#1f77b4'), FigureFactory._make_half_violin( - yy, xx, fillcolor=DEFAULT_FILLCOLOR), + yy, xx, fillcolor='#1f77b4'), FigureFactory._make_non_outlier_interval(d1, d2), FigureFactory._make_quartiles(q1, q3), FigureFactory._make_median(q2)] @@ -1676,7 +1746,7 @@ def _violinplot(vals, fillcolor=DEFAULT_FILLCOLOR, rugplot=True): vals, max_pdf, distance=distance, - color=DEFAULT_FILLCOLOR) + color='#1f77b4') ) return plot_data, plot_xrange @@ -1777,8 +1847,12 @@ def _violin_colorscale(data, data_header, group_header, colors, print_grid=True) # prepare low and high color for colorscale - lowcolor = FigureFactory._unlabel_rgb(colors[0]) - highcolor = FigureFactory._unlabel_rgb(colors[1]) + lowcolor = FigureFactory._color_parser( + colors[0], FigureFactory._unlabel_rgb + ) + highcolor = FigureFactory._color_parser( + colors[1], FigureFactory._unlabel_rgb + ) # find min and max values in group_stats group_stats_values = [] @@ -2037,126 +2111,7 @@ def create_violin(data, data_header=None, group_header=None, from numbers import Number # Validate colors - if colors is None: - colors = DEFAULT_PLOTLY_COLORS - - if isinstance(colors, str): - if colors in PLOTLY_SCALES: - colors = PLOTLY_SCALES[colors] - - elif 'rgb' in colors: - # validate rgb color - colors = FigureFactory._unlabel_rgb(colors) - for value in colors: - if value > 255.0: - raise exceptions.PlotlyError( - "Whoops! The elements in your rgb colors " - "tuples cannot exceed 255.0." - ) - - colors = FigureFactory._label_rgb(colors) - - # put colors in list - colors_list = [] - colors_list.append(colors) - colors = colors_list - - elif '#' in colors: - colors = FigureFactory._hex_to_rgb(colors) - colors = FigureFactory._label_rgb(colors) - - # put colors in list - colors_list = [] - colors_list.append(colors) - colors = colors_list - - else: - scale_keys = list(PLOTLY_SCALES.keys()) - raise exceptions.PlotlyError( - "If you input a string for 'colors', it must either be " - "a Plotly colorscale, an 'rgb' color or a hex color. " - "Valid plotly colorscale names are {}".format(scale_keys) - ) - - elif isinstance(colors, tuple): - # validate tuple color - for value in colors: - if value > 1.0: - raise exceptions.PlotlyError( - "Whoops! The elements in your colors tuples cannot " - "exceed 1.0." - ) - - colors = FigureFactory._convert_to_RGB_255(colors) - colors = FigureFactory._label_rgb(colors) - - colors_list = [] - colors_list.append(colors) - colors = colors_list - - elif isinstance(colors, list): - new_colormap = [] - for color in colors: - if 'rgb' in color: - color = FigureFactory._unlabel_rgb(color) - for value in color: - if value > 255.0: - raise exceptions.PlotlyError( - "Whoops! The elements in your rgb colors " - "tuples cannot exceed 255.0." - ) - - color = FigureFactory._label_rgb(color) - new_colormap.append(color) - elif '#' in color: - color = FigureFactory._hex_to_rgb(color) - color = FigureFactory._label_rgb(color) - new_colormap.append(color) - elif isinstance(color, tuple): - for value in color: - if value > 1.0: - raise exceptions.PlotlyError( - "Whoops! The elements in your colors " - "tuples cannot exceed 1.0." - ) - - color = FigureFactory._convert_to_RGB_255(color) - color = FigureFactory._label_rgb(color) - new_colormap.append(color) - colors = new_colormap - - elif isinstance(colors, dict): - for name in colors: - if 'rgb' in colors[name]: - color = FigureFactory._unlabel_rgb(colors[name]) - for value in color: - if value > 255.0: - raise exceptions.PlotlyError( - "Whoops! The elements in your rgb colors " - "tuples cannot exceed 255.0." - ) - - elif '#' in colors[name]: - color = FigureFactory._hex_to_rgb(colors[name]) - color = FigureFactory._label_rgb(color) - colors[name] = color - - elif isinstance(colors[name], tuple): - for value in colors[name]: - if value > 1.0: - raise exceptions.PlotlyError( - "Whoops! The elements in your colors " - "tuples cannot exceed 1.0." - ) - - color = FigureFactory._convert_to_RGB_255(colors[name]) - color = FigureFactory._label_rgb(color) - colors[name] = color - else: - raise exceptions.PlotlyError( - "You must input a valid colors choice. Valid types include a " - "plotly scale, rgb, hex or tuple color, a list of any color " - "types or a dictionary.") + valid_colors = FigureFactory._validate_colors(colors, 'rgb') # validate data and choose plot type if group_header is None: @@ -2181,8 +2136,9 @@ def create_violin(data, data_header=None, group_header=None, # call the plotting functions plot_data, plot_xrange = FigureFactory._violinplot( - data, fillcolor=colors[0] + data, fillcolor=valid_colors[0] ) + layout = graph_objs.Layout( title=title, autosize=False, @@ -2200,6 +2156,7 @@ def create_violin(data, data_header=None, group_header=None, fig = graph_objs.Figure(data=graph_objs.Data(plot_data), layout=layout) + return fig else: @@ -2214,26 +2171,26 @@ def create_violin(data, data_header=None, group_header=None, "data for the violin plot.") if use_colorscale is False: - if isinstance(colors, dict): + if isinstance(valid_colors, dict): # validate colors dict choice below fig = FigureFactory._violin_dict( - data, data_header, group_header, colors, + data, data_header, group_header, valid_colors, use_colorscale, group_stats, height, width, title ) return fig else: fig = FigureFactory._violin_no_colorscale( - data, data_header, group_header, colors, + data, data_header, group_header, valid_colors, use_colorscale, group_stats, height, width, title ) - return fig + return fig else: - if isinstance(colors, dict): + if isinstance(valid_colors, dict): raise exceptions.PlotlyError("The colors param cannot be " "a dictionary if you are " "using a colorscale.") - if len(colors) < 2: + if len(valid_colors) < 2: raise exceptions.PlotlyError("colors must be a list with " "at least 2 colors. A " "Plotly scale is allowed.") @@ -2243,8 +2200,8 @@ def create_violin(data, data_header=None, group_header=None, "must be a dictionary.") fig = FigureFactory._violin_colorscale( - data, data_header, group_header, colors, use_colorscale, - group_stats, height, width, title + data, data_header, group_header, valid_colors, + use_colorscale, group_stats, height, width, title ) return fig @@ -2274,22 +2231,26 @@ def _color_parser(colors, function): 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 + 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 """ - if hasattr(colors[0], '__iter__') or isinstance(colors[0], str): + 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 = ( - function(colors[0]), - function(colors[1]), - function(colors[2]) - ) + new_color_tuple = tuple(function(item) for item in colors) return new_color_tuple - for index, color in enumerate(colors): - colors[index] = function(color) - return colors - else: - return function(colors) + + else: + new_color_list = [function(item) for item in colors] + return new_color_list @staticmethod def _unconvert_from_RGB_255(colors): From d0f55cf190fa3daf280985671b1e16c65a556ebe Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 13 Jun 2016 14:00:06 -0400 Subject: [PATCH 24/27] Replaced DEFAULT_FILLCOLOR with fillcolor param in functions --- plotly/tools.py | 165 +++++++++++++++++++++++++----------------------- 1 file changed, 87 insertions(+), 78 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index de6c3ca8d83..d61b343235f 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1476,8 +1476,6 @@ def _validate_colors(colors, colortype='tuple'): """ Validates color(s) and returns a list of color(s) of a specified type """ - # needs to be able to convert a tuple of colors to a list of colors - # currently uses index assignment, which doesn't work on a tuple from numbers import Number if colors is None: colors = DEFAULT_PLOTLY_COLORS @@ -1495,45 +1493,45 @@ def _validate_colors(colors, colortype='tuple'): colors = list(colors) # convert color elements in list to tuple color - for j, color_type in enumerate(colors): - if 'rgb' in color_type: - color_type = FigureFactory._color_parser( - color_type, FigureFactory._unlabel_rgb + for j, each_color in enumerate(colors): + if 'rgb' in each_color: + each_color = FigureFactory._color_parser( + each_color, FigureFactory._unlabel_rgb ) - for value in color_type: + for value in each_color: if value > 255.0: raise exceptions.PlotlyError( "Whoops! The elements in your rgb colors " "tuples cannot exceed 255.0." ) - color_type = FigureFactory._color_parser( - color_type, FigureFactory._unconvert_from_RGB_255 + each_color = FigureFactory._color_parser( + each_color, FigureFactory._unconvert_from_RGB_255 ) - colors[j] = color_type + colors[j] = each_color - if '#' in color_type: - color_type = FigureFactory._color_parser( - color_type, FigureFactory._hex_to_rgb + if '#' in each_color: + each_color = FigureFactory._color_parser( + each_color, FigureFactory._hex_to_rgb ) - color_type = FigureFactory._color_parser( - color_type, FigureFactory._unconvert_from_RGB_255 + each_color = FigureFactory._color_parser( + each_color, FigureFactory._unconvert_from_RGB_255 ) - colors[j] = color_type + colors[j] = each_color - if isinstance(color_type, tuple): - for value in color_type: + 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] = color_type + colors[j] = each_color if colortype == 'rgb': - for j, color_type in enumerate(colors): + for j, each_color in enumerate(colors): rgb_color = FigureFactory._color_parser( - color_type, FigureFactory._convert_to_RGB_255 + each_color, FigureFactory._convert_to_RGB_255 ) colors[j] = FigureFactory._color_parser( rgb_color, FigureFactory._label_rgb @@ -1541,6 +1539,54 @@ def _validate_colors(colors, colortype='tuple'): return colors + @staticmethod + 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] = FigureFactory._color_parser( + colors[key], FigureFactory._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] = FigureFactory._color_parser( + colors[key], FigureFactory._unconvert_from_RGB_255 + ) + + if '#' in colors[key]: + colors[key] = FigureFactory._color_parser( + colors[key], FigureFactory._hex_to_rgb + ) + colors[key] = FigureFactory._color_parser( + colors[key], FigureFactory._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] = FigureFactory._color_parser( + colors[key], FigureFactory._convert_to_RGB_255 + ) + colors[key] = FigureFactory._color_parser( + colors[key], FigureFactory._label_rgb + ) + + return colors + @staticmethod def _calc_stats(data): """ @@ -1590,7 +1636,7 @@ def _make_half_violin(x, y, fillcolor='#1f77b4', name='', text=text, fill='tonextx', - fillcolor='#1f77b4', + fillcolor=fillcolor, line=graph_objs.Line(width=0.5, color=linecolor, shape='spline'), hoverinfo='text', opacity=0.5 @@ -1735,9 +1781,9 @@ def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): # range for x values in the plot plot_xrange = [-max_pdf - distance - 0.1, max_pdf + 0.1] plot_data = [FigureFactory._make_half_violin( - -yy, xx, fillcolor='#1f77b4'), + -yy, xx, fillcolor=fillcolor), FigureFactory._make_half_violin( - yy, xx, fillcolor='#1f77b4'), + yy, xx, fillcolor=fillcolor), FigureFactory._make_non_outlier_interval(d1, d2), FigureFactory._make_quartiles(q1, q3), FigureFactory._make_median(q2)] @@ -1746,7 +1792,7 @@ def _violinplot(vals, fillcolor='#1f77b4', rugplot=True): vals, max_pdf, distance=distance, - color='#1f77b4') + color=fillcolor) ) return plot_data, plot_xrange @@ -2101,7 +2147,7 @@ def create_violin(data, data_header=None, group_header=None, # create violin fig fig = FF.create_violin(df, data_header='Score', group_header='Group', height=600, width=1000, use_colorscale=True, - group_stats=group_stats) + group_stats=group_stats) # plot py.iplot(fig, filename='Violin Plot with Colorscale') @@ -2111,7 +2157,10 @@ def create_violin(data, data_header=None, group_header=None, from numbers import Number # Validate colors - valid_colors = FigureFactory._validate_colors(colors, 'rgb') + if isinstance(colors, dict): + valid_colors = FigureFactory._validate_colors_dict(colors, 'rgb') + else: + valid_colors = FigureFactory._validate_colors(colors, 'rgb') # validate data and choose plot type if group_header is None: @@ -2561,54 +2610,7 @@ def dist_origin(x, y, z): from plotly.graph_objs import graph_objs # Validate colormap - if colormap is None: - colormap = [DEFAULT_PLOTLY_COLORS[0], - DEFAULT_PLOTLY_COLORS[1]] - colormap = FigureFactory._unlabel_rgb(colormap) - colormap = FigureFactory._unconvert_from_RGB_255(colormap) - - if isinstance(colormap, str): - if colormap not in PLOTLY_SCALES: - scale_keys = list(PLOTLY_SCALES.keys()) - raise exceptions.PlotlyError("You must pick a valid " - "plotly colorscale " - "name from " - "{}".format(scale_keys)) - - colormap = [PLOTLY_SCALES[colormap][0], - PLOTLY_SCALES[colormap][1]] - colormap = FigureFactory._unlabel_rgb(colormap) - colormap = FigureFactory._unconvert_from_RGB_255(colormap) - - else: - if not isinstance(colormap, list): - raise exceptions.PlotlyError("If 'colormap' is a list, then " - "its items must be tripets of " - "the form a,b,c or 'rgbx,y,z' " - "where a,b,c are between 0 and " - "1 inclusive and x,y,z are " - "between 0 and 255 inclusive.") - if 'rgb' in colormap[0]: - colormap = FigureFactory._unlabel_rgb(colormap) - for color in colormap: - for index in range(3): - if color[index] > 255.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in your " - "rgb colormap " - "tuples cannot " - "exceed 255.0.") - colormap = FigureFactory._unconvert_from_RGB_255(colormap) - - if isinstance(colormap[0], tuple): - for color in colormap: - for index in range(3): - if color[index] > 1.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in your " - "rgb colormap " - "tuples cannot " - "exceed 1.0.") + colormap = FigureFactory._validate_colors(colormap, 'tuple') data1 = FigureFactory._trisurf(x, y, z, simplices, dist_func=dist_func, @@ -2923,7 +2925,10 @@ def _scatterplot_theme(dataframe, headers, diag, size, height, width, # Convert palette to list of n RGB tuples if isinstance(palette, str): if palette in PLOTLY_SCALES: - foo = FigureFactory._unlabel_rgb(PLOTLY_SCALES[palette]) + foo = FigureFactory._color_parser( + PLOTLY_SCALES[palette], FigureFactory._unlabel_rgb + ) + foo = FigureFactory._n_colors(foo[0], foo[1], n_colors_len) @@ -2931,13 +2936,17 @@ def _scatterplot_theme(dataframe, headers, diag, size, height, width, if isinstance(palette, list): if 'rgb' in palette[0]: - foo = FigureFactory._unlabel_rgb(palette) + foo = FigureFactory._color_parser( + palette, FigureFactory._unlabel_rgb + ) foo = FigureFactory._n_colors(foo[0], foo[1], n_colors_len) theme = FigureFactory._label_rgb(foo) else: - foo = FigureFactory._convert_to_RGB_255(palette) + foo = FigureFactory._color_parser( + palette, FigureFactory._convert_to_RGB_255 + ) foo = FigureFactory._n_colors(foo[0], foo[1], n_colors_len) From d31944866329a39ae9d7cdcb814f85376500be7e Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 13 Jun 2016 16:18:10 -0400 Subject: [PATCH 25/27] Made tests compatible with color_parser code --- .../test_optional/test_figure_factory.py | 58 +++++++------------ plotly/tools.py | 8 ++- 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/plotly/tests/test_optional/test_figure_factory.py b/plotly/tests/test_optional/test_figure_factory.py index 1bb724a6325..afc465f6d04 100644 --- a/plotly/tests/test_optional/test_figure_factory.py +++ b/plotly/tests/test_optional/test_figure_factory.py @@ -784,15 +784,22 @@ def test_valid_colormap(self): tri = Delaunay(points2D) simplices = tri.simplices - # check that a valid plotly colorscale name is entered - self.assertRaises(PlotlyError, tls.FigureFactory.create_trisurf, - x, y, z, simplices, colormap='foo') + # check that a valid plotly colorscale string is entered + + pattern = ( + "If your colors variable is a string, it must be a Plotly scale, " + "an rgb color or a hex color." + ) + + self.assertRaisesRegexp(PlotlyError, pattern, + tls.FigureFactory.create_trisurf, + x, y, z, simplices, colormap='foo') # check: if colormap is a list of rgb color strings, make sure the # entries of each color are no greater than 255.0 pattern2 = ( - "Whoops! The elements in your rgb colormap tuples " + "Whoops! The elements in your rgb colors tuples " "cannot exceed 255.0." ) @@ -805,7 +812,7 @@ def test_valid_colormap(self): # of each tuple are no greater than 1.0 pattern3 = ( - "Whoops! The elements in your colormap tuples cannot exceed 1.0." + "Whoops! The elements in your colors tuples cannot exceed 1.0." ) self.assertRaisesRegexp(PlotlyError, pattern3, @@ -813,18 +820,6 @@ def test_valid_colormap(self): x, y, z, simplices, colormap=[(0.8, 1.0, 1.2)]) - # check: - - pattern4 = ( - "You must input a valid colormap. Valid types include a plotly " - "scale, rgb, hex or tuple color, or lastly a list of any color " - "types." - ) - - self.assertRaisesRegexp(PlotlyError, pattern4, - tls.FigureFactory.create_trisurf, - x, y, z, simplices, colormap=1) - def test_trisurf_all_args(self): # check if trisurf plot matches with expected output @@ -849,14 +844,14 @@ def test_trisurf_all_args(self): exp_trisurf_plot = { 'data': [ { - 'facecolor': ['rgb(143.0, 123.0, 97.0)', - 'rgb(255.0, 127.0, 14.0)', - 'rgb(143.0, 123.0, 97.0)', + 'facecolor': ['rgb(144.0, 94.5, 132.0)', + 'rgb(23.0, 190.0, 207.0)', + 'rgb(144.0, 94.5, 132.0)', 'rgb(31.0, 119.0, 180.0)', - 'rgb(143.0, 123.0, 97.0)', + 'rgb(144.0, 94.5, 132.0)', 'rgb(31.0, 119.0, 180.0)', - 'rgb(143.0, 123.0, 97.0)', - 'rgb(255.0, 127.0, 14.0)'], + 'rgb(144.0, 94.5, 132.0)', + 'rgb(23.0, 190.0, 207.0)'], 'i': [3, 1, 1, 5, 7, 3, 5, 7], 'j': [1, 3, 5, 1, 3, 7, 7, 5], 'k': [4, 0, 4, 2, 4, 6, 4, 8], @@ -1325,15 +1320,6 @@ def test_colors_validation(self): self.assertRaises(PlotlyError, tls.FigureFactory.create_violin, data, colors='foo') - # error message if colors is no good - pattern3 = ("You must input a valid colors choice. Valid types " - "include a plotly scale, rgb, hex or tuple color, a list " - "of any color types or a dictionary.") - - self.assertRaisesRegexp(PlotlyError, pattern3, - tls.FigureFactory.create_violin, - data, colors=5) - def test_data_header(self): # make sure data_header is entered @@ -1451,7 +1437,7 @@ def test_group_stats(self): use_colorscale=True, colors=['rgb(1, 2, 3)', 'rgb(4, 5, 6)'], group_stats={'apple': 1}) -""" + def test_violin_fig(self): # check: test violin fig matches expected fig @@ -1462,7 +1448,7 @@ def test_violin_fig(self): 'data': [{'fill': 'tonextx', 'fillcolor': 'rgb(31.0, 119.0, 180.0)', 'hoverinfo': 'text', - 'line': {'color': 'rgb(0,0,0)', + 'line': {'color': 'rgb(0, 0, 0)', 'shape': 'spline', 'width': 0.5}, 'mode': 'lines', @@ -1640,7 +1626,7 @@ def test_violin_fig(self): {'fill': 'tonextx', 'fillcolor': 'rgb(31.0, 119.0, 180.0)', 'hoverinfo': 'text', - 'line': {'color': 'rgb(0,0,0)', + 'line': {'color': 'rgb(0, 0, 0)', 'shape': 'spline', 'width': 0.5}, 'mode': 'lines', @@ -1880,5 +1866,3 @@ def test_violin_fig(self): self.assert_dict_equal(test_violin['layout'], exp_violin['layout']) - -""" diff --git a/plotly/tools.py b/plotly/tools.py index 35330e2b209..dbf35ecd7da 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1482,12 +1482,18 @@ def _validate_colors(colors, colortype='tuple'): from numbers import Number if colors is None: colors = DEFAULT_PLOTLY_COLORS + #colors = [DEFAULT_PLOTLY_COLORS[0], + # DEFAULT_PLOTLY_COLORS[1]] if isinstance(colors, str): if colors in PLOTLY_SCALES: colors = PLOTLY_SCALES[colors] - else: + if '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): From 303385d72dfb7e46d989b135575b9d5d88c952a3 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Tue, 14 Jun 2016 09:58:36 -0400 Subject: [PATCH 26/27] Replaced new color_parser function into scatterplot_theme --- .../test_optional/test_figure_factory.py | 19 +- plotly/tools.py | 204 +++--------------- 2 files changed, 39 insertions(+), 184 deletions(-) diff --git a/plotly/tests/test_optional/test_figure_factory.py b/plotly/tests/test_optional/test_figure_factory.py index afc465f6d04..ad64fa82fb3 100644 --- a/plotly/tests/test_optional/test_figure_factory.py +++ b/plotly/tests/test_optional/test_figure_factory.py @@ -1052,19 +1052,8 @@ def test_valid_colormap(self): tls.FigureFactory.create_scatterplotmatrix, df, index='a', colormap='fake_scale') - pattern = ( - "You must input a valid colormap. Valid types include a plotly " - "scale, rgb, hex or tuple color, a list of any color types, or a " - "dictionary with index names each assigned to a color." - ) - - # check: accepted data type for colormap - self.assertRaisesRegexp(PlotlyError, pattern, - tls.FigureFactory.create_scatterplotmatrix, - df, colormap=1) - pattern_rgb = ( - "Whoops! The elements in your rgb colormap tuples cannot " + "Whoops! The elements in your rgb colors tuples cannot " "exceed 255.0." ) @@ -1078,7 +1067,7 @@ def test_valid_colormap(self): df, colormap=['rgb(500, 1, 1)'], index='c') pattern_tuple = ( - "Whoops! The elements in your colormap tuples cannot " + "Whoops! The elements in your colors tuples cannot " "exceed 1.0." ) @@ -1243,13 +1232,13 @@ def test_scatter_plot_matrix_kwargs(self): ) exp_scatter_plot_matrix = { - 'data': [{'marker': {'color': 'rgb(128,0,38)'}, + 'data': [{'marker': {'color': 'rgb(128.0, 0.0, 38.0)'}, 'showlegend': False, 'type': 'histogram', 'x': [2, -15, -2, 0], 'xaxis': 'x1', 'yaxis': 'y1'}, - {'marker': {'color': 'rgb(255,255,204)'}, + {'marker': {'color': 'rgb(255.0, 255.0, 204.0)'}, 'showlegend': False, 'type': 'histogram', 'x': [6, 5], diff --git a/plotly/tools.py b/plotly/tools.py index dbf35ecd7da..483fe0329cd 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1482,13 +1482,11 @@ def _validate_colors(colors, colortype='tuple'): from numbers import Number if colors is None: colors = DEFAULT_PLOTLY_COLORS - #colors = [DEFAULT_PLOTLY_COLORS[0], - # DEFAULT_PLOTLY_COLORS[1]] if isinstance(colors, str): if colors in PLOTLY_SCALES: colors = PLOTLY_SCALES[colors] - if 'rgb' in colors or '#' in colors: + elif 'rgb' in colors or '#' in colors: colors = [colors] else: raise exceptions.PlotlyError( @@ -3004,11 +3002,16 @@ def _scatterplot_theme(dataframe, headers, diag, size, height, # Convert colormap to list of n RGB tuples if colormap_type == 'seq': - foo = FigureFactory._unlabel_rgb(colormap) + foo = FigureFactory._color_parser( + colormap, FigureFactory._unlabel_rgb + ) foo = FigureFactory._n_colors(foo[0], foo[1], n_colors_len) - theme = FigureFactory._label_rgb(foo) + theme = FigureFactory._color_parser( + foo, FigureFactory._label_rgb + ) + if colormap_type == 'cat': # leave list of colors the same way theme = colormap @@ -3173,11 +3176,22 @@ def _scatterplot_theme(dataframe, headers, diag, size, height, # Convert colormap to list of n RGB tuples if colormap_type == 'seq': - foo = FigureFactory._unlabel_rgb(colormap) + foo = FigureFactory._color_parser( + colormap, FigureFactory._unlabel_rgb + ) foo = FigureFactory._n_colors(foo[0], foo[1], len(intervals)) - theme = FigureFactory._label_rgb(foo) + theme = FigureFactory._color_parser( + foo, FigureFactory._label_rgb + ) + + + #foo = FigureFactory._unlabel_rgb(colormap) + #foo = FigureFactory._n_colors(foo[0], + # foo[1], + # len(intervals)) + #theme = FigureFactory._label_rgb(foo) if colormap_type == 'cat': # leave list of colors the same way theme = colormap @@ -3908,154 +3922,16 @@ def create_scatterplotmatrix(df, index=None, endpts=None, diag='scatter', headers = [] if index_vals is None: index_vals = [] - 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': ['rgb(68,1,84)', 'rgb(253,231,37)']} FigureFactory._validate_scatterplotmatrix(df, index, diag, colormap_type, **kwargs) # Validate colormap - if colormap is None: - colormap = DEFAULT_PLOTLY_COLORS - - if isinstance(colormap, str): - if colormap in plotly_scales: - colormap = plotly_scales[colormap] - - elif 'rgb' in colormap: - colormap = FigureFactory._unlabel_rgb(colormap) - for value in colormap: - if value > 255.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in your " - "rgb colormap " - "tuples cannot " - "exceed 255.0.") - colormap = FigureFactory._label_rgb(colormap) - - # put colormap in list - colors_list = [] - colors_list.append(colormap) - colormap = colors_list - - elif '#' in colormap: - colormap = FigureFactory._hex_to_rgb(colormap) - colormap = FigureFactory._label_rgb(colormap) - - # put colormap in list - colors_list = [] - colors_list.append(colormap) - colormap = colors_list - - else: - scale_keys = list(plotly_scales.keys()) - raise exceptions.PlotlyError("If you input a string " - "for 'colormap', it must " - "either be a Plotly " - "colorscale, an 'rgb' " - "color or a hex color." - "Valid plotly colorscale " - "names are {}".format(scale_keys)) - elif isinstance(colormap, tuple): - for value in colormap: - if value > 1.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in " - "your colormap " - "tuples cannot " - "exceed 1.0.") - - colors_list = [] - colors_list.append(colormap) - colormap = colors_list - - colormap = FigureFactory._convert_to_RGB_255(colormap) - colormap = FigureFactory._label_rgb(colormap) - - elif isinstance(colormap, list): - new_colormap = [] - for color in colormap: - if 'rgb' in color: - color = FigureFactory._unlabel_rgb(color) - - for value in color: - if value > 255.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in your " - "rgb colormap " - "tuples cannot " - "exceed 255.0.") - - color = FigureFactory._label_rgb(color) - new_colormap.append(color) - elif '#' in color: - color = FigureFactory._hex_to_rgb(color) - color = FigureFactory._label_rgb(color) - new_colormap.append(color) - elif isinstance(color, tuple): - for value in color: - if value > 1.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in " - "your colormap " - "tuples cannot " - "exceed 1.0.") - color = FigureFactory._convert_to_RGB_255(color) - color = FigureFactory._label_rgb(color) - new_colormap.append(color) - colormap = new_colormap - - elif isinstance(colormap, dict): - for name in colormap: - if 'rgb' in colormap[name]: - color = FigureFactory._unlabel_rgb(colormap[name]) - for value in color: - if value > 255.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in your " - "rgb colormap " - "tuples cannot " - "exceed 255.0.") - - elif '#' in colormap[name]: - color = FigureFactory._hex_to_rgb(colormap[name]) - color = FigureFactory._label_rgb(color) - colormap[name] = color - - elif isinstance(colormap[name], tuple): - for value in colormap[name]: - if value > 1.0: - raise exceptions.PlotlyError("Whoops! The " - "elements in " - "your colormap " - "tuples cannot " - "exceed 1.0.") - color = FigureFactory._convert_to_RGB_255(colormap[name]) - color = FigureFactory._label_rgb(color) - colormap[name] = color - + if isinstance(colormap, dict): + colormap = FigureFactory._validate_colors_dict(colormap, 'rgb') else: - raise exceptions.PlotlyError("You must input a valid colormap. " - "Valid types include a plotly scale, " - "rgb, hex or tuple color, a list of " - "any color types, or a dictionary " - "with index names each assigned " - "to a color.") + colormap = FigureFactory._validate_colors(colormap, 'rgb') + if not index: for name in df: headers.append(name) @@ -4094,29 +3970,19 @@ def create_scatterplotmatrix(df, index=None, endpts=None, diag='scatter', "dictionary, all the " "names in the index " "must be keys.") - - figure = FigureFactory._scatterplot_dict(dataframe, - headers, - diag, - size, height, - width, title, - index, - index_vals, - endpts, - colormap, - colormap_type, - **kwargs) + figure = FigureFactory._scatterplot_dict( + dataframe, headers, diag, size, height, width, title, + index, index_vals, endpts, colormap, colormap_type, + **kwargs + ) return figure else: - figure = FigureFactory._scatterplot_theme(dataframe, headers, - diag, size, - height, width, - title, index, - index_vals, - endpts, colormap, - colormap_type, - **kwargs) + figure = FigureFactory._scatterplot_theme( + dataframe, headers, diag, size, height, width, title, + index, index_vals, endpts, colormap, colormap_type, + **kwargs + ) return figure @staticmethod From c24b374fc608f3edd1942e74099622e55c0762ff Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Tue, 14 Jun 2016 11:32:09 -0400 Subject: [PATCH 27/27] Removed commented out code and removed brackets from 'if colors[index] == ','...' line in _unlabel_rgb --- plotly/tools.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 483fe0329cd..910c53b2412 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -3186,12 +3186,6 @@ def _scatterplot_theme(dataframe, headers, diag, size, height, foo, FigureFactory._label_rgb ) - - #foo = FigureFactory._unlabel_rgb(colormap) - #foo = FigureFactory._n_colors(foo[0], - # foo[1], - # len(intervals)) - #theme = FigureFactory._label_rgb(foo) if colormap_type == 'cat': # leave list of colors the same way theme = colormap @@ -3698,7 +3692,7 @@ def _unlabel_rgb(colors): float(colors[index]) str_vals = str_vals + colors[index] except ValueError: - if (colors[index] == ',') or (colors[index] == '.'): + if colors[index] == ',' or colors[index] == '.': str_vals = str_vals + colors[index] str_vals = str_vals + ','