From 3d6dbb924d200ae0963d113c1df72c0e26898bf9 Mon Sep 17 00:00:00 2001 From: Oxana Date: Tue, 28 Jul 2015 15:20:36 +0200 Subject: [PATCH 01/35] Added Dendrogram class and tests --- .../test_tools/test_figure_factory.py | 48 +++++ plotly/tools.py | 169 +++++++++++++++++- 2 files changed, 216 insertions(+), 1 deletion(-) 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 e4852ac816e..cd30fbe0e2f 100644 --- a/plotly/tests/test_core/test_tools/test_figure_factory.py +++ b/plotly/tests/test_core/test_tools/test_figure_factory.py @@ -4,6 +4,8 @@ import datetime from nose.tools import raises +import numpy as np + import plotly.tools as tls from plotly.exceptions import PlotlyError from plotly.graph_objs import graph_objs @@ -802,3 +804,49 @@ def test_datetime_candlestick(self): self.assertEqual(candle, exp_candle) +class TestDendrogram(TestCase): + + def test_default_dendrogram(self): + dendro = tls.TraceFactory.create_dendrogram(X=[[1, 2, 3, 4], + [1, 1, 3, 4], + [1, 2, 1, 4], + [1, 2, 3, 1]]) + expected_dendro_data = [{'marker': {'color': 'rgb(255,133,27)'}, + 'mode': 'lines', 'xaxis': 'xs', + 'yaxis': 'ys', + 'y': np.array([0., 1., 1., 0.]), + 'x': np.array([25., 25., 35., 35.]), + 'type': u'scatter'}, + {'marker': {'color': 'rgb(255,133,27)'}, + 'mode': 'lines', + 'xaxis': 'xs', + 'yaxis': 'ys', + 'y': np.array([0., 2.23606798, 2.23606798, 1.]), + 'x': np.array([15., 15., 30., 30.]), + 'type': u'scatter'}, + {'marker': {'color': 'blue'}, + 'mode': 'lines', + 'xaxis': 'xs', + 'yaxis': 'ys', + 'y': np.array([0., 3.60555128, 3.60555128, 2.23606798]), + 'x': np.array([5., 5., 22.5, 22.5]), 'type': u'scatter'}] + + self.assertEqual(len(dendro.data), len(expected_dendro_data)) + self.assertTrue(np.array_equal(dendro.labels, np.array(['3', '2', '0', '1']))) + + for i in range(1,len(dendro.data)): + self.assertTrue(np.allclose(dendro.data[i]['x'], expected_dendro_data[i]['x'])) + self.assertTrue(np.allclose(dendro.data[i]['y'], expected_dendro_data[i]['y'])) + + def test_dendrogram_random_matrix(self): + # create a random uncorrelated matrix + X = np.random.rand(5,5) + # variable 2 is correlated with all the other variables + X[2,:] = sum(X,0) + + dendro = tls.TraceFactory.create_dendrogram(X) + + # Check that 2 is in a separate cluster + self.assertEqual(dendro.labels[0], '2') + + diff --git a/plotly/tools.py b/plotly/tools.py index 590166906d6..ab857372b78 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -15,14 +15,16 @@ import six import math +import scipy +import scipy.cluster.hierarchy as sch from plotly import utils from plotly import exceptions from plotly import session from plotly.graph_objs import graph_objs -from plotly.graph_objs import Scatter, Marker +from plotly.graph_objs import Scatter, Marker, Line, Data # Warning format @@ -1760,6 +1762,7 @@ def create_streamline(x, y, u, v, y=streamline_y + arrow_y, mode='lines', **kwargs) +<<<<<<< HEAD data = [streamline] layout = graph_objs.Layout(hovermode='closest') @@ -2275,6 +2278,23 @@ def create_candlestick(open, high, low, close, return dict(data=data, layout=layout) + @staticmethod + def create_dendrogram(X, orientation="bottom", labels=None, + colorscale=None, **kwargs): + """ + Returns a dendrogram Plotly figure object. + + X: Heatmap matrix as array of arrays + orientation: 'top', 'right', 'bottom', or 'left' + labels: List of axis category labels + colorscale: Optional colorscale for dendgrogram tree clusters + """ + + #TODO: add validations of input + + dendrogram = _Dendrogram(X, orientation, labels, colorscale) + return dendrogram + class _Quiver(FigureFactory): """ Refer to FigureFactory.create_quiver() for docstring @@ -2690,6 +2710,7 @@ def sum_streamlines(self): streamline_y = sum(self.st_y, []) return streamline_x, streamline_y +<<<<<<< HEAD class _OHLC(FigureFactory): """ @@ -2871,3 +2892,149 @@ def get_candle_decrease(self): return (decrease_x, decrease_close, decrease_dif, stick_decrease_y, stick_decrease_x) +class _Dendrogram(TraceFactory): + ''' Returns a Dendrogram figure object + Example usage: + D = Dendrogram( Z ) + fig = { 'data':D.data, 'layout':D.layout } + py.iplot( fig, filename='Dendro', validate=False )''' + + def __init__(self, X, orientation='bottom', labels=None, colorscale=None, \ + width=700, height=700, xaxis='xaxis', yaxis='yaxis' ): + ''' Draw a 2d dendrogram tree + X: Heatmap matrix as array of arrays + orientation: 'top', 'right', 'bottom', or 'left' + labels: List of axis category labels + colorscale: Optional colorscale for dendgrogram tree clusters + Returns a dendrogram Plotly figure object ''' + + self.orientation = orientation + self.labels = labels + self.xaxis = xaxis + self.yaxis = yaxis + self.data = [] + self.leaves = [] + self.sign = { self.xaxis:1, self.yaxis:1 } + self.layout = { self.xaxis:{}, self.yaxis:{} } + + self.sign[self.xaxis] = 1 if self.orientation in ['left','bottom'] else -1 + self.sign[self.yaxis] = 1 if self.orientation in ['right','bottom'] else -1 + + dd_traces, xvals, yvals, ordered_labels, leaves = self.get_dendrogram_traces( X, colorscale ) + + self.labels = ordered_labels + self.leaves = leaves + yvals_flat = yvals.flatten() + xvals_flat = xvals.flatten() + + self.zero_vals = [] + + for i in range(len(yvals_flat)): + if yvals_flat[i] == 0.0 and xvals_flat[i] not in self.zero_vals: + self.zero_vals.append( xvals_flat[i] ) + + self.zero_vals.sort() + + self.layout = self.set_figure_layout( width, height ) + self.data = Data( dd_traces ) + + def get_color_dict( self, colorscale ): + ''' Return colorscale used for dendrogram tree clusters ''' + + # These are the color codes returned for dendrograms + # We're replacing them with nicer colors + default_colors = {'r':'red','g':'green','b':'blue','c':'cyan',\ + 'm':'magenta','y':'yellow','k':'black','w':'white'} + + if colorscale is None: + colorscale = [ + "rgb(0,116,217)", + "rgb(255,65,54)", + "rgb(133,20,75)", + "rgb(255,133,27)", + "rgb(255,220,0)", + "rgb(61,153,112)"] + for i in range(len(default_colors.keys())): + k = default_colors.keys()[i] + if i < len( colorscale ): + default_colors[k] = colorscale[i] + + return default_colors + + def set_axis_layout( self, axis_key ): + ''' Sets and returns default axis object for dendrogram figure + axis_key: "xaxis", "xaxis1", "yaxis", yaxis1", etc ''' + + axis_defaults = { + 'type': 'linear', + 'ticks': 'inside', + 'mirror': 'allticks', + 'rangemode': 'tozero', + 'showticklabels': True, + 'zeroline': False, + 'showgrid': False, + 'showline': True, + } + + if self.labels != None: + axis_key_labels = self.xaxis + if self.orientation in ['left','right']: + axis_key_labels = self.yaxis + if axis_key_labels not in self.layout: + self.layout[axis_key_labels] = {} + self.layout[axis_key_labels]['tickvals'] = [ea*self.sign[axis_key] for ea in self.zero_vals] + self.layout[axis_key_labels]['ticktext'] = self.labels + self.layout[axis_key_labels]['tickmode'] = 'array' + + self.layout[axis_key].update( axis_defaults ) + + return self.layout[axis_key] + + def set_figure_layout( self, width, height ): + ''' Sets and returns default layout object for dendrogram figure ''' + + self.layout.update({ + 'showlegend':False, + 'autoscale':False, + 'hovermode':'closest', + 'width':width, + 'width':height + }) + + self.set_axis_layout(self.xaxis) + self.set_axis_layout(self.yaxis) + + return self.layout + + def get_dendrogram_traces( self, X, colorscale ): + ''' Returns a tuple with: + (a) List of Plotly trace objects for the dendrogram tree + (b) icoord: All X points of the dendogram tree as array of arrays with lenght 4 + (c) dcoord: All Y points of the dendogram tree as array of arrays with lenght 4 ''' + + d = sch.distance.pdist(X) + Z = sch.linkage(d, method='complete') + P = sch.dendrogram(Z,orientation=self.orientation,labels=self.labels, no_plot=True) + + icoord = scipy.array( P['icoord'] ) + dcoord = scipy.array( P['dcoord'] ) + ordered_labels = scipy.array( P['ivl'] ) + color_list = scipy.array( P['color_list'] ) + colors = self.get_color_dict( colorscale ) + + trace_list = [] + + for i in range(len(icoord)): + # xs and ys are arrays of 4 points that make up the '∩' shapes of the dendrogram tree + xs = icoord[i] if self.orientation in ['top','bottom'] else dcoord[i] + ys = dcoord[i] if self.orientation in ['top','bottom'] else icoord[i] + color_key = color_list[i] + trace = Scatter(x=np.multiply(self.sign[self.xaxis],xs), \ + y=np.multiply(self.sign[self.yaxis],ys), \ + mode='lines', marker=Marker(color=colors[color_key]) ) + trace['xaxis'] = 'x'+self.xaxis[-1] + trace['yaxis'] = 'y'+self.yaxis[-1] + trace_list.append( trace ) + + return trace_list, icoord, dcoord, ordered_labels, P['leaves'] + From 2d9b88c869cc01318ead8648cbb121c26867d939 Mon Sep 17 00:00:00 2001 From: Oxana Date: Tue, 4 Aug 2015 12:42:38 +0200 Subject: [PATCH 02/35] Added a small validator --- plotly/tools.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index ab857372b78..09850dd45b3 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1762,7 +1762,6 @@ def create_streamline(x, y, u, v, y=streamline_y + arrow_y, mode='lines', **kwargs) -<<<<<<< HEAD data = [streamline] layout = graph_objs.Layout(hovermode='closest') @@ -2290,7 +2289,9 @@ def create_dendrogram(X, orientation="bottom", labels=None, colorscale: Optional colorscale for dendgrogram tree clusters """ - #TODO: add validations of input + s = X.shape + if len(s) != 2: + exceptions.PlotlyError("X should be 2-dimensional array.") dendrogram = _Dendrogram(X, orientation, labels, colorscale) return dendrogram From 819e41e19c88869e028f3014720de7ff81ab7a9c Mon Sep 17 00:00:00 2001 From: Oxana Date: Tue, 4 Aug 2015 13:04:17 +0200 Subject: [PATCH 03/35] updates after renaming --- plotly/tests/test_core/test_tools/test_figure_factory.py | 6 +++--- plotly/tools.py | 4 +--- 2 files changed, 4 insertions(+), 6 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 cd30fbe0e2f..2ed4e47972c 100644 --- a/plotly/tests/test_core/test_tools/test_figure_factory.py +++ b/plotly/tests/test_core/test_tools/test_figure_factory.py @@ -807,10 +807,10 @@ def test_datetime_candlestick(self): class TestDendrogram(TestCase): def test_default_dendrogram(self): - dendro = tls.TraceFactory.create_dendrogram(X=[[1, 2, 3, 4], + dendro = tls.FigureFactory.create_dendrogram(X=np.array([[1, 2, 3, 4], [1, 1, 3, 4], [1, 2, 1, 4], - [1, 2, 3, 1]]) + [1, 2, 3, 1]])) expected_dendro_data = [{'marker': {'color': 'rgb(255,133,27)'}, 'mode': 'lines', 'xaxis': 'xs', 'yaxis': 'ys', @@ -844,7 +844,7 @@ def test_dendrogram_random_matrix(self): # variable 2 is correlated with all the other variables X[2,:] = sum(X,0) - dendro = tls.TraceFactory.create_dendrogram(X) + dendro = tls.FigureFactory.create_dendrogram(X) # Check that 2 is in a separate cluster self.assertEqual(dendro.labels[0], '2') diff --git a/plotly/tools.py b/plotly/tools.py index 09850dd45b3..1eae7355b3c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2711,8 +2711,6 @@ def sum_streamlines(self): streamline_y = sum(self.st_y, []) return streamline_x, streamline_y -<<<<<<< HEAD - class _OHLC(FigureFactory): """ Refer to FigureFactory.create_ohlc_increase() for docstring. @@ -2893,7 +2891,7 @@ def get_candle_decrease(self): return (decrease_x, decrease_close, decrease_dif, stick_decrease_y, stick_decrease_x) -class _Dendrogram(TraceFactory): +class _Dendrogram(FigureFactory): ''' Returns a Dendrogram figure object Example usage: D = Dendrogram( Z ) From 378190691dd9fec92e62a232c1f4d5385c71085e Mon Sep 17 00:00:00 2001 From: Oxana Date: Wed, 12 Aug 2015 14:32:55 +0200 Subject: [PATCH 04/35] Moved dendrogram tests to optional --- .../test_tools/test_figure_factory.py | 49 ------------------- .../test_optional/test_opt_tracefactory.py | 44 +++++++++++++++++ 2 files changed, 44 insertions(+), 49 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 2ed4e47972c..e3cdb357e8e 100644 --- a/plotly/tests/test_core/test_tools/test_figure_factory.py +++ b/plotly/tests/test_core/test_tools/test_figure_factory.py @@ -3,9 +3,6 @@ import datetime from nose.tools import raises - -import numpy as np - import plotly.tools as tls from plotly.exceptions import PlotlyError from plotly.graph_objs import graph_objs @@ -804,49 +801,3 @@ def test_datetime_candlestick(self): self.assertEqual(candle, exp_candle) -class TestDendrogram(TestCase): - - def test_default_dendrogram(self): - dendro = tls.FigureFactory.create_dendrogram(X=np.array([[1, 2, 3, 4], - [1, 1, 3, 4], - [1, 2, 1, 4], - [1, 2, 3, 1]])) - expected_dendro_data = [{'marker': {'color': 'rgb(255,133,27)'}, - 'mode': 'lines', 'xaxis': 'xs', - 'yaxis': 'ys', - 'y': np.array([0., 1., 1., 0.]), - 'x': np.array([25., 25., 35., 35.]), - 'type': u'scatter'}, - {'marker': {'color': 'rgb(255,133,27)'}, - 'mode': 'lines', - 'xaxis': 'xs', - 'yaxis': 'ys', - 'y': np.array([0., 2.23606798, 2.23606798, 1.]), - 'x': np.array([15., 15., 30., 30.]), - 'type': u'scatter'}, - {'marker': {'color': 'blue'}, - 'mode': 'lines', - 'xaxis': 'xs', - 'yaxis': 'ys', - 'y': np.array([0., 3.60555128, 3.60555128, 2.23606798]), - 'x': np.array([5., 5., 22.5, 22.5]), 'type': u'scatter'}] - - self.assertEqual(len(dendro.data), len(expected_dendro_data)) - self.assertTrue(np.array_equal(dendro.labels, np.array(['3', '2', '0', '1']))) - - for i in range(1,len(dendro.data)): - self.assertTrue(np.allclose(dendro.data[i]['x'], expected_dendro_data[i]['x'])) - self.assertTrue(np.allclose(dendro.data[i]['y'], expected_dendro_data[i]['y'])) - - def test_dendrogram_random_matrix(self): - # create a random uncorrelated matrix - X = np.random.rand(5,5) - # variable 2 is correlated with all the other variables - X[2,:] = sum(X,0) - - dendro = tls.FigureFactory.create_dendrogram(X) - - # Check that 2 is in a separate cluster - self.assertEqual(dendro.labels[0], '2') - - diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index 2c87c20c232..dd54d2277ad 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -105,3 +105,47 @@ def test_simple_streamline(self): self.assertListEqual(strln['data'][0]['x'][0:100], expected_strln_0_100['x']) +class TestDendrogram(TestCase): + + def test_default_dendrogram(self): + dendro = tls.FigureFactory.create_dendrogram(X=np.array([[1, 2, 3, 4], + [1, 1, 3, 4], + [1, 2, 1, 4], + [1, 2, 3, 1]])) + expected_dendro_data = [{'marker': {'color': 'rgb(255,133,27)'}, + 'mode': 'lines', 'xaxis': 'xs', + 'yaxis': 'ys', + 'y': np.array([0., 1., 1., 0.]), + 'x': np.array([25., 25., 35., 35.]), + 'type': u'scatter'}, + {'marker': {'color': 'rgb(255,133,27)'}, + 'mode': 'lines', + 'xaxis': 'xs', + 'yaxis': 'ys', + 'y': np.array([0., 2.23606798, 2.23606798, 1.]), + 'x': np.array([15., 15., 30., 30.]), + 'type': u'scatter'}, + {'marker': {'color': 'blue'}, + 'mode': 'lines', + 'xaxis': 'xs', + 'yaxis': 'ys', + 'y': np.array([0., 3.60555128, 3.60555128, 2.23606798]), + 'x': np.array([5., 5., 22.5, 22.5]), 'type': u'scatter'}] + + self.assertEqual(len(dendro.data), len(expected_dendro_data)) + self.assertTrue(np.array_equal(dendro.labels, np.array(['3', '2', '0', '1']))) + + for i in range(1,len(dendro.data)): + self.assertTrue(np.allclose(dendro.data[i]['x'], expected_dendro_data[i]['x'])) + self.assertTrue(np.allclose(dendro.data[i]['y'], expected_dendro_data[i]['y'])) + + def test_dendrogram_random_matrix(self): + # create a random uncorrelated matrix + X = np.random.rand(5,5) + # variable 2 is correlated with all the other variables + X[2,:] = sum(X,0) + + dendro = tls.FigureFactory.create_dendrogram(X) + + # Check that 2 is in a separate cluster + self.assertEqual(dendro.labels[0], '2') From 31efa33c29e0ce4ad02a03b4fecd7ab8fa5683c7 Mon Sep 17 00:00:00 2001 From: Oxana Date: Wed, 12 Aug 2015 14:53:14 +0200 Subject: [PATCH 05/35] Protected scipy imports --- plotly/tools.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 1eae7355b3c..4ab04b494f4 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -15,9 +15,6 @@ import six import math -import scipy - -import scipy.cluster.hierarchy as sch from plotly import utils from plotly import exceptions @@ -53,6 +50,15 @@ def warning_on_one_line(message, category, filename, lineno, except ImportError: _numpy_imported = False +try: + import scipy as scp + import scipy.spatial as scs + import scipy.cluster.hierarchy as sch + + _scipy_imported = True +except ImportError: + _scipy_imported = False + PLOTLY_DIR = os.path.join(os.path.expanduser("~"), ".plotly") CREDENTIALS_FILE = os.path.join(PLOTLY_DIR, ".credentials") CONFIG_FILE = os.path.join(PLOTLY_DIR, ".config") @@ -2288,6 +2294,8 @@ def create_dendrogram(X, orientation="bottom", labels=None, labels: List of axis category labels colorscale: Optional colorscale for dendgrogram tree clusters """ + if _scipy_imported is False: + raise ImportError("FigureFactory.create_dendrogram requires scipy, scipy.spatial and scipy.hierarchy") s = X.shape if len(s) != 2: @@ -3011,14 +3019,14 @@ def get_dendrogram_traces( self, X, colorscale ): (b) icoord: All X points of the dendogram tree as array of arrays with lenght 4 (c) dcoord: All Y points of the dendogram tree as array of arrays with lenght 4 ''' - d = sch.distance.pdist(X) + d = scs.distance.pdist(X) Z = sch.linkage(d, method='complete') P = sch.dendrogram(Z,orientation=self.orientation,labels=self.labels, no_plot=True) - icoord = scipy.array( P['icoord'] ) - dcoord = scipy.array( P['dcoord'] ) - ordered_labels = scipy.array( P['ivl'] ) - color_list = scipy.array( P['color_list'] ) + icoord = scp.array( P['icoord'] ) + dcoord = scp.array( P['dcoord'] ) + ordered_labels = scp.array( P['ivl'] ) + color_list = scp.array( P['color_list'] ) colors = self.get_color_dict( colorscale ) trace_list = [] From 02beb6ce250fd963d5c86723256c38deb1ba7bca Mon Sep 17 00:00:00 2001 From: Oxana Date: Wed, 12 Aug 2015 15:36:09 +0200 Subject: [PATCH 06/35] Fixed typos, docstring and return value --- .../test_optional/test_opt_tracefactory.py | 12 +++--- plotly/tools.py | 39 +++++++++++++++---- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index dd54d2277ad..5652b10959f 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -132,12 +132,12 @@ def test_default_dendrogram(self): 'y': np.array([0., 3.60555128, 3.60555128, 2.23606798]), 'x': np.array([5., 5., 22.5, 22.5]), 'type': u'scatter'}] - self.assertEqual(len(dendro.data), len(expected_dendro_data)) - self.assertTrue(np.array_equal(dendro.labels, np.array(['3', '2', '0', '1']))) + self.assertEqual(len(dendro['data']), len(expected_dendro_data)) + self.assertTrue(np.array_equal(dendro['labels'], np.array(['3', '2', '0', '1']))) - for i in range(1,len(dendro.data)): - self.assertTrue(np.allclose(dendro.data[i]['x'], expected_dendro_data[i]['x'])) - self.assertTrue(np.allclose(dendro.data[i]['y'], expected_dendro_data[i]['y'])) + for i in range(1,len(dendro['data'])): + self.assertTrue(np.allclose(dendro['data'][i]['x'], expected_dendro_data[i]['x'])) + self.assertTrue(np.allclose(dendro['data'][i]['y'], expected_dendro_data[i]['y'])) def test_dendrogram_random_matrix(self): # create a random uncorrelated matrix @@ -148,4 +148,4 @@ def test_dendrogram_random_matrix(self): dendro = tls.FigureFactory.create_dendrogram(X) # Check that 2 is in a separate cluster - self.assertEqual(dendro.labels[0], '2') + self.assertEqual(dendro['labels'][0], '2') diff --git a/plotly/tools.py b/plotly/tools.py index 4ab04b494f4..d42fff16545 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -558,7 +558,7 @@ def get_subplots(rows=1, columns=1, print_grid=False, **kwargs): y_start = (plot_height + vertical_spacing) * rrr y_end = y_start + plot_height - xaxis = graph_objs.XAxis(domain=[x_start, x_end], anchor=x_anchor) + xaxis = graph_objs.Axis(domain=[x_start, x_end], anchor=x_anchor) fig['layout'][xaxis_name] = xaxis yaxis = graph_objs.YAxis(domain=[y_start, y_end], anchor=y_anchor) fig['layout'][yaxis_name] = yaxis @@ -2292,8 +2292,32 @@ def create_dendrogram(X, orientation="bottom", labels=None, X: Heatmap matrix as array of arrays orientation: 'top', 'right', 'bottom', or 'left' labels: List of axis category labels - colorscale: Optional colorscale for dendgrogram tree clusters + colorscale: Optional colorscale for dendrogram tree clusters + + + Example 1: Simple bottom oriented dendrogram + ``` + import plotly.plotly as py + from plotly.tools import FigureFactory as FF + + import numpy as np + + X = np.random.rand(5,5) + dendro_X = FF.create_dendrogram(X) + py.iplot(dendro_X, validate=False, height=300, width=1000) + + ``` + + Example 2: Dendrogram to put on the left of the heatmap + ``` + X = np.random.rand(5,5) + names_X = ['Jack', 'Oxana', 'John', 'Chelsea', 'Mark'] + dendro_X = FF.create_dendrogram(X, orientation='right', labels=names_X) + + py.iplot(dendro_X, validate=False, height=1000, width=300) + ``` """ + if _scipy_imported is False: raise ImportError("FigureFactory.create_dendrogram requires scipy, scipy.spatial and scipy.hierarchy") @@ -2302,7 +2326,8 @@ def create_dendrogram(X, orientation="bottom", labels=None, exceptions.PlotlyError("X should be 2-dimensional array.") dendrogram = _Dendrogram(X, orientation, labels, colorscale) - return dendrogram + + return {'layout': dendrogram.layout, 'data': dendrogram.data, 'labels': dendrogram.labels} class _Quiver(FigureFactory): """ @@ -2912,7 +2937,7 @@ def __init__(self, X, orientation='bottom', labels=None, colorscale=None, \ X: Heatmap matrix as array of arrays orientation: 'top', 'right', 'bottom', or 'left' labels: List of axis category labels - colorscale: Optional colorscale for dendgrogram tree clusters + colorscale: Optional colorscale for dendrogram tree clusters Returns a dendrogram Plotly figure object ''' self.orientation = orientation @@ -3016,12 +3041,12 @@ def set_figure_layout( self, width, height ): def get_dendrogram_traces( self, X, colorscale ): ''' Returns a tuple with: (a) List of Plotly trace objects for the dendrogram tree - (b) icoord: All X points of the dendogram tree as array of arrays with lenght 4 - (c) dcoord: All Y points of the dendogram tree as array of arrays with lenght 4 ''' + (b) icoord: All X points of the dendogram tree as array of arrays with length 4 + (c) dcoord: All Y points of the dendogram tree as array of arrays with length 4 ''' d = scs.distance.pdist(X) Z = sch.linkage(d, method='complete') - P = sch.dendrogram(Z,orientation=self.orientation,labels=self.labels, no_plot=True) + P = sch.dendrogram(Z, orientation=self.orientation,labels=self.labels, no_plot=True) icoord = scp.array( P['icoord'] ) dcoord = scp.array( P['dcoord'] ) From bfd3cca44f35ad5f456306eba20dfb7416d28f13 Mon Sep 17 00:00:00 2001 From: Oxana Date: Wed, 12 Aug 2015 15:37:24 +0200 Subject: [PATCH 07/35] Typo --- plotly/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index d42fff16545..9ed1ad6d923 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -558,7 +558,7 @@ def get_subplots(rows=1, columns=1, print_grid=False, **kwargs): y_start = (plot_height + vertical_spacing) * rrr y_end = y_start + plot_height - xaxis = graph_objs.Axis(domain=[x_start, x_end], anchor=x_anchor) + xaxis = graph_objs.XAxis(domain=[x_start, x_end], anchor=x_anchor) fig['layout'][xaxis_name] = xaxis yaxis = graph_objs.YAxis(domain=[y_start, y_end], anchor=y_anchor) fig['layout'][yaxis_name] = yaxis From ff98cb7c71241809a6f6e3dcaeb1a3354841b357 Mon Sep 17 00:00:00 2001 From: Oxana Date: Sat, 15 Aug 2015 15:17:15 +0200 Subject: [PATCH 08/35] Fixing default colors mapping --- plotly/tools.py | 50 +++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 9ed1ad6d923..10609641094 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -8,12 +8,12 @@ """ from __future__ import absolute_import +from collections import OrderedDict import os.path import warnings import six - import math from plotly import utils @@ -2963,37 +2963,47 @@ def __init__(self, X, orientation='bottom', labels=None, colorscale=None, \ for i in range(len(yvals_flat)): if yvals_flat[i] == 0.0 and xvals_flat[i] not in self.zero_vals: - self.zero_vals.append( xvals_flat[i] ) + self.zero_vals.append(xvals_flat[i]) - self.zero_vals.sort() + self.zero_vals.sort() - self.layout = self.set_figure_layout( width, height ) - self.data = Data( dd_traces ) + self.layout = self.set_figure_layout(width, height) + self.data = Data(dd_traces) - def get_color_dict( self, colorscale ): + def get_color_dict(self, colorscale): ''' Return colorscale used for dendrogram tree clusters ''' - + # These are the color codes returned for dendrograms # We're replacing them with nicer colors - default_colors = {'r':'red','g':'green','b':'blue','c':'cyan',\ - 'm':'magenta','y':'yellow','k':'black','w':'white'} - + d = {'r': 'red', + 'g': 'green', + 'b': 'blue', + 'c': 'cyan', + 'm': 'magenta', + 'y': 'yellow', + 'k': 'black', + 'w': 'white'} + default_colors = OrderedDict(sorted(d.items(), key=lambda t: t[0])) + if colorscale is None: colorscale = [ - "rgb(0,116,217)", - "rgb(255,65,54)", - "rgb(133,20,75)", - "rgb(255,133,27)", - "rgb(255,220,0)", - "rgb(61,153,112)"] + "rgb(0,116,217)", # blue + "rgb(35,205,205)", # cyan + "rgb(61,153,112)", # green + "rgb(40,35,35)", # black + "rgb(133,20,75)", # magenta + "rgb(255,65,54)", # red + "rgb(255,255,255)", # white + "rgb(255,220,0)"] # yellow + for i in range(len(default_colors.keys())): - k = default_colors.keys()[i] - if i < len( colorscale ): - default_colors[k] = colorscale[i] + k = default_colors.keys()[i] + if i < len(colorscale): + default_colors[k] = colorscale[i] return default_colors - def set_axis_layout( self, axis_key ): + def set_axis_layout(self, axis_key): ''' Sets and returns default axis object for dendrogram figure axis_key: "xaxis", "xaxis1", "yaxis", yaxis1", etc ''' From 4afc8c99c6a4b1e6585a37ecacc439bfed4027d5 Mon Sep 17 00:00:00 2001 From: Oxana Date: Sat, 15 Aug 2015 15:39:46 +0200 Subject: [PATCH 09/35] Minor styling changes and fixes --- plotly/tools.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 10609641094..3dee489a06f 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2932,8 +2932,8 @@ class _Dendrogram(FigureFactory): py.iplot( fig, filename='Dendro', validate=False )''' def __init__(self, X, orientation='bottom', labels=None, colorscale=None, \ - width=700, height=700, xaxis='xaxis', yaxis='yaxis' ): - ''' Draw a 2d dendrogram tree + width="100%", height="100%", xaxis='xaxis', yaxis='yaxis' ): + ''' Draw a 2d dendrogram tree X: Heatmap matrix as array of arrays orientation: 'top', 'right', 'bottom', or 'left' labels: List of axis category labels @@ -3009,7 +3009,7 @@ def set_axis_layout(self, axis_key): axis_defaults = { 'type': 'linear', - 'ticks': 'inside', + 'ticks': 'outside', 'mirror': 'allticks', 'rangemode': 'tozero', 'showticklabels': True, @@ -3018,9 +3018,9 @@ def set_axis_layout(self, axis_key): 'showline': True, } - if self.labels != None: + if len(self.labels) != 0: axis_key_labels = self.xaxis - if self.orientation in ['left','right']: + if self.orientation in ['left', 'right']: axis_key_labels = self.yaxis if axis_key_labels not in self.layout: self.layout[axis_key_labels] = {} @@ -3028,19 +3028,19 @@ def set_axis_layout(self, axis_key): self.layout[axis_key_labels]['ticktext'] = self.labels self.layout[axis_key_labels]['tickmode'] = 'array' - self.layout[axis_key].update( axis_defaults ) + self.layout[axis_key].update(axis_defaults) return self.layout[axis_key] - def set_figure_layout( self, width, height ): + def set_figure_layout(self, width, height): ''' Sets and returns default layout object for dendrogram figure ''' self.layout.update({ - 'showlegend':False, - 'autoscale':False, - 'hovermode':'closest', - 'width':width, - 'width':height + 'showlegend': False, + 'autoscale': False, + 'hovermode': 'closest', + 'width': width, + 'width': height }) self.set_axis_layout(self.xaxis) From 57e15bdffbd5f4ffd00d0e349e8346116ab4ee56 Mon Sep 17 00:00:00 2001 From: Oxana Date: Sat, 15 Aug 2015 16:57:17 +0200 Subject: [PATCH 10/35] pep8 and docsrings --- plotly/tools.py | 143 ++++++++++++++++++++++++++++++------------------ 1 file changed, 91 insertions(+), 52 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 3dee489a06f..b53556739c8 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2285,9 +2285,15 @@ def create_candlestick(open, high, low, close, @staticmethod def create_dendrogram(X, orientation="bottom", labels=None, - colorscale=None, **kwargs): + colorscale=None): """ - Returns a dendrogram Plotly figure object. + BETA function that returns a dendrogram Plotly figure object. + + :param (ndarray) X: Matrix of observations as arrray of arrays + :param (str) orientation: 'top', 'right', 'bottom', or 'left' + :param (list) labels: List of axis category labels(observation labels) + :param (list) colorscale: Optional colorscale for dendrogram tree + clusters X: Heatmap matrix as array of arrays orientation: 'top', 'right', 'bottom', or 'left' @@ -2302,7 +2308,7 @@ def create_dendrogram(X, orientation="bottom", labels=None, import numpy as np - X = np.random.rand(5,5) + X = np.random.rand(5,5) dendro_X = FF.create_dendrogram(X) py.iplot(dendro_X, validate=False, height=300, width=1000) @@ -2319,7 +2325,8 @@ def create_dendrogram(X, orientation="bottom", labels=None, """ if _scipy_imported is False: - raise ImportError("FigureFactory.create_dendrogram requires scipy, scipy.spatial and scipy.hierarchy") + raise ImportError("FigureFactory.create_dendrogram requires scipy, + scipy.spatial and scipy.hierarchy") s = X.shape if len(s) != 2: @@ -2327,7 +2334,10 @@ def create_dendrogram(X, orientation="bottom", labels=None, dendrogram = _Dendrogram(X, orientation, labels, colorscale) - return {'layout': dendrogram.layout, 'data': dendrogram.data, 'labels': dendrogram.labels} + return {'layout': dendrogram.layout, + 'data': dendrogram.data, + 'labels': dendrogram.labels} + class _Quiver(FigureFactory): """ @@ -2925,34 +2935,34 @@ def get_candle_decrease(self): stick_decrease_y, stick_decrease_x) class _Dendrogram(FigureFactory): - ''' Returns a Dendrogram figure object - Example usage: - D = Dendrogram( Z ) - fig = { 'data':D.data, 'layout':D.layout } - py.iplot( fig, filename='Dendro', validate=False )''' + """ + Refer to FigureFactory.create_dendrogram() for docstring. + """ + def __init__(self, X, orientation='bottom', labels=None, colorscale=None, \ width="100%", height="100%", xaxis='xaxis', yaxis='yaxis' ): - ''' Draw a 2d dendrogram tree - X: Heatmap matrix as array of arrays - orientation: 'top', 'right', 'bottom', or 'left' - labels: List of axis category labels - colorscale: Optional colorscale for dendrogram tree clusters - Returns a dendrogram Plotly figure object ''' - self.orientation = orientation self.labels = labels self.xaxis = xaxis self.yaxis = yaxis self.data = [] self.leaves = [] - self.sign = { self.xaxis:1, self.yaxis:1 } - self.layout = { self.xaxis:{}, self.yaxis:{} } - - self.sign[self.xaxis] = 1 if self.orientation in ['left','bottom'] else -1 - self.sign[self.yaxis] = 1 if self.orientation in ['right','bottom'] else -1 + self.sign = {self.xaxis: 1, self.yaxis: 1} + self.layout = {self.xaxis: {}, self.yaxis: {}} + + if self.orientation in ['left', 'bottom']: + self.sign[self.xaxis] = 1 + else: + self.sign[self.xaxis] = -1 + + if self.orientation in ['right', 'bottom']: + self.sign[self.yaxis] = 1 + else: + self.sign[self.yaxis] = -1 - dd_traces, xvals, yvals, ordered_labels, leaves = self.get_dendrogram_traces( X, colorscale ) + dd_traces, xvals, yvals, + ordered_labels, leaves = self.get_dendrogram_traces(X, colorscale) self.labels = ordered_labels self.leaves = leaves @@ -2971,7 +2981,11 @@ def __init__(self, X, orientation='bottom', labels=None, colorscale=None, \ self.data = Data(dd_traces) def get_color_dict(self, colorscale): - ''' Return colorscale used for dendrogram tree clusters ''' + """ + Returns colorscale used for dendrogram tree clusters + :param (list) colorscale: colors to use for the plot, + in rgb format + """ # These are the color codes returned for dendrograms # We're replacing them with nicer colors @@ -3004,9 +3018,11 @@ def get_color_dict(self, colorscale): return default_colors def set_axis_layout(self, axis_key): - ''' Sets and returns default axis object for dendrogram figure - axis_key: "xaxis", "xaxis1", "yaxis", yaxis1", etc ''' - + """ + Sets and returns default axis object for dendrogram figure + :param (str) axis_key: "xaxis", "xaxis1", "yaxis", yaxis1", etc. + """ + axis_defaults = { 'type': 'linear', 'ticks': 'outside', @@ -3024,7 +3040,8 @@ def set_axis_layout(self, axis_key): axis_key_labels = self.yaxis if axis_key_labels not in self.layout: self.layout[axis_key_labels] = {} - self.layout[axis_key_labels]['tickvals'] = [ea*self.sign[axis_key] for ea in self.zero_vals] + self.layout[axis_key_labels]['tickvals'] = [ea*self.sign[axis_key] + for ea in self.zero_vals] self.layout[axis_key_labels]['ticktext'] = self.labels self.layout[axis_key_labels]['tickmode'] = 'array' @@ -3033,11 +3050,13 @@ def set_axis_layout(self, axis_key): return self.layout[axis_key] def set_figure_layout(self, width, height): - ''' Sets and returns default layout object for dendrogram figure ''' + """ + Sets and returns default layout object for dendrogram figure + """ self.layout.update({ 'showlegend': False, - 'autoscale': False, + 'autoscale': False, 'hovermode': 'closest', 'width': width, 'width': height @@ -3048,35 +3067,55 @@ def set_figure_layout(self, width, height): return self.layout - def get_dendrogram_traces( self, X, colorscale ): - ''' Returns a tuple with: - (a) List of Plotly trace objects for the dendrogram tree - (b) icoord: All X points of the dendogram tree as array of arrays with length 4 - (c) dcoord: All Y points of the dendogram tree as array of arrays with length 4 ''' + def get_dendrogram_traces(self, X, colorscale): + """ + Calculates all the elements needed for plotting a dendrogram + + :rtype (tuple): Contains all the traces in the following order + (a) trace_list: List of Plotly trace objects for the dendrogram + tree + (b) icoord: All X points of the dendogram tree as array of arrays + with length 4 + (c) dcoord: All Y points of the dendogram tree as array of arrays + with length 4 + (d) ordered_labels: leaf labels in the order they are going to + appear on the plot + (e) P['leaves']: left-to-right traversal of the leaves + """ d = scs.distance.pdist(X) - Z = sch.linkage(d, method='complete') - P = sch.dendrogram(Z, orientation=self.orientation,labels=self.labels, no_plot=True) - - icoord = scp.array( P['icoord'] ) - dcoord = scp.array( P['dcoord'] ) - ordered_labels = scp.array( P['ivl'] ) - color_list = scp.array( P['color_list'] ) - colors = self.get_color_dict( colorscale ) + Z = sch.linkage(d, method='complete') + P = sch.dendrogram(Z, orientation=self.orientation, + labels=self.labels, no_plot=True) + + icoord = scp.array(P['icoord']) + dcoord = scp.array(P['dcoord']) + ordered_labels = scp.array(P['ivl']) + color_list = scp.array(P['color_list']) + colors = self.get_color_dict(colorscale) trace_list = [] for i in range(len(icoord)): - # xs and ys are arrays of 4 points that make up the '∩' shapes of the dendrogram tree - xs = icoord[i] if self.orientation in ['top','bottom'] else dcoord[i] - ys = dcoord[i] if self.orientation in ['top','bottom'] else icoord[i] - color_key = color_list[i] - trace = Scatter(x=np.multiply(self.sign[self.xaxis],xs), \ - y=np.multiply(self.sign[self.yaxis],ys), \ - mode='lines', marker=Marker(color=colors[color_key]) ) + # xs and ys are arrays of 4 points that make up the '∩' shapes + # of the dendrogram tree + if self.orientation in ['top', 'bottom']: + xs = icoord[i] + else: + xs = dcoord[i] + + if self.orientation in ['top', 'bottom']: + ys = dcoord[i] + else: + ys = icoord[i] + color_key = color_list[i] + trace = Scatter(x=np.multiply(self.sign[self.xaxis], xs), + y=np.multiply(self.sign[self.yaxis], ys), + mode='lines', + marker=Marker(color=colors[color_key])) trace['xaxis'] = 'x'+self.xaxis[-1] trace['yaxis'] = 'y'+self.yaxis[-1] - trace_list.append( trace ) - + trace_list.append(trace) + return trace_list, icoord, dcoord, ordered_labels, P['leaves'] From b7d3d3189bbfedf702b61a0913b5387b235ba116 Mon Sep 17 00:00:00 2001 From: Oxana Date: Sat, 15 Aug 2015 17:03:44 +0200 Subject: [PATCH 11/35] pep8 in tests --- .../test_optional/test_opt_tracefactory.py | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index 5652b10959f..226d4baa328 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -105,13 +105,14 @@ def test_simple_streamline(self): self.assertListEqual(strln['data'][0]['x'][0:100], expected_strln_0_100['x']) + class TestDendrogram(TestCase): def test_default_dendrogram(self): dendro = tls.FigureFactory.create_dendrogram(X=np.array([[1, 2, 3, 4], - [1, 1, 3, 4], - [1, 2, 1, 4], - [1, 2, 3, 1]])) + [1, 1, 3, 4], + [1, 2, 1, 4], + [1, 2, 3, 1]])) expected_dendro_data = [{'marker': {'color': 'rgb(255,133,27)'}, 'mode': 'lines', 'xaxis': 'xs', 'yaxis': 'ys', @@ -122,28 +123,34 @@ def test_default_dendrogram(self): 'mode': 'lines', 'xaxis': 'xs', 'yaxis': 'ys', - 'y': np.array([0., 2.23606798, 2.23606798, 1.]), + 'y': np.array([0., 2.23606798, + 2.23606798, 1.]), 'x': np.array([15., 15., 30., 30.]), 'type': u'scatter'}, {'marker': {'color': 'blue'}, 'mode': 'lines', 'xaxis': 'xs', 'yaxis': 'ys', - 'y': np.array([0., 3.60555128, 3.60555128, 2.23606798]), - 'x': np.array([5., 5., 22.5, 22.5]), 'type': u'scatter'}] - + 'y': np.array([0., 3.60555128, + 3.60555128, 2.23606798]), + 'x': np.array([5., 5., 22.5, 22.5]), + 'type': u'scatter'}] + self.assertEqual(len(dendro['data']), len(expected_dendro_data)) - self.assertTrue(np.array_equal(dendro['labels'], np.array(['3', '2', '0', '1']))) + self.assertTrue(np.array_equal(dendro['labels'], + np.array(['3', '2', '0', '1']))) - for i in range(1,len(dendro['data'])): - self.assertTrue(np.allclose(dendro['data'][i]['x'], expected_dendro_data[i]['x'])) - self.assertTrue(np.allclose(dendro['data'][i]['y'], expected_dendro_data[i]['y'])) + for i in range(1, len(dendro['data'])): + self.assertTrue(np.allclose(dendro['data'][i]['x'], + expected_dendro_data[i]['x'])) + self.assertTrue(np.allclose(dendro['data'][i]['y'], + expected_dendro_data[i]['y'])) def test_dendrogram_random_matrix(self): # create a random uncorrelated matrix - X = np.random.rand(5,5) + X = np.random.rand(5, 5) # variable 2 is correlated with all the other variables - X[2,:] = sum(X,0) + X[2, :] = sum(X, 0) dendro = tls.FigureFactory.create_dendrogram(X) From 44cdece2c3b91c8cb57ad0ddb3fff9283ff0f989 Mon Sep 17 00:00:00 2001 From: Oxana Date: Fri, 28 Aug 2015 16:13:52 +0200 Subject: [PATCH 12/35] Styling and imports --- plotly/tools.py | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index b53556739c8..277864c7b04 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -20,8 +20,7 @@ from plotly import exceptions from plotly import session -from plotly.graph_objs import graph_objs -from plotly.graph_objs import Scatter, Marker, Line, Data +from plotly.graph_objs import graph_objs, Scatter, Marker, Line, Data # Warning format @@ -52,13 +51,23 @@ def warning_on_one_line(message, category, filename, lineno, try: import scipy as scp - import scipy.spatial as scs - import scipy.cluster.hierarchy as sch - _scipy_imported = True except ImportError: _scipy_imported = False +try: + import scipy.spatial as scs + _scipy__spatial_imported = True +except ImportError: + _scipy__spatial_imported = False + +try: + import scipy.cluster.hierarchy as sch + _scipy__cluster__hierarchy_imported = True +except ImportError: + _scipy__cluster__hierarchy_imported = False + + PLOTLY_DIR = os.path.join(os.path.expanduser("~"), ".plotly") CREDENTIALS_FILE = os.path.join(PLOTLY_DIR, ".credentials") CONFIG_FILE = os.path.join(PLOTLY_DIR, ".config") @@ -2323,10 +2332,12 @@ def create_dendrogram(X, orientation="bottom", labels=None, py.iplot(dendro_X, validate=False, height=1000, width=300) ``` """ - - if _scipy_imported is False: - raise ImportError("FigureFactory.create_dendrogram requires scipy, - scipy.spatial and scipy.hierarchy") + dependencies = (_scipy_imported and _scipy__spatial_imported and + _scipy__cluster__hierarchy_imported) + + if dependencies is False: + raise ImportError("FigureFactory.create_dendrogram requires scipy, \ + scipy.spatial and scipy.hierarchy") s = X.shape if len(s) != 2: @@ -2934,14 +2945,15 @@ def get_candle_decrease(self): return (decrease_x, decrease_close, decrease_dif, stick_decrease_y, stick_decrease_x) + class _Dendrogram(FigureFactory): - + """ Refer to FigureFactory.create_dendrogram() for docstring. """ - def __init__(self, X, orientation='bottom', labels=None, colorscale=None, \ - width="100%", height="100%", xaxis='xaxis', yaxis='yaxis' ): + def __init__(self, X, orientation='bottom', labels=None, colorscale=None, + width="100%", height="100%", xaxis='xaxis', yaxis='yaxis'): self.orientation = orientation self.labels = labels self.xaxis = xaxis @@ -2961,8 +2973,8 @@ def __init__(self, X, orientation='bottom', labels=None, colorscale=None, \ else: self.sign[self.yaxis] = -1 - dd_traces, xvals, yvals, - ordered_labels, leaves = self.get_dendrogram_traces(X, colorscale) + (dd_traces, xvals, yvals, + ordered_labels, leaves) = self.get_dendrogram_traces(X, colorscale) self.labels = ordered_labels self.leaves = leaves From 16c74c1da67c4301e96c4a5ea0612ca405f1c7fe Mon Sep 17 00:00:00 2001 From: Oxana Date: Tue, 1 Sep 2015 10:13:43 +0200 Subject: [PATCH 13/35] Cleanup of labels, axis and tests --- .../test_optional/test_opt_tracefactory.py | 43 +++++++++++++++---- plotly/tools.py | 9 ++-- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index 226d4baa328..d4190065e23 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -115,29 +115,29 @@ def test_default_dendrogram(self): [1, 2, 3, 1]])) expected_dendro_data = [{'marker': {'color': 'rgb(255,133,27)'}, 'mode': 'lines', 'xaxis': 'xs', - 'yaxis': 'ys', + 'yaxis': 'y', 'y': np.array([0., 1., 1., 0.]), 'x': np.array([25., 25., 35., 35.]), 'type': u'scatter'}, {'marker': {'color': 'rgb(255,133,27)'}, 'mode': 'lines', - 'xaxis': 'xs', - 'yaxis': 'ys', + 'xaxis': 'x', + 'yaxis': 'y', 'y': np.array([0., 2.23606798, 2.23606798, 1.]), 'x': np.array([15., 15., 30., 30.]), 'type': u'scatter'}, {'marker': {'color': 'blue'}, 'mode': 'lines', - 'xaxis': 'xs', - 'yaxis': 'ys', + 'xaxis': 'x', + 'yaxis': 'y', 'y': np.array([0., 3.60555128, 3.60555128, 2.23606798]), 'x': np.array([5., 5., 22.5, 22.5]), 'type': u'scatter'}] self.assertEqual(len(dendro['data']), len(expected_dendro_data)) - self.assertTrue(np.array_equal(dendro['labels'], + self.assertTrue(np.array_equal(dendro['layout']['x']['ticktext'], np.array(['3', '2', '0', '1']))) for i in range(1, len(dendro['data'])): @@ -152,7 +152,32 @@ def test_dendrogram_random_matrix(self): # variable 2 is correlated with all the other variables X[2, :] = sum(X, 0) - dendro = tls.FigureFactory.create_dendrogram(X) + names = ['Jack', 'Oxana', 'John', 'Chelsea', 'Mark'] + dendro = tls.FigureFactory.create_dendrogram(X, labels=names) - # Check that 2 is in a separate cluster - self.assertEqual(dendro['labels'][0], '2') + # Check that 2 is in a separate cluster and it's labelled correctly + self.assertEqual(dendro['layout']['x']['ticktext'][0], 'John') + + def test_dendrogram_orientation(self): + X = np.random.rand(5, 5) + + dendro_left = tls.FigureFactory.create_dendrogram(X, orientation='left') + + self.assertEqual(len(dendro_left['layout']['y']['ticktext']), 5) + tickvals_left = np.array(dendro_left['layout']['y']['tickvals']) + self.assertTrue((tickvals_left <= 0).all()) + + dendro_right = tls.FigureFactory.create_dendrogram(X, orientation='right') + tickvals_right = np.array(dendro_right['layout']['y']['tickvals']) + self.assertTrue((tickvals_right >= 0).all()) + + dendro_bottom = tls.FigureFactory.create_dendrogram(X, orientation='bottom') + self.assertEqual(len(dendro_bottom['layout']['x']['ticktext']), 5) + tickvals_bottom = np.array(dendro_bottom['layout']['x']['tickvals']) + self.assertTrue((tickvals_bottom >= 0).all()) + + dendro_top = tls.FigureFactory.create_dendrogram(X, orientation='top') + tickvals_top = np.array(dendro_top['layout']['x']['tickvals']) + self.assertTrue((tickvals_top <= 0).all()) + + \ No newline at end of file diff --git a/plotly/tools.py b/plotly/tools.py index 277864c7b04..1905d15d232 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2346,8 +2346,7 @@ def create_dendrogram(X, orientation="bottom", labels=None, dendrogram = _Dendrogram(X, orientation, labels, colorscale) return {'layout': dendrogram.layout, - 'data': dendrogram.data, - 'labels': dendrogram.labels} + 'data': dendrogram.data} class _Quiver(FigureFactory): @@ -2953,7 +2952,7 @@ class _Dendrogram(FigureFactory): """ def __init__(self, X, orientation='bottom', labels=None, colorscale=None, - width="100%", height="100%", xaxis='xaxis', yaxis='yaxis'): + width="100%", height="100%", xaxis='x', yaxis='y'): self.orientation = orientation self.labels = labels self.xaxis = xaxis @@ -3125,8 +3124,8 @@ def get_dendrogram_traces(self, X, colorscale): y=np.multiply(self.sign[self.yaxis], ys), mode='lines', marker=Marker(color=colors[color_key])) - trace['xaxis'] = 'x'+self.xaxis[-1] - trace['yaxis'] = 'y'+self.yaxis[-1] + trace['xaxis'] = self.xaxis + trace['yaxis'] = self.yaxis trace_list.append(trace) return trace_list, icoord, dcoord, ordered_labels, P['leaves'] From ebff53e5c2367b42679f2a4ddb49018ab9b93e2e Mon Sep 17 00:00:00 2001 From: Oxana Date: Tue, 1 Sep 2015 22:44:44 +0200 Subject: [PATCH 14/35] Added more tests and fixed axis reference --- .../test_optional/test_opt_tracefactory.py | 91 +++++++++++++------ plotly/tools.py | 16 +++- 2 files changed, 76 insertions(+), 31 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index d4190065e23..c4f8b456d34 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -113,38 +113,71 @@ def test_default_dendrogram(self): [1, 1, 3, 4], [1, 2, 1, 4], [1, 2, 3, 1]])) - expected_dendro_data = [{'marker': {'color': 'rgb(255,133,27)'}, - 'mode': 'lines', 'xaxis': 'xs', - 'yaxis': 'y', - 'y': np.array([0., 1., 1., 0.]), - 'x': np.array([25., 25., 35., 35.]), - 'type': u'scatter'}, - {'marker': {'color': 'rgb(255,133,27)'}, - 'mode': 'lines', - 'xaxis': 'x', - 'yaxis': 'y', - 'y': np.array([0., 2.23606798, - 2.23606798, 1.]), - 'x': np.array([15., 15., 30., 30.]), - 'type': u'scatter'}, - {'marker': {'color': 'blue'}, - 'mode': 'lines', - 'xaxis': 'x', - 'yaxis': 'y', - 'y': np.array([0., 3.60555128, - 3.60555128, 2.23606798]), - 'x': np.array([5., 5., 22.5, 22.5]), - 'type': u'scatter'}] - - self.assertEqual(len(dendro['data']), len(expected_dendro_data)) - self.assertTrue(np.array_equal(dendro['layout']['x']['ticktext'], - np.array(['3', '2', '0', '1']))) - + expected_data = [{'marker': {'color': 'rgb(255,133,27)'}, + 'mode': 'lines', 'xaxis': 'xs', + 'yaxis': 'y', + 'y': np.array([0., 1., 1., 0.]), + 'x': np.array([25., 25., 35., 35.]), + 'type': u'scatter'}, + {'marker': {'color': 'rgb(255,133,27)'}, + 'mode': 'lines', + 'xaxis': 'x', + 'yaxis': 'y', + 'y': np.array([0., 2.23606798, + 2.23606798, 1.]), + 'x': np.array([15., 15., 30., 30.]), + 'type': u'scatter'}, + {'marker': {'color': 'blue'}, + 'mode': 'lines', + 'xaxis': 'x', + 'yaxis': 'y', + 'y': np.array([0., 3.60555128, + 3.60555128, 2.23606798]), + 'x': np.array([5., 5., 22.5, 22.5]), + 'type': u'scatter'}] + expected_layout = {'width': '100%', + 'showlegend': False, + 'autoscale': False, + 'x': {'showticklabels': True, + 'tickmode': 'array', + 'ticks': 'outside', + 'showgrid': False, + 'mirror': 'allticks', + 'zeroline': False, + 'showline': True, + 'ticktext': np.array(['3', '2', + '0', '1'], + dtype='|S1'), + 'rangemode': 'tozero', + 'type': 'linear', + 'tickvals': np.array([5.0, 15.0, + 25.0, 35.0])}, + 'y': {'showticklabels': True, + 'ticks': 'outside', + 'showgrid': False, + 'mirror': 'allticks', + 'zeroline': False, + 'showline': True, + 'rangemode': 'tozero', + 'type': 'linear'}, + 'hovermode': 'closest'} + + # Make sure data is as expected + self.assertEqual(len(dendro['data']), len(expected_data)) for i in range(1, len(dendro['data'])): self.assertTrue(np.allclose(dendro['data'][i]['x'], - expected_dendro_data[i]['x'])) + expected_data[i]['x'])) self.assertTrue(np.allclose(dendro['data'][i]['y'], - expected_dendro_data[i]['y'])) + expected_data[i]['y'])) + + # Make sure layout is as expected + self.assertTrue(np.array_equal(dendro['layout']['x']['ticktext'], + expected_layout['x']['ticktext'])) + self.assertTrue(np.array_equal(dendro['layout']['x']['tickvals'], + expected_layout['x']['tickvals'])) + self.assertEqual(dendro['layout']['x']['ticks'], 'outside') + self.assertEqual(dendro['layout']['y']['ticks'], 'outside') + self.assertEqual(dendro['layout']['width'], expected_layout['width']) def test_dendrogram_random_matrix(self): # create a random uncorrelated matrix diff --git a/plotly/tools.py b/plotly/tools.py index 1905d15d232..d8ca7040741 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -3124,8 +3124,20 @@ def get_dendrogram_traces(self, X, colorscale): y=np.multiply(self.sign[self.yaxis], ys), mode='lines', marker=Marker(color=colors[color_key])) - trace['xaxis'] = self.xaxis - trace['yaxis'] = self.yaxis + + try: + x_index = int(self.xaxis[-1]) + except ValueError: + x_index = '' + + try: + y_index = int(self.yaxis[-1]) + except ValueError: + y_index = '' + + trace['xaxis'] = 'x' + x_index + trace['yaxis'] = 'y' + y_index + trace_list.append(trace) return trace_list, icoord, dcoord, ordered_labels, P['leaves'] From 0de03495abfed7de27a60c9e82013a775d542448 Mon Sep 17 00:00:00 2001 From: Oxana Date: Tue, 1 Sep 2015 23:06:10 +0200 Subject: [PATCH 15/35] another fix for axis confusion --- .../test_optional/test_opt_tracefactory.py | 50 +++++++++---------- plotly/tools.py | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index c4f8b456d34..121378eb7fc 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -136,9 +136,9 @@ def test_default_dendrogram(self): 'x': np.array([5., 5., 22.5, 22.5]), 'type': u'scatter'}] expected_layout = {'width': '100%', - 'showlegend': False, - 'autoscale': False, - 'x': {'showticklabels': True, + 'showlegend': False, + 'autoscale': False, + 'xaxis': {'showticklabels': True, 'tickmode': 'array', 'ticks': 'outside', 'showgrid': False, @@ -152,15 +152,15 @@ def test_default_dendrogram(self): 'type': 'linear', 'tickvals': np.array([5.0, 15.0, 25.0, 35.0])}, - 'y': {'showticklabels': True, - 'ticks': 'outside', - 'showgrid': False, - 'mirror': 'allticks', - 'zeroline': False, - 'showline': True, - 'rangemode': 'tozero', - 'type': 'linear'}, - 'hovermode': 'closest'} + 'yaxis': {'showticklabels': True, + 'ticks': 'outside', + 'showgrid': False, + 'mirror': 'allticks', + 'zeroline': False, + 'showline': True, + 'rangemode': 'tozero', + 'type': 'linear'}, + 'hovermode': 'closest'} # Make sure data is as expected self.assertEqual(len(dendro['data']), len(expected_data)) @@ -171,12 +171,12 @@ def test_default_dendrogram(self): expected_data[i]['y'])) # Make sure layout is as expected - self.assertTrue(np.array_equal(dendro['layout']['x']['ticktext'], - expected_layout['x']['ticktext'])) - self.assertTrue(np.array_equal(dendro['layout']['x']['tickvals'], - expected_layout['x']['tickvals'])) - self.assertEqual(dendro['layout']['x']['ticks'], 'outside') - self.assertEqual(dendro['layout']['y']['ticks'], 'outside') + self.assertTrue(np.array_equal(dendro['layout']['xaxis']['ticktext'], + expected_layout['xaxis']['ticktext'])) + self.assertTrue(np.array_equal(dendro['layout']['xaxis']['tickvals'], + expected_layout['xaxis']['tickvals'])) + self.assertEqual(dendro['layout']['xaxis']['ticks'], 'outside') + self.assertEqual(dendro['layout']['yaxis']['ticks'], 'outside') self.assertEqual(dendro['layout']['width'], expected_layout['width']) def test_dendrogram_random_matrix(self): @@ -189,28 +189,28 @@ def test_dendrogram_random_matrix(self): dendro = tls.FigureFactory.create_dendrogram(X, labels=names) # Check that 2 is in a separate cluster and it's labelled correctly - self.assertEqual(dendro['layout']['x']['ticktext'][0], 'John') + self.assertEqual(dendro['layout']['xaxis']['ticktext'][0], 'John') def test_dendrogram_orientation(self): X = np.random.rand(5, 5) dendro_left = tls.FigureFactory.create_dendrogram(X, orientation='left') - self.assertEqual(len(dendro_left['layout']['y']['ticktext']), 5) - tickvals_left = np.array(dendro_left['layout']['y']['tickvals']) + self.assertEqual(len(dendro_left['layout']['yaxis']['ticktext']), 5) + tickvals_left = np.array(dendro_left['layout']['yaxis']['tickvals']) self.assertTrue((tickvals_left <= 0).all()) dendro_right = tls.FigureFactory.create_dendrogram(X, orientation='right') - tickvals_right = np.array(dendro_right['layout']['y']['tickvals']) + tickvals_right = np.array(dendro_right['layout']['yaxis']['tickvals']) self.assertTrue((tickvals_right >= 0).all()) dendro_bottom = tls.FigureFactory.create_dendrogram(X, orientation='bottom') - self.assertEqual(len(dendro_bottom['layout']['x']['ticktext']), 5) - tickvals_bottom = np.array(dendro_bottom['layout']['x']['tickvals']) + self.assertEqual(len(dendro_bottom['layout']['xaxis']['ticktext']), 5) + tickvals_bottom = np.array(dendro_bottom['layout']['xaxis']['tickvals']) self.assertTrue((tickvals_bottom >= 0).all()) dendro_top = tls.FigureFactory.create_dendrogram(X, orientation='top') - tickvals_top = np.array(dendro_top['layout']['x']['tickvals']) + tickvals_top = np.array(dendro_top['layout']['xaxis']['tickvals']) self.assertTrue((tickvals_top <= 0).all()) \ No newline at end of file diff --git a/plotly/tools.py b/plotly/tools.py index d8ca7040741..b7ea702020b 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2952,7 +2952,7 @@ class _Dendrogram(FigureFactory): """ def __init__(self, X, orientation='bottom', labels=None, colorscale=None, - width="100%", height="100%", xaxis='x', yaxis='y'): + width="100%", height="100%", xaxis='xaxis', yaxis='yaxis'): self.orientation = orientation self.labels = labels self.xaxis = xaxis From 20fc40c48a42b74915575d8dacec125e81b06c35 Mon Sep 17 00:00:00 2001 From: Oxana Date: Fri, 4 Sep 2015 15:14:52 +0200 Subject: [PATCH 16/35] Added colorscale test --- .../test_optional/test_opt_tracefactory.py | 33 ++++++++++++++++--- plotly/tools.py | 16 ++++----- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index 121378eb7fc..3187c4fdeb9 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -194,17 +194,19 @@ def test_dendrogram_random_matrix(self): def test_dendrogram_orientation(self): X = np.random.rand(5, 5) - dendro_left = tls.FigureFactory.create_dendrogram(X, orientation='left') - + dendro_left = tls.FigureFactory.create_dendrogram( + X, orientation='left') self.assertEqual(len(dendro_left['layout']['yaxis']['ticktext']), 5) tickvals_left = np.array(dendro_left['layout']['yaxis']['tickvals']) self.assertTrue((tickvals_left <= 0).all()) - dendro_right = tls.FigureFactory.create_dendrogram(X, orientation='right') + dendro_right = tls.FigureFactory.create_dendrogram( + X, orientation='right') tickvals_right = np.array(dendro_right['layout']['yaxis']['tickvals']) self.assertTrue((tickvals_right >= 0).all()) - dendro_bottom = tls.FigureFactory.create_dendrogram(X, orientation='bottom') + dendro_bottom = tls.FigureFactory.create_dendrogram( + X, orientation='bottom') self.assertEqual(len(dendro_bottom['layout']['xaxis']['ticktext']), 5) tickvals_bottom = np.array(dendro_bottom['layout']['xaxis']['tickvals']) self.assertTrue((tickvals_bottom >= 0).all()) @@ -213,4 +215,25 @@ def test_dendrogram_orientation(self): tickvals_top = np.array(dendro_top['layout']['xaxis']['tickvals']) self.assertTrue((tickvals_top <= 0).all()) - \ No newline at end of file + def test_dendrogram_orientation(self): + X = np.array([[1, 2, 3, 4], + [1, 1, 3, 4], + [1, 2, 1, 4], + [1, 2, 3, 1]]) + greyscale = [ + 'rgb(0,0,0)', # black + 'rgb(05,105,105)', # dim grey + 'rgb(128,128,128)', # grey + 'rgb(169,169,169)', # dark grey + 'rgb(192,192,192)', # silver + 'rgb(211,211,211)', # light grey + 'rgb(220,220,220)', # gainsboro + 'rgb(245,245,245)'] # white smoke + + dendro = tls.FigureFactory.create_dendrogram(X, colorscale=greyscale) + self.assertEqual(dendro["data"][0]['marker']['color'], + 'rgb(128,128,128)') + self.assertEqual(dendro["data"][1]['marker']['color'], + 'rgb(128,128,128)') + self.assertEqual(dendro["data"][2]['marker']['color'], + 'rgb(0,0,0)') diff --git a/plotly/tools.py b/plotly/tools.py index b7ea702020b..6ed363331ce 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -3012,14 +3012,14 @@ def get_color_dict(self, colorscale): if colorscale is None: colorscale = [ - "rgb(0,116,217)", # blue - "rgb(35,205,205)", # cyan - "rgb(61,153,112)", # green - "rgb(40,35,35)", # black - "rgb(133,20,75)", # magenta - "rgb(255,65,54)", # red - "rgb(255,255,255)", # white - "rgb(255,220,0)"] # yellow + 'rgb(0,116,217)', # blue + 'rgb(35,205,205)', # cyan + 'rgb(61,153,112)', # green + 'rgb(40,35,35)', # black + 'rgb(133,20,75)', # magenta + 'rgb(255,65,54)', # red + 'rgb(255,255,255)', # white + 'rgb(255,220,0)'] # yellow for i in range(len(default_colors.keys())): k = default_colors.keys()[i] From 88842b1a9c8d6e15b71aaa01d8f1ad67aceb7377 Mon Sep 17 00:00:00 2001 From: Oxana Date: Fri, 4 Sep 2015 16:44:57 +0200 Subject: [PATCH 17/35] cow comments --- plotly/tools.py | 117 ++++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 6ed363331ce..e52554593b3 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2304,37 +2304,31 @@ def create_dendrogram(X, orientation="bottom", labels=None, :param (list) colorscale: Optional colorscale for dendrogram tree clusters - X: Heatmap matrix as array of arrays - orientation: 'top', 'right', 'bottom', or 'left' - labels: List of axis category labels - colorscale: Optional colorscale for dendrogram tree clusters - - Example 1: Simple bottom oriented dendrogram ``` + import numpy as np + import plotly.plotly as py from plotly.tools import FigureFactory as FF - import numpy as np - X = np.random.rand(5,5) - dendro_X = FF.create_dendrogram(X) - py.iplot(dendro_X, validate=False, height=300, width=1000) + dendro = FF.create_dendrogram(X) + py.iplot(dendro, validate=False, height=300, width=1000) ``` Example 2: Dendrogram to put on the left of the heatmap ``` X = np.random.rand(5,5) - names_X = ['Jack', 'Oxana', 'John', 'Chelsea', 'Mark'] - dendro_X = FF.create_dendrogram(X, orientation='right', labels=names_X) + names = ['Jack', 'Oxana', 'John', 'Chelsea', 'Mark'] + dendro = FF.create_dendrogram(X, orientation='right', labels=names) - py.iplot(dendro_X, validate=False, height=1000, width=300) + py.iplot(dendro, validate=False, height=1000, width=300) ``` """ dependencies = (_scipy_imported and _scipy__spatial_imported and _scipy__cluster__hierarchy_imported) - + if dependencies is False: raise ImportError("FigureFactory.create_dendrogram requires scipy, \ scipy.spatial and scipy.hierarchy") @@ -2946,7 +2940,7 @@ def get_candle_decrease(self): class _Dendrogram(FigureFactory): - + """ Refer to FigureFactory.create_dendrogram() for docstring. """ @@ -2971,10 +2965,10 @@ def __init__(self, X, orientation='bottom', labels=None, colorscale=None, self.sign[self.yaxis] = 1 else: self.sign[self.yaxis] = -1 - + (dd_traces, xvals, yvals, ordered_labels, leaves) = self.get_dendrogram_traces(X, colorscale) - + self.labels = ordered_labels self.leaves = leaves yvals_flat = yvals.flatten() @@ -2987,17 +2981,19 @@ def __init__(self, X, orientation='bottom', labels=None, colorscale=None, self.zero_vals.append(xvals_flat[i]) self.zero_vals.sort() - + self.layout = self.set_figure_layout(width, height) self.data = Data(dd_traces) def get_color_dict(self, colorscale): - """ - Returns colorscale used for dendrogram tree clusters - :param (list) colorscale: colors to use for the plot, - in rgb format """ - + Returns colorscale used for dendrogram tree clusters + :param (list) colorscale: colors to use for the plot, + in rgb format + :rtype (dict): returns a dictionary of default colors mapped + to the user colorscale + """ + # These are the color codes returned for dendrograms # We're replacing them with nicer colors d = {'r': 'red', @@ -3025,13 +3021,14 @@ def get_color_dict(self, colorscale): k = default_colors.keys()[i] if i < len(colorscale): default_colors[k] = colorscale[i] - + return default_colors - + def set_axis_layout(self, axis_key): - """ - Sets and returns default axis object for dendrogram figure - :param (str) axis_key: "xaxis", "xaxis1", "yaxis", yaxis1", etc. + """ + Sets and returns default axis object for dendrogram figure + :param (str) axis_key: "xaxis", "xaxis1", "yaxis", yaxis1", etc. + :rtype (dict): returns an axis_key dictionary with set parameters """ axis_defaults = { @@ -3039,61 +3036,64 @@ def set_axis_layout(self, axis_key): 'ticks': 'outside', 'mirror': 'allticks', 'rangemode': 'tozero', - 'showticklabels': True, + 'showticklabels': True, 'zeroline': False, - 'showgrid': False, + 'showgrid': False, 'showline': True, } - + if len(self.labels) != 0: axis_key_labels = self.xaxis if self.orientation in ['left', 'right']: axis_key_labels = self.yaxis if axis_key_labels not in self.layout: self.layout[axis_key_labels] = {} - self.layout[axis_key_labels]['tickvals'] = [ea*self.sign[axis_key] - for ea in self.zero_vals] + self.layout[axis_key_labels]['tickvals'] = [zv*self.sign[axis_key] + for zv in self.zero_vals] self.layout[axis_key_labels]['ticktext'] = self.labels self.layout[axis_key_labels]['tickmode'] = 'array' - + self.layout[axis_key].update(axis_defaults) - + return self.layout[axis_key] def set_figure_layout(self, width, height): - """ - Sets and returns default layout object for dendrogram figure """ - + Sets and returns default layout object for dendrogram figure + """ + self.layout.update({ 'showlegend': False, - 'autoscale': False, + 'autoscale': False, 'hovermode': 'closest', 'width': width, 'width': height }) - + self.set_axis_layout(self.xaxis) self.set_axis_layout(self.yaxis) return self.layout def get_dendrogram_traces(self, X, colorscale): - """ - Calculates all the elements needed for plotting a dendrogram - - :rtype (tuple): Contains all the traces in the following order - (a) trace_list: List of Plotly trace objects for the dendrogram - tree - (b) icoord: All X points of the dendogram tree as array of arrays - with length 4 - (c) dcoord: All Y points of the dendogram tree as array of arrays - with length 4 - (d) ordered_labels: leaf labels in the order they are going to - appear on the plot - (e) P['leaves']: left-to-right traversal of the leaves - """ - + """ + Calculates all the elements needed for plotting a dendrogram + + :param (ndarray) X: Matrix of observations as arrray of arrays + :param (list) colorscale: Optional colorscale for dendrogram tree + clusters + + :rtype (tuple): Contains all the traces in the following order + (a) trace_list: List of Plotly trace objects for the dendrogram tree + (b) icoord: All X points of the dendogram tree as array of arrays + with length 4 + (c) dcoord: All Y points of the dendogram tree as array of arrays + with length 4 + (d) ordered_labels: leaf labels in the order they are going to + appear on the plot + (e) P['leaves']: left-to-right traversal of the leaves + """ + d = scs.distance.pdist(X) Z = sch.linkage(d, method='complete') P = sch.dendrogram(Z, orientation=self.orientation, @@ -3104,9 +3104,9 @@ def get_dendrogram_traces(self, X, colorscale): ordered_labels = scp.array(P['ivl']) color_list = scp.array(P['color_list']) colors = self.get_color_dict(colorscale) - + trace_list = [] - + for i in range(len(icoord)): # xs and ys are arrays of 4 points that make up the '∩' shapes # of the dendrogram tree @@ -3139,6 +3139,5 @@ def get_dendrogram_traces(self, X, colorscale): trace['yaxis'] = 'y' + y_index trace_list.append(trace) - - return trace_list, icoord, dcoord, ordered_labels, P['leaves'] + return trace_list, icoord, dcoord, ordered_labels, P['leaves'] From e7bf58c2ce6b9cd6e2f8c4cecf043ec56051a651 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Wed, 30 Sep 2015 22:47:07 +0700 Subject: [PATCH 18/35] Duplicate `width` to `height` in layout update. --- plotly/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index a078cf98ebe..e7b07524d67 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -3333,7 +3333,7 @@ def set_figure_layout(self, width, height): 'autoscale': False, 'hovermode': 'closest', 'width': width, - 'width': height + 'height': height }) self.set_axis_layout(self.xaxis) From 3632084abefc285b221ce76b5120382bced2ed2b Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Wed, 30 Sep 2015 23:06:51 +0700 Subject: [PATCH 19/35] Make docs a little bit more consistent. --- plotly/tools.py | 56 ++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index e7b07524d67..d7279c8bc4e 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2434,7 +2434,7 @@ def create_dendrogram(X, orientation="bottom", labels=None, :param (str) orientation: 'top', 'right', 'bottom', or 'left' :param (list) labels: List of axis category labels(observation labels) :param (list) colorscale: Optional colorscale for dendrogram tree - clusters + clusters Example 1: Simple bottom oriented dendrogram ``` @@ -2457,6 +2457,7 @@ def create_dendrogram(X, orientation="bottom", labels=None, py.iplot(dendro, validate=False, height=1000, width=300) ``` + """ dependencies = (_scipy_imported and _scipy__spatial_imported and _scipy__cluster__hierarchy_imported) @@ -3204,10 +3205,7 @@ def make_rug(self): class _Dendrogram(FigureFactory): - - """ - Refer to FigureFactory.create_dendrogram() for docstring. - """ + """Refer to FigureFactory.create_dendrogram() for docstring.""" def __init__(self, X, orientation='bottom', labels=None, colorscale=None, width="100%", height="100%", xaxis='xaxis', yaxis='yaxis'): @@ -3253,11 +3251,11 @@ def __init__(self, X, orientation='bottom', labels=None, colorscale=None, def get_color_dict(self, colorscale): """ - Returns colorscale used for dendrogram tree clusters - :param (list) colorscale: colors to use for the plot, - in rgb format - :rtype (dict): returns a dictionary of default colors mapped - to the user colorscale + Returns colorscale used for dendrogram tree clusters. + + :param (list) colorscale: Colors to use for the plot in rgb format. + :rtype (dict): A dict of default colors mapped to the user colorscale. + """ # These are the color codes returned for dendrograms @@ -3292,11 +3290,12 @@ def get_color_dict(self, colorscale): def set_axis_layout(self, axis_key): """ - Sets and returns default axis object for dendrogram figure - :param (str) axis_key: "xaxis", "xaxis1", "yaxis", yaxis1", etc. - :rtype (dict): returns an axis_key dictionary with set parameters - """ + Sets and returns default axis object for dendrogram figure. + :param (str) axis_key: E.g., 'xaxis', 'xaxis1', 'yaxis', yaxis1', etc. + :rtype (dict): An axis_key dictionary with set parameters. + + """ axis_defaults = { 'type': 'linear', 'ticks': 'outside', @@ -3325,9 +3324,9 @@ def set_axis_layout(self, axis_key): def set_figure_layout(self, width, height): """ - Sets and returns default layout object for dendrogram figure - """ + Sets and returns default layout object for dendrogram figure. + """ self.layout.update({ 'showlegend': False, 'autoscale': False, @@ -3343,21 +3342,20 @@ def set_figure_layout(self, width, height): def get_dendrogram_traces(self, X, colorscale): """ - Calculates all the elements needed for plotting a dendrogram + Calculates all the elements needed for plotting a dendrogram. :param (ndarray) X: Matrix of observations as arrray of arrays - :param (list) colorscale: Optional colorscale for dendrogram tree - clusters - - :rtype (tuple): Contains all the traces in the following order - (a) trace_list: List of Plotly trace objects for the dendrogram tree - (b) icoord: All X points of the dendogram tree as array of arrays - with length 4 - (c) dcoord: All Y points of the dendogram tree as array of arrays - with length 4 - (d) ordered_labels: leaf labels in the order they are going to - appear on the plot - (e) P['leaves']: left-to-right traversal of the leaves + :param (list) colorscale: Colorscale for dendrogram tree clusters + :rtype (tuple): Contains all the traces in the following order: + (a) trace_list: List of Plotly trace objects for dendrogram tree + (b) icoord: All X points of the dendogram tree as array of arrays + with length 4 + (c) dcoord: All Y points of the dendogram tree as array of arrays + with length 4 + (d) ordered_labels: leaf labels in the order they are going to + appear on the plot + (e) P['leaves']: left-to-right traversal of the leaves + """ # TODO: protected until #282 from plotly.graph_objs import graph_objs From 8aea12eaf0bf71992ce0d17d4c1ed1e40b1563ec Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Wed, 30 Sep 2015 23:09:41 +0700 Subject: [PATCH 20/35] Spell check. --- plotly/tools.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index d7279c8bc4e..5a27679fc0c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2430,7 +2430,7 @@ def create_dendrogram(X, orientation="bottom", labels=None, """ BETA function that returns a dendrogram Plotly figure object. - :param (ndarray) X: Matrix of observations as arrray of arrays + :param (ndarray) X: Matrix of observations as array of arrays :param (str) orientation: 'top', 'right', 'bottom', or 'left' :param (list) labels: List of axis category labels(observation labels) :param (list) colorscale: Optional colorscale for dendrogram tree @@ -3344,13 +3344,13 @@ def get_dendrogram_traces(self, X, colorscale): """ Calculates all the elements needed for plotting a dendrogram. - :param (ndarray) X: Matrix of observations as arrray of arrays - :param (list) colorscale: Colorscale for dendrogram tree clusters + :param (ndarray) X: Matrix of observations as array of arrays + :param (list) colorscale: Color scale for dendrogram tree clusters :rtype (tuple): Contains all the traces in the following order: (a) trace_list: List of Plotly trace objects for dendrogram tree - (b) icoord: All X points of the dendogram tree as array of arrays + (b) icoord: All X points of the dendrogram tree as array of arrays with length 4 - (c) dcoord: All Y points of the dendogram tree as array of arrays + (c) dcoord: All Y points of the dendrogram tree as array of arrays with length 4 (d) ordered_labels: leaf labels in the order they are going to appear on the plot From fae204752f4331cc99f07a0274dda4ed710197a5 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Wed, 30 Sep 2015 23:09:48 +0700 Subject: [PATCH 21/35] pep:8ball: --- .../tests/test_optional/test_opt_tracefactory.py | 14 +++++++------- plotly/tools.py | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index a8210ab1344..685f25ab7b0 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -249,10 +249,8 @@ def test_simple_streamline(self): class TestDendrogram(TestCase): def test_default_dendrogram(self): - dendro = tls.FigureFactory.create_dendrogram(X=np.array([[1, 2, 3, 4], - [1, 1, 3, 4], - [1, 2, 1, 4], - [1, 2, 3, 1]])) + X = np.array([[1, 2, 3, 4], [1, 1, 3, 4], [1, 2, 1, 4], [1, 2, 3, 1]]) + dendro = tls.FigureFactory.create_dendrogram(X=X) expected_data = [{'marker': {'color': 'rgb(255,133,27)'}, 'mode': 'lines', 'xaxis': 'xs', 'yaxis': 'y', @@ -348,7 +346,9 @@ def test_dendrogram_orientation(self): dendro_bottom = tls.FigureFactory.create_dendrogram( X, orientation='bottom') self.assertEqual(len(dendro_bottom['layout']['xaxis']['ticktext']), 5) - tickvals_bottom = np.array(dendro_bottom['layout']['xaxis']['tickvals']) + tickvals_bottom = np.array( + dendro_bottom['layout']['xaxis']['tickvals'] + ) self.assertTrue((tickvals_bottom >= 0).all()) dendro_top = tls.FigureFactory.create_dendrogram(X, orientation='top') @@ -361,8 +361,8 @@ def test_dendrogram_orientation(self): [1, 2, 1, 4], [1, 2, 3, 1]]) greyscale = [ - 'rgb(0,0,0)', # black - 'rgb(05,105,105)', # dim grey + 'rgb(0,0,0)', # black + 'rgb(05,105,105)', # dim grey 'rgb(128,128,128)', # grey 'rgb(169,169,169)', # dark grey 'rgb(192,192,192)', # silver diff --git a/plotly/tools.py b/plotly/tools.py index 5a27679fc0c..eaba4b743b0 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -3272,14 +3272,14 @@ def get_color_dict(self, colorscale): if colorscale is None: colorscale = [ - 'rgb(0,116,217)', # blue + 'rgb(0,116,217)', # blue 'rgb(35,205,205)', # cyan 'rgb(61,153,112)', # green - 'rgb(40,35,35)', # black - 'rgb(133,20,75)', # magenta - 'rgb(255,65,54)', # red - 'rgb(255,255,255)', # white - 'rgb(255,220,0)'] # yellow + 'rgb(40,35,35)', # black + 'rgb(133,20,75)', # magenta + 'rgb(255,65,54)', # red + 'rgb(255,255,255)', # white + 'rgb(255,220,0)'] # yellow for i in range(len(default_colors.keys())): k = default_colors.keys()[i] @@ -3313,8 +3313,8 @@ def set_axis_layout(self, axis_key): axis_key_labels = self.yaxis if axis_key_labels not in self.layout: self.layout[axis_key_labels] = {} - self.layout[axis_key_labels]['tickvals'] = [zv*self.sign[axis_key] - for zv in self.zero_vals] + self.layout[axis_key_labels]['tickvals'] = \ + [zv*self.sign[axis_key] for zv in self.zero_vals] self.layout[axis_key_labels]['ticktext'] = self.labels self.layout[axis_key_labels]['tickmode'] = 'array' From 723dde4603781c01da141a9c501f658d0860bc8f Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Wed, 30 Sep 2015 23:09:48 +0700 Subject: [PATCH 22/35] pep:8ball: --- .../test_optional/test_opt_tracefactory.py | 25 +++++++++---------- plotly/tools.py | 16 ++++++------ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index a8210ab1344..1919fb0b3ed 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -249,22 +249,19 @@ def test_simple_streamline(self): class TestDendrogram(TestCase): def test_default_dendrogram(self): - dendro = tls.FigureFactory.create_dendrogram(X=np.array([[1, 2, 3, 4], - [1, 1, 3, 4], - [1, 2, 1, 4], - [1, 2, 3, 1]])) + X = np.array([[1, 2, 3, 4], [1, 1, 3, 4], [1, 2, 1, 4], [1, 2, 3, 1]]) + dendro = tls.FigureFactory.create_dendrogram(X=X) expected_data = [{'marker': {'color': 'rgb(255,133,27)'}, 'mode': 'lines', 'xaxis': 'xs', 'yaxis': 'y', - 'y': np.array([0., 1., 1., 0.]), - 'x': np.array([25., 25., 35., 35.]), + 'y': np.array([0., 1., 1., 0.]), + 'x': np.array([25., 25., 35., 35.]), 'type': u'scatter'}, {'marker': {'color': 'rgb(255,133,27)'}, 'mode': 'lines', 'xaxis': 'x', 'yaxis': 'y', - 'y': np.array([0., 2.23606798, - 2.23606798, 1.]), + 'y': np.array([0., 2.23606798, 2.23606798, 1.]), 'x': np.array([15., 15., 30., 30.]), 'type': u'scatter'}, {'marker': {'color': 'blue'}, @@ -272,7 +269,7 @@ def test_default_dendrogram(self): 'xaxis': 'x', 'yaxis': 'y', 'y': np.array([0., 3.60555128, - 3.60555128, 2.23606798]), + 3.60555128, 2.23606798]), 'x': np.array([5., 5., 22.5, 22.5]), 'type': u'scatter'}] expected_layout = {'width': '100%', @@ -291,7 +288,7 @@ def test_default_dendrogram(self): 'rangemode': 'tozero', 'type': 'linear', 'tickvals': np.array([5.0, 15.0, - 25.0, 35.0])}, + 25.0, 35.0])}, 'yaxis': {'showticklabels': True, 'ticks': 'outside', 'showgrid': False, @@ -348,7 +345,9 @@ def test_dendrogram_orientation(self): dendro_bottom = tls.FigureFactory.create_dendrogram( X, orientation='bottom') self.assertEqual(len(dendro_bottom['layout']['xaxis']['ticktext']), 5) - tickvals_bottom = np.array(dendro_bottom['layout']['xaxis']['tickvals']) + tickvals_bottom = np.array( + dendro_bottom['layout']['xaxis']['tickvals'] + ) self.assertTrue((tickvals_bottom >= 0).all()) dendro_top = tls.FigureFactory.create_dendrogram(X, orientation='top') @@ -361,8 +360,8 @@ def test_dendrogram_orientation(self): [1, 2, 1, 4], [1, 2, 3, 1]]) greyscale = [ - 'rgb(0,0,0)', # black - 'rgb(05,105,105)', # dim grey + 'rgb(0,0,0)', # black + 'rgb(05,105,105)', # dim grey 'rgb(128,128,128)', # grey 'rgb(169,169,169)', # dark grey 'rgb(192,192,192)', # silver diff --git a/plotly/tools.py b/plotly/tools.py index 5a27679fc0c..eaba4b743b0 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -3272,14 +3272,14 @@ def get_color_dict(self, colorscale): if colorscale is None: colorscale = [ - 'rgb(0,116,217)', # blue + 'rgb(0,116,217)', # blue 'rgb(35,205,205)', # cyan 'rgb(61,153,112)', # green - 'rgb(40,35,35)', # black - 'rgb(133,20,75)', # magenta - 'rgb(255,65,54)', # red - 'rgb(255,255,255)', # white - 'rgb(255,220,0)'] # yellow + 'rgb(40,35,35)', # black + 'rgb(133,20,75)', # magenta + 'rgb(255,65,54)', # red + 'rgb(255,255,255)', # white + 'rgb(255,220,0)'] # yellow for i in range(len(default_colors.keys())): k = default_colors.keys()[i] @@ -3313,8 +3313,8 @@ def set_axis_layout(self, axis_key): axis_key_labels = self.yaxis if axis_key_labels not in self.layout: self.layout[axis_key_labels] = {} - self.layout[axis_key_labels]['tickvals'] = [zv*self.sign[axis_key] - for zv in self.zero_vals] + self.layout[axis_key_labels]['tickvals'] = \ + [zv*self.sign[axis_key] for zv in self.zero_vals] self.layout[axis_key_labels]['ticktext'] = self.labels self.layout[axis_key_labels]['tickmode'] = 'array' From 806a6e065b3891e4398978779713577e84152c6e Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 00:18:11 +0700 Subject: [PATCH 23/35] =?UTF-8?q?`autoscale`=20=E2=80=94>=20`autosize`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plotly/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index eaba4b743b0..d812da7331d 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -3329,7 +3329,7 @@ def set_figure_layout(self, width, height): """ self.layout.update({ 'showlegend': False, - 'autoscale': False, + 'autosize': False, 'hovermode': 'closest', 'width': width, 'height': height From ca087773c991e40c35e223fafe9fd9e0a2a91b25 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 00:57:34 +0700 Subject: [PATCH 24/35] Add a `NumpyTestUtilsMixin` to help dict comps. The difficulty of comparing dicts with nested either (a) ndarrays or (b) number arrays that equal one another up-to-a-tolerance was preventing us from writing stronger tests. This is far from perfect, but it allows you to just throw a set of dict objects in without getting false negative results. --- plotly/tests/test_optional/optional_utils.py | 82 ++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/plotly/tests/test_optional/optional_utils.py b/plotly/tests/test_optional/optional_utils.py index c31507bd47f..684e25faea4 100644 --- a/plotly/tests/test_optional/optional_utils.py +++ b/plotly/tests/test_optional/optional_utils.py @@ -3,7 +3,12 @@ import matplotlib # Force matplotlib to not use any Xwindows backend. matplotlib.use('Agg') + +import numpy as np + from plotly.matplotlylib import Exporter, PlotlyRenderer +from plotly.tests.utils import is_num_list +from plotly.utils import get_by_path, node_generator def run_fig(fig): @@ -11,3 +16,80 @@ def run_fig(fig): exporter = Exporter(renderer) exporter.run(fig) return renderer + + +class NumpyTestUtilsMixin(object): + """Provides some helper functions to make testing easier.""" + + def _format_path(self, path): + str_path = [repr(p) for p in path] + return '[' + ']['.join(sp for sp in str_path) + ']' + + def assert_dict_equal(self, d1, d2, msg=None): + """ + Uses `np.allclose()` on number arrays. + + :raises: (AssertionError) Using TestCase's self.failureException + + """ + self.assertIsInstance(d1, dict, 'First argument is not a dictionary') + self.assertIsInstance(d2, dict, 'Second argument is not a dictionary') + + for node, path in node_generator(d1): + + # first check that this sub-dict is contained in both dicts + try: + comp_node = get_by_path(d2, path) + except (KeyError, IndexError): + standard_msg = ( + 'Path {} exists in dict 1, but not dict 2.' + .format(path) + ) + self.fail(self._formatMessage(msg, standard_msg)) + self.assertIsInstance( + comp_node, dict, 'Value at path {} is not a dict.'.format(path) + ) + + # check that each key in the first is contained in the second + for key, val in node.items(): + if isinstance(val, dict): + continue # this gets tested as its own node + + # check that the values at this key are equal + val_path = path + (key, ) + try: + comp_val = comp_node[key] + except KeyError: + standard_msg = ( + 'Path {} exists in dict 1, but not dict 2.' + .format(self._format_path(val_path)) + ) + self.fail(self._formatMessage(msg, standard_msg)) + + if (isinstance(val, np.ndarray) or + isinstance(comp_val, np.ndarray)): + if np.array_equal(val, comp_val): + continue + elif val == comp_val: + continue + + if is_num_list(val) and is_num_list(comp_val): + if np.allclose(val, comp_val): + continue + + standard_msg = ( + 'Value comparison failed at path {}.\n' + '{} != {}' + .format(self._format_path(val_path), val, comp_val) + ) + self.fail(self._formatMessage(msg, standard_msg)) + + # finally, check that keys in the second are in the first + for key in comp_node: + val_path = path + (key, ) + if key not in node: + standard_msg = ( + 'Path {} exists in dict 2, but not dict 1.' + .format(self._format_path(val_path)) + ) + self.fail(self._formatMessage(msg, standard_msg)) From 8866e3427b82734bac6c2a6ac758f1afa826666c Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 01:00:46 +0700 Subject: [PATCH 25/35] Show that the dendrogram test should have failed! --- .../test_optional/test_opt_tracefactory.py | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index 1919fb0b3ed..4cf6e166405 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -3,6 +3,7 @@ from plotly.exceptions import PlotlyError import plotly.tools as tls +from plotly.tests.test_optional.optional_utils import NumpyTestUtilsMixin import math from nose.tools import raises @@ -246,7 +247,7 @@ def test_simple_streamline(self): expected_strln_0_100['x']) -class TestDendrogram(TestCase): +class TestDendrogram(NumpyTestUtilsMixin, TestCase): def test_default_dendrogram(self): X = np.array([[1, 2, 3, 4], [1, 1, 3, 4], [1, 2, 1, 4], [1, 2, 3, 1]]) @@ -297,24 +298,17 @@ def test_default_dendrogram(self): 'showline': True, 'rangemode': 'tozero', 'type': 'linear'}, - 'hovermode': 'closest'} - - # Make sure data is as expected - self.assertEqual(len(dendro['data']), len(expected_data)) - for i in range(1, len(dendro['data'])): - self.assertTrue(np.allclose(dendro['data'][i]['x'], - expected_data[i]['x'])) - self.assertTrue(np.allclose(dendro['data'][i]['y'], - expected_data[i]['y'])) - - # Make sure layout is as expected - self.assertTrue(np.array_equal(dendro['layout']['xaxis']['ticktext'], - expected_layout['xaxis']['ticktext'])) - self.assertTrue(np.array_equal(dendro['layout']['xaxis']['tickvals'], - expected_layout['xaxis']['tickvals'])) - self.assertEqual(dendro['layout']['xaxis']['ticks'], 'outside') - self.assertEqual(dendro['layout']['yaxis']['ticks'], 'outside') - self.assertEqual(dendro['layout']['width'], expected_layout['width']) + 'hovermode': 'closest'} + + expected_dendro = {'data': expected_data, 'layout': expected_layout} + self.assertEqual(len(dendro['data']), 3) + + # this is actually a bit clearer when debugging tests. + self.assert_dict_equal(dendro['data'][0], expected_dendro['data'][0]) + self.assert_dict_equal(dendro['data'][1], expected_dendro['data'][1]) + self.assert_dict_equal(dendro['data'][2], expected_dendro['data'][2]) + + self.assert_dict_equal(dendro['layout'], expected_dendro['layout']) def test_dendrogram_random_matrix(self): # create a random uncorrelated matrix From d0b0f6a85731b07eda399ae6dedb71ae94c12ee8 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 01:02:44 +0700 Subject: [PATCH 26/35] Fix dendrogram test, lock down *entire* figure. --- .../test_optional/test_opt_tracefactory.py | 111 ++++++++++-------- 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index 4cf6e166405..ef2d3027141 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -1,5 +1,5 @@ from unittest import TestCase -from plotly.graph_objs import graph_objs, Line +from plotly.graph_objs import graph_objs as go from plotly.exceptions import PlotlyError import plotly.tools as tls @@ -252,55 +252,66 @@ class TestDendrogram(NumpyTestUtilsMixin, TestCase): def test_default_dendrogram(self): X = np.array([[1, 2, 3, 4], [1, 1, 3, 4], [1, 2, 1, 4], [1, 2, 3, 1]]) dendro = tls.FigureFactory.create_dendrogram(X=X) - expected_data = [{'marker': {'color': 'rgb(255,133,27)'}, - 'mode': 'lines', 'xaxis': 'xs', - 'yaxis': 'y', - 'y': np.array([0., 1., 1., 0.]), - 'x': np.array([25., 25., 35., 35.]), - 'type': u'scatter'}, - {'marker': {'color': 'rgb(255,133,27)'}, - 'mode': 'lines', - 'xaxis': 'x', - 'yaxis': 'y', - 'y': np.array([0., 2.23606798, 2.23606798, 1.]), - 'x': np.array([15., 15., 30., 30.]), - 'type': u'scatter'}, - {'marker': {'color': 'blue'}, - 'mode': 'lines', - 'xaxis': 'x', - 'yaxis': 'y', - 'y': np.array([0., 3.60555128, - 3.60555128, 2.23606798]), - 'x': np.array([5., 5., 22.5, 22.5]), - 'type': u'scatter'}] - expected_layout = {'width': '100%', - 'showlegend': False, - 'autoscale': False, - 'xaxis': {'showticklabels': True, - 'tickmode': 'array', - 'ticks': 'outside', - 'showgrid': False, - 'mirror': 'allticks', - 'zeroline': False, - 'showline': True, - 'ticktext': np.array(['3', '2', - '0', '1'], - dtype='|S1'), - 'rangemode': 'tozero', - 'type': 'linear', - 'tickvals': np.array([5.0, 15.0, - 25.0, 35.0])}, - 'yaxis': {'showticklabels': True, - 'ticks': 'outside', - 'showgrid': False, - 'mirror': 'allticks', - 'zeroline': False, - 'showline': True, - 'rangemode': 'tozero', - 'type': 'linear'}, - 'hovermode': 'closest'} - - expected_dendro = {'data': expected_data, 'layout': expected_layout} + + expected_dendro = go.Figure( + data=go.Data([ + go.Scatter( + x=np.array([25., 25., 35., 35.]), + y=np.array([0., 1., 1., 0.]), + marker=go.Marker(color='rgb(61,153,112)'), + mode='lines', + xaxis='x', + yaxis='y' + ), + go.Scatter( + x=np.array([15., 15., 30., 30.]), + y=np.array([0., 2.23606798, 2.23606798, 1.]), + marker=go.Marker(color='rgb(61,153,112)'), + mode='lines', + xaxis='x', + yaxis='y' + ), + go.Scatter( + x=np.array([5., 5., 22.5, 22.5]), + y=np.array([0., 3.60555128, 3.60555128, 2.23606798]), + marker=go.Marker(color='rgb(0,116,217)'), + mode='lines', + xaxis='x', + yaxis='y' + ) + ]), + layout=go.Layout( + autosize=False, + height='100%', + hovermode='closest', + showlegend=False, + width='100%', + xaxis=go.XAxis( + mirror='allticks', + rangemode='tozero', + showgrid=False, + showline=True, + showticklabels=True, + tickmode='array', + ticks='outside', + ticktext=np.array(['3', '2', '0', '1'], dtype='|S1'), + tickvals=[5.0, 15.0, 25.0, 35.0], + type='linear', + zeroline=False + ), + yaxis=go.YAxis( + mirror='allticks', + rangemode='tozero', + showgrid=False, + showline=True, + showticklabels=True, + ticks='outside', + type='linear', + zeroline=False + ) + ) + ) + self.assertEqual(len(dendro['data']), 3) # this is actually a bit clearer when debugging tests. From bed3becade9cc0387c162200d710138ea2db2979 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 01:34:57 +0700 Subject: [PATCH 27/35] Lock down `test_dendrogram_random_matrix` test. --- .../test_optional/test_opt_tracefactory.py | 92 ++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index ef2d3027141..2b76e1b2edb 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -322,16 +322,104 @@ def test_default_dendrogram(self): self.assert_dict_equal(dendro['layout'], expected_dendro['layout']) def test_dendrogram_random_matrix(self): + # create a random uncorrelated matrix X = np.random.rand(5, 5) + # variable 2 is correlated with all the other variables X[2, :] = sum(X, 0) names = ['Jack', 'Oxana', 'John', 'Chelsea', 'Mark'] dendro = tls.FigureFactory.create_dendrogram(X, labels=names) - # Check that 2 is in a separate cluster and it's labelled correctly - self.assertEqual(dendro['layout']['xaxis']['ticktext'][0], 'John') + expected_dendro = go.Figure( + data=go.Data([ + go.Scatter( + marker=go.Marker(color='rgb(61,153,112)'), + mode='lines', + xaxis='x', + yaxis='y' + ), + go.Scatter( + marker=go.Marker( + color='rgb(61,153,112)' + ), + mode='lines', + xaxis='x', + yaxis='y' + ), + go.Scatter( + marker=go.Marker(color='rgb(61,153,112)'), + mode='lines', + xaxis='x', + yaxis='y' + ), + go.Scatter( + marker=go.Marker(color='rgb(0,116,217)'), + mode='lines', + xaxis='x', + yaxis='y' + ) + ]), + layout=go.Layout( + autosize=False, + height='100%', + hovermode='closest', + showlegend=False, + width='100%', + xaxis=go.XAxis( + mirror='allticks', + rangemode='tozero', + showgrid=False, + showline=True, + showticklabels=True, + tickmode='array', + ticks='outside', + tickvals=[5.0, 15.0, 25.0, 35.0, 45.0], + type='linear', + zeroline=False + ), + yaxis=go.YAxis( + mirror='allticks', + rangemode='tozero', + showgrid=False, + showline=True, + showticklabels=True, + ticks='outside', + type='linear', + zeroline=False + ) + ) + ) + + self.assertEqual(len(dendro['data']), 4) + + # it's random, so we can only check that the values aren't equal + y_vals = [dendro['data'][0].pop('y'), dendro['data'][1].pop('y'), + dendro['data'][2].pop('y'), dendro['data'][3].pop('y')] + for i in range(len(y_vals)): + for j in range(len(y_vals)): + if i != j: + self.assertFalse(np.allclose(y_vals[i], y_vals[j])) + + x_vals = [dendro['data'][0].pop('x'), dendro['data'][1].pop('x'), + dendro['data'][2].pop('x'), dendro['data'][3].pop('x')] + for i in range(len(x_vals)): + for j in range(len(x_vals)): + if i != j: + self.assertFalse(np.allclose(x_vals[i], x_vals[j])) + + # we also need to check the ticktext manually + xaxis_ticktext = dendro['layout']['xaxis'].pop('ticktext') + self.assertEqual(xaxis_ticktext[0], 'John') + + # this is actually a bit clearer when debugging tests. + self.assert_dict_equal(dendro['data'][0], expected_dendro['data'][0]) + self.assert_dict_equal(dendro['data'][1], expected_dendro['data'][1]) + self.assert_dict_equal(dendro['data'][2], expected_dendro['data'][2]) + self.assert_dict_equal(dendro['data'][3], expected_dendro['data'][3]) + + self.assert_dict_equal(dendro['layout'], expected_dendro['layout']) def test_dendrogram_orientation(self): X = np.random.rand(5, 5) From f3b57fcff19dc0f9053308e0610a07fe602b907e Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 01:35:56 +0700 Subject: [PATCH 28/35] Fix duplicated `test_dendrogram_orientation` test. --- plotly/tests/test_optional/test_opt_tracefactory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index 2b76e1b2edb..1c7c7a3feb3 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -447,7 +447,7 @@ def test_dendrogram_orientation(self): tickvals_top = np.array(dendro_top['layout']['xaxis']['tickvals']) self.assertTrue((tickvals_top <= 0).all()) - def test_dendrogram_orientation(self): + def test_dendrogram_orientation_two(self): X = np.array([[1, 2, 3, 4], [1, 1, 3, 4], [1, 2, 1, 4], From 3cf13aecb5bbe4ea7a39956a471e62f6a1108696 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 01:41:17 +0700 Subject: [PATCH 29/35] Lock down `test_dendrogram_orientation_two` spec. --- .../test_optional/test_opt_tracefactory.py | 72 +++++++++++++++++-- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index 1c7c7a3feb3..9e77db9de6e 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -463,9 +463,69 @@ def test_dendrogram_orientation_two(self): 'rgb(245,245,245)'] # white smoke dendro = tls.FigureFactory.create_dendrogram(X, colorscale=greyscale) - self.assertEqual(dendro["data"][0]['marker']['color'], - 'rgb(128,128,128)') - self.assertEqual(dendro["data"][1]['marker']['color'], - 'rgb(128,128,128)') - self.assertEqual(dendro["data"][2]['marker']['color'], - 'rgb(0,0,0)') + + expected_dendro = go.Figure( + data=go.Data([ + go.Scatter( + x=np.array([25., 25., 35., 35.]), + y=np.array([0., 1., 1., 0.]), + marker=go.Marker(color='rgb(128,128,128)'), + mode='lines', + xaxis='x', + yaxis='y' + ), + go.Scatter( + x=np.array([15., 15., 30., 30.]), + y=np.array([0., 2.23606798, 2.23606798, 1.]), + marker=go.Marker(color='rgb(128,128,128)'), + mode='lines', + xaxis='x', + yaxis='y' + ), + go.Scatter( + x=np.array([5., 5., 22.5, 22.5]), + y=np.array([0., 3.60555128, 3.60555128, 2.23606798]), + marker=go.Marker(color='rgb(0,0,0)'), + mode='lines', + xaxis='x', + yaxis='y' + ) + ]), + layout=go.Layout( + autosize=False, + height='100%', + hovermode='closest', + showlegend=False, + width='100%', + xaxis=go.XAxis( + mirror='allticks', + rangemode='tozero', + showgrid=False, + showline=True, + showticklabels=True, + tickmode='array', + ticks='outside', + ticktext=np.array(['3', '2', '0', '1']), + tickvals=[5.0, 15.0, 25.0, 35.0], + type='linear', + zeroline=False + ), + yaxis=go.YAxis( + mirror='allticks', + rangemode='tozero', + showgrid=False, + showline=True, + showticklabels=True, + ticks='outside', + type='linear', + zeroline=False + ) + ) + ) + + self.assertEqual(len(dendro['data']), 3) + + # this is actually a bit clearer when debugging tests. + self.assert_dict_equal(dendro['data'][0], expected_dendro['data'][0]) + self.assert_dict_equal(dendro['data'][1], expected_dendro['data'][1]) + self.assert_dict_equal(dendro['data'][2], expected_dendro['data'][2]) From dab5d3700d3fe9903653e7397705a089f4f4ebfe Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 01:42:43 +0700 Subject: [PATCH 30/35] =?UTF-8?q?Rename=20`..=5Forientation=5Ftwo`=20?= =?UTF-8?q?=E2=80=94>=20`..=5Fcolorscale`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I think this was likely the intent? --- plotly/tests/test_optional/test_opt_tracefactory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index 9e77db9de6e..db5b2afbbeb 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -447,7 +447,7 @@ def test_dendrogram_orientation(self): tickvals_top = np.array(dendro_top['layout']['xaxis']['tickvals']) self.assertTrue((tickvals_top <= 0).all()) - def test_dendrogram_orientation_two(self): + def test_dendrogram_colorscale(self): X = np.array([[1, 2, 3, 4], [1, 1, 3, 4], [1, 2, 1, 4], From 89592cdc9a1a0ad573f970bd306a0ee4ce1c41a2 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 01:44:58 +0700 Subject: [PATCH 31/35] =?UTF-8?q?PY3=20fix.=20Can=E2=80=99t=20index=20a=20?= =?UTF-8?q?`.keys()`=20view.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plotly/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index d812da7331d..bf688f741f1 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -3282,7 +3282,7 @@ def get_color_dict(self, colorscale): 'rgb(255,220,0)'] # yellow for i in range(len(default_colors.keys())): - k = default_colors.keys()[i] + k = list(default_colors.keys())[i] # PY3 won't index keys if i < len(colorscale): default_colors[k] = colorscale[i] From 3e3de24380ecdcbca8c13df8f5fa9098d48bd641 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 02:05:10 +0700 Subject: [PATCH 32/35] Loosen `dtype` on constructor in test. (PY3 fail). --- plotly/tests/test_optional/test_opt_tracefactory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_opt_tracefactory.py index db5b2afbbeb..f9e107c5dca 100644 --- a/plotly/tests/test_optional/test_opt_tracefactory.py +++ b/plotly/tests/test_optional/test_opt_tracefactory.py @@ -294,7 +294,7 @@ def test_default_dendrogram(self): showticklabels=True, tickmode='array', ticks='outside', - ticktext=np.array(['3', '2', '0', '1'], dtype='|S1'), + ticktext=np.array(['3', '2', '0', '1']), tickvals=[5.0, 15.0, 25.0, 35.0], type='linear', zeroline=False From 76361e6a645ff52238237ff224310d6b38d5e3d5 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 02:07:10 +0700 Subject: [PATCH 33/35] Rename test suite for figure factory. --- .../{test_opt_tracefactory.py => test_figure_factory.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plotly/tests/test_optional/{test_opt_tracefactory.py => test_figure_factory.py} (100%) diff --git a/plotly/tests/test_optional/test_opt_tracefactory.py b/plotly/tests/test_optional/test_figure_factory.py similarity index 100% rename from plotly/tests/test_optional/test_opt_tracefactory.py rename to plotly/tests/test_optional/test_figure_factory.py From 01d2826247a49ce3d6c5431416eefe7d9a14d335 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 11:08:26 +0700 Subject: [PATCH 34/35] For consistency use Fixed and Added in changelog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5781877d13..b5e1f0f4b97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ## [1.8.6] - 2015-09-28 +### Fixed - Saving "world_readable" to your config file via `plotly.tools.set_config` actually works. + +### Added - You can also save `auto_open` and `sharing` to the config file so that you can forget these keyword argument in `py.iplot` and `py.plot`. From 3815a8f9dd13c1ff005d2ddb9b5727fc5d034a1d Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 1 Oct 2015 11:10:05 +0700 Subject: [PATCH 35/35] =?UTF-8?q?version=20bump=20=E2=80=94>=201.8.7=20(ad?= =?UTF-8?q?ded=20dendrogram)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ plotly/version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5e1f0f4b97..10172c4b645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [1.8.7] - 2015-10-01 +### Added +- The FigureFactory can now create dendrogram plots with `.create_dendrogram`. + ## [1.8.6] - 2015-09-28 ### Fixed - Saving "world_readable" to your config file via `plotly.tools.set_config` actually works. diff --git a/plotly/version.py b/plotly/version.py index 871921a52a6..655be52946c 100644 --- a/plotly/version.py +++ b/plotly/version.py @@ -1 +1 @@ -__version__ = '1.8.6' +__version__ = '1.8.7'