From 9b2f4c060951e14b8774d55201869a733a1b07fe Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 22 Dec 2014 16:29:09 -0500 Subject: [PATCH 001/103] add specs to revamped get_subplots() --- plotly/tools.py | 75 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index f7bdb1c492a..dc53c5c080e 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -387,8 +387,7 @@ def mpl_to_plotly(fig, resize=False, strip_style=False, verbose=False): ### graph_objs related tools ### # TODO: Scale spacing based on number of plots and figure size -def get_subplots(rows=1, columns=1, horizontal_spacing=0.1, - vertical_spacing=0.15, print_grid=False): +def get_subplots(*arg, **kwargs): """Return a dictionary instance with the subplots set in 'layout'. Example 1: @@ -401,25 +400,73 @@ def get_subplots(rows=1, columns=1, horizontal_spacing=0.1, # print out string showing the subplot grid you've put in the layout fig = tools.get_subplots(rows=3, columns=2, print_grid=True) - key (types, default=default): - description. + fig (arg[0]): + Plotly figure object or dictionary. - rows (int, default=1): - Number of rows, evenly spaced vertically on the figure. + By default, get_subplots add keys 'xaxis[1-9]' and 'yaxis [1-9]' + to fig['layout'] - columns (int, default=1): - Number of columns, evenly spaced horizontally on the figure. + If fig['data'] contains ONLY 3D traces, get_subplots add keys + 'scene[1-9]' to fig['layout'] - horizontal_spacing (float in [0,1], default=0.1): - Space between subplot columns. Applied to all columns. + rows (kwarg, int, default=1): + Number of rows on the figure. - vertical_spacing (float in [0,1], default=0.05): - Space between subplot rows. Applied to all rows. + columns (kwarg, int, default=1): + Number of columns on the figure. - print_grid (True | False, default=False): - If True, prints a tab-delimited string representation of your plot grid. + arrangements (kwarg, list or list of lists, default=[]): + Subplot arrangement as a list (or list of lists) of subplot indices. + Overrides the 'rows' and 'columns' arguments. + Use integers 1, 2, ... for 2d subplots. + Use 'scene1', 'scene2', ... for 3d subscenes. + + The x-domain of each subplot i is given by: + + number of index i in row / total number of indices in row + + The y-domain of each subplot is given by: + + number of index i in column / total number of indices in column + + ex1: [[1, 2, 3], [4, 5], [6]] + ex2: [[1], [1, 2], [1, 3]] + ex3: [1, 'scene1'] + + horizontal_spacing (kwarg, float in [0,1] or list, default=0.1): + Space between subplot columns. + Applied to all columns if float. + Applied to per column from left to right if list. + Applied to per column from left to right and per row from bottom to top + if list of lists. + + vertical_spacing (kwarg, float in [0,1] or list, default=0.05): + Space between subplot rows. + Applied to all rows if float. + Applied to per row from bottom to top if list. + Applied to per row from bottom to top and per column from left to right + if list of lists. + + shared_xaxes (kwarg, boolean or list, default=False) + Assign shared x axes. + If True, share all x axes. + If list of booleans, share all x axes per column from left to right. + If list of integers, share x axes per subplot index, + set in 'arrangement'. + + shared_yaxes (kwarg, boolean or list, default=False) + Assign shared y axes. + If True, share all y axes. + If list of booleans, share all y axes per row from left to right. + If list of integers, share y axes per subplot index, + set in 'arrangement'. + + print_grid (kwarg, boolean, default=False): + If True, prints a tab-delimited string representation of + your plot grid. """ + fig = dict(layout=graph_objs.Layout()) # will return this at the end plot_width = (1 - horizontal_spacing * (columns - 1)) / columns plot_height = (1 - vertical_spacing * (rows - 1)) / rows From 637c5dec42a9ac0f5ac0f430c322c35a38d53d31 Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 18:59:48 -0500 Subject: [PATCH 002/103] sync latest graph ref master --- plotly/graph_reference/graph_objs_meta.json | 10 ++++++++++ submodules/graph_reference | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/plotly/graph_reference/graph_objs_meta.json b/plotly/graph_reference/graph_objs_meta.json index 786df295e10..61425e31548 100644 --- a/plotly/graph_reference/graph_objs_meta.json +++ b/plotly/graph_reference/graph_objs_meta.json @@ -3317,6 +3317,16 @@ ], "description": "Sets the camera position with respect to the scene. The first entry (a list or 1d numpy array of length 4) sets the angular position of the camera. The second entry (a list or 1d numpy array of length 3) sets the (x,y,z) translation of the camera. The third entry (a scalar) sets zoom of the camera." }, + "domain": { + "key_type": "plot_info", + "val_types": "domain dictionary", + "required": false, + "examples": [ + "{'x': [0, 0.4], 'y': [0.6, 1]}", + "dict(x=[0, 0.4], y=[0.6, 1])" + ], + "description": "Sets the x-y domain of this scene on the plotting surface." + }, "bgcolor": { "key_type": "style", "val_types": "a string describing color", diff --git a/submodules/graph_reference b/submodules/graph_reference index 9d31f815edc..9ce9ade27da 160000 --- a/submodules/graph_reference +++ b/submodules/graph_reference @@ -1 +1 @@ -Subproject commit 9d31f815edc59bbf1a9533e241cc4f41ed859fdc +Subproject commit 9ce9ade27da2554bde2b98b99c48fdfd2078f0d1 From 55637e02b74a042ce9ffb27a79731fc0cc41bd2b Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:05:56 -0500 Subject: [PATCH 003/103] update get_subplots() docstring --- plotly/tools.py | 115 +++++++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 49 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index dc53c5c080e..c504048e15e 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -387,8 +387,11 @@ def mpl_to_plotly(fig, resize=False, strip_style=False, verbose=False): ### graph_objs related tools ### # TODO: Scale spacing based on number of plots and figure size -def get_subplots(*arg, **kwargs): - """Return a dictionary instance with the subplots set in 'layout'. +def get_subplots(rows=1, columns=1, + shared_xaxes=False, shared_yaxes=False, + print_grid=False, **kwargs): + """Return an instance of plotly.graph_objs.Figure + with the subplots domain set in 'layout'. Example 1: # stack two subplots vertically @@ -397,17 +400,24 @@ def get_subplots(*arg, **kwargs): fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] Example 2: + # subplots with shared x axes + fig = tools.get_subplots(rows=2, shared_xaxes=True) + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y1')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y2')] + + Example 3: + # irregular subplot layout + fig = tools.get_subplots(rows=2, columns=2, + specs=[[{}, {}], [{'colspan': 2}]]) + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x3', yaxis='y3')] + + Example 4: # print out string showing the subplot grid you've put in the layout fig = tools.get_subplots(rows=3, columns=2, print_grid=True) - fig (arg[0]): - Plotly figure object or dictionary. - - By default, get_subplots add keys 'xaxis[1-9]' and 'yaxis [1-9]' - to fig['layout'] - - If fig['data'] contains ONLY 3D traces, get_subplots add keys - 'scene[1-9]' to fig['layout'] + Keywords arguments with constant defaults: rows (kwarg, int, default=1): Number of rows on the figure. @@ -415,56 +425,63 @@ def get_subplots(*arg, **kwargs): columns (kwarg, int, default=1): Number of columns on the figure. - arrangements (kwarg, list or list of lists, default=[]): - Subplot arrangement as a list (or list of lists) of subplot indices. - Overrides the 'rows' and 'columns' arguments. + shared_xaxes (kwarg, boolean or list, default=False) + Assign shared x axes. + If True, all x axes are shared. + To assign shared x axes per subplot grid cell (see 'specs'), + send list (or list of lists, one list per shared axis) + of cell index tuples. - Use integers 1, 2, ... for 2d subplots. - Use 'scene1', 'scene2', ... for 3d subscenes. + shared_yaxes (kwarg, boolean or list, default=False) + Assign shared y axes. + If True, all y axes are shared. + To assign shared y axes per subplot grid cell (see 'specs'), + send list (or list of lists, one list per shared axis) + of cell index tuples. - The x-domain of each subplot i is given by: + print_grid (kwarg, boolean, default=False): + If True, prints a tab-delimited string representation of + your plot grid. - number of index i in row / total number of indices in row + Keyword arguments with variable defaults: - The y-domain of each subplot is given by: + horizontal_spacing (kwarg, float in [0,1], default=0.2 / columns): + Space between subplot columns. - number of index i in column / total number of indices in column + Applies to all columns (use 'specs' subplot-dependents spacing) - ex1: [[1, 2, 3], [4, 5], [6]] - ex2: [[1], [1, 2], [1, 3]] - ex3: [1, 'scene1'] + vertical_spacing (kwarg, float in [0,1], default=0.3 / rows): + Space between subplot rows. + Applies to all rows (use 'specs' subplot-dependents spacing) - horizontal_spacing (kwarg, float in [0,1] or list, default=0.1): - Space between subplot columns. - Applied to all columns if float. - Applied to per column from left to right if list. - Applied to per column from left to right and per row from bottom to top - if list of lists. + specs (kwarg, list (of lists) of dictionaries): + Subplot specifications. - vertical_spacing (kwarg, float in [0,1] or list, default=0.05): - Space between subplot rows. - Applied to all rows if float. - Applied to per row from bottom to top if list. - Applied to per row from bottom to top and per column from left to right - if list of lists. + - Indices of the outer list correspond to subplot grid rows + starting from the bottom. - shared_xaxes (kwarg, boolean or list, default=False) - Assign shared x axes. - If True, share all x axes. - If list of booleans, share all x axes per column from left to right. - If list of integers, share x axes per subplot index, - set in 'arrangement'. + - Indices of the inner lists correspond to subplot grid columns + starting from the left - shared_yaxes (kwarg, boolean or list, default=False) - Assign shared y axes. - If True, share all y axes. - If list of booleans, share all y axes per row from left to right. - If list of integers, share y axes per subplot index, - set in 'arrangement'. + - Note that specs[0][0] has the specs for the bottom-left subplot - print_grid (kwarg, boolean, default=False): - If True, prints a tab-delimited string representation of - your plot grid. + - Each item in the 'specs' list corresponds to one subplot + in a subplot grid. The subplot grid has 'rows' times 'columns' + cells. + + - Each item in the 'specs' is a dictionary. + The available keys are: + + * isEmpty (boolean, default=False): flag for empty grid cells + * is3D (boolean, default=False): flag for 3d scenes + * colspan (int, default=1): span across grid columns + from left to right + * rowspan (int, default=1): span across grid rows + from bottom to top + * l (float, default=0.0): padding left of cell + * r (float, default=0.0): padding right of cell + * t (float, default=0.0): padding right of cell + * b (float, default=0.0): padding bottom of cell """ fig = dict(layout=graph_objs.Layout()) # will return this at the end From 1b1d23a56504f796fa5629332662f8a43c9b0f6d Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:06:34 -0500 Subject: [PATCH 004/103] remove old get_subplots() code --- plotly/tools.py | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index c504048e15e..3f054e4fc5c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -386,7 +386,6 @@ def mpl_to_plotly(fig, resize=False, strip_style=False, verbose=False): ### graph_objs related tools ### -# TODO: Scale spacing based on number of plots and figure size def get_subplots(rows=1, columns=1, shared_xaxes=False, shared_yaxes=False, print_grid=False, **kwargs): @@ -484,37 +483,9 @@ def get_subplots(rows=1, columns=1, * b (float, default=0.0): padding bottom of cell """ - fig = dict(layout=graph_objs.Layout()) # will return this at the end - plot_width = (1 - horizontal_spacing * (columns - 1)) / columns - plot_height = (1 - vertical_spacing * (rows - 1)) / rows - plot_num = 0 - for rrr in range(rows): - for ccc in range(columns): - xaxis_name = 'xaxis{0}'.format(plot_num + 1) - x_anchor = 'y{0}'.format(plot_num + 1) - x_start = (plot_width + horizontal_spacing) * ccc - x_end = x_start + plot_width - - yaxis_name = 'yaxis{0}'.format(plot_num + 1) - y_anchor = 'x{0}'.format(plot_num + 1) - 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) - fig['layout'][xaxis_name] = xaxis - yaxis = graph_objs.YAxis(domain=[y_start, y_end], anchor=y_anchor) - fig['layout'][yaxis_name] = yaxis - plot_num += 1 if print_grid: print("This is the format of your plot grid!") grid_string = "" - plot = 1 - for rrr in range(rows): - grid_line = "" - for ccc in range(columns): - grid_line += "[{0}]\t".format(plot) - plot += 1 - grid_string = grid_line + '\n' + grid_string print(grid_string) return graph_objs.Figure(fig) # forces us to validate what we just did... From 76c6d4eb5941f891909ef6032b69b06b3cbc3730 Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:07:06 -0500 Subject: [PATCH 005/103] add Exception if rows or/and columns are not integers --- plotly/tools.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 3f054e4fc5c..deedb22fc2e 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -483,6 +483,12 @@ def get_subplots(rows=1, columns=1, * b (float, default=0.0): padding bottom of cell """ + # Throw exception for non-integer rows and columns + if not isinstance(rows, int): + raise Exception("Keyword argument 'rows' must be an int") + if not isinstance(columns, int): + raise Exception("Keyword argument 'columns' must be an int") + if print_grid: print("This is the format of your plot grid!") grid_string = "" From 9f1c240f9d9d3ffd58ea376ba9ed2c9336e8a98a Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:09:55 -0500 Subject: [PATCH 006/103] make h/v spacing depend on the number of rows/columns: - breaks backwards compatibility - horz = 0.2 / columns (same as old if rows==2) - vert = 0.3 / rows (same as old if columns==2) --- plotly/tools.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index deedb22fc2e..0c781be02ba 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -489,6 +489,16 @@ def get_subplots(rows=1, columns=1, if not isinstance(columns, int): raise Exception("Keyword argument 'columns' must be an int") + # Set 'horizontal_spacing' / 'vertical_spacing' w.r.t. rows / columns + try: + horizontal_spacing = float(kwargs['horizontal_spacing']) + except KeyError: + horizontal_spacing = 0.2 / columns + try: + vertical_spacing = float(kwargs['vertical_spacing']) + except KeyError: + vertical_spacing = 0.3 / rows + if print_grid: print("This is the format of your plot grid!") grid_string = "" From 5406fb3550d8fe033025ddafc8dfce607f859113 Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:12:40 -0500 Subject: [PATCH 007/103] initialize specs list of lists: - subplot domain computation are made using specs whether or not it is a supplied argument --- plotly/tools.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 0c781be02ba..8313e76882b 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -499,6 +499,37 @@ def get_subplots(rows=1, columns=1, except KeyError: vertical_spacing = 0.3 / rows + # Sanitize 'specs' -- TODO more sanitizing? + try: + specs = kwargs['specs'] + if not isinstance(specs, list): + raise Exception("Keyword argument 'specs' must be a list") + elif isinstance(specs[0], dict): + # To support one-row specs=[{},{}] + specs = [specs] + except KeyError: + specs = [[{} + for col in range(columns)] + for row in range(rows)] # default 'specs' + + # Default spec key-values + SPEC_defaults = dict( + isEmpty=False, + is3D=False, + colspan=1, + rowspan=1, + l=0.0, + r=0.0, + t=0.0, + b=0.0 + ) + + # Fill in 'specs' with defaults + for spec_row in specs: + for spec in spec_row: + for k in SPEC_defaults.keys(): + if k not in spec.keys(): + spec[k] = SPEC_defaults[k] if print_grid: print("This is the format of your plot grid!") grid_string = "" From 26f704e37d68ce19ddb82551eb6fa0377211d058 Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:13:03 -0500 Subject: [PATCH 008/103] compute some common distances --- plotly/tools.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 8313e76882b..7e941dd7b99 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -530,6 +530,14 @@ def get_subplots(rows=1, columns=1, for k in SPEC_defaults.keys(): if k not in spec.keys(): spec[k] = SPEC_defaults[k] + + # Width / Height of each subplot cell (exclud. spacing and padding) + width = 1. / columns + height = 1. / rows + + # Spacing corrections between each subplot + x_space = horizontal_spacing * (columns - 1) / columns + y_space = vertical_spacing * (rows - 1) / rows if print_grid: print("This is the format of your plot grid!") grid_string = "" From d9341c8b4c23614c7e5f85bdc448ea3d1cca7dfb Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:17:02 -0500 Subject: [PATCH 009/103] loop through specs: - use tracers (x and y) to keep track of position from subplot to subplot - cases where colspan > 1 or rowspan > 1 require more attention - use _get_shared() to get the shared axis (if any) - use _add_domain() to paste result into fig object - use _fill_grid() to generate the str repr of the subplot grid --- plotly/tools.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 7e941dd7b99..14d221fd360 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -538,6 +538,53 @@ def get_subplots(rows=1, columns=1, # Spacing corrections between each subplot x_space = horizontal_spacing * (columns - 1) / columns y_space = vertical_spacing * (rows - 1) / rows + + # Loop through 'specs' + for row, spec_row in enumerate(specs): + + x = 0 # init x tracer before row + + for col, spec in enumerate(spec_row): + + # Get x domain (and correct x_e for colspan > 1) + x_s = x + spec['l'] + x_e = x_s + spec['colspan'] * width + x_e -= (spec['l'] + spec['r'] + x_space) + if spec['colspan'] > 1: + x_e += (spec['colspan'] - 1) * (horizontal_spacing - x_space) + + # Get y domain (and correct y_e for rowspan > 1) + y_s = y + spec['b'] + y_e = y_s + spec['rowspan'] * height + y_e -= (spec['b'] + spec['t'] + y_space) + if spec['rowspan'] > 1: + y_e += (spec['rowspan'] - 1) * (vertical_spacing - y_space) + + # Add domains to fig! + if not spec['isEmpty']: + if spec['is3D']: + _add_domain_is3D(fig, s_cnt, [x_s, x_e], [y_s, y_e]) + _fill_grid(grid, (row, col), spec, s_cnt, False) + s_cnt += 1 + + else: + x_shared = get_shared((row, col), 'x', shared_xaxes) + y_shared = get_shared((row, col), 'y', shared_yaxes) + _fill_grid(grid, (row, col), spec, + (x_cnt, y_cnt), (x_shared, y_shared)) + if not x_shared: + _add_domain(fig, 'x', x_cnt, y_shared, [x_s, x_e]) + x_cnt += 1 + if not y_shared: + _add_domain(fig, 'y', y_cnt, x_shared, [y_s, y_e]) + y_cnt += 1 + else: + _fill_grid(grid, (row, col), spec, False, (False, False)) + + x = x_e + horizontal_spacing # move tracer to next col + + y = y_e + vertical_spacing # move tracer to next row + if print_grid: print("This is the format of your plot grid!") grid_string = "" From 8705058380b2d88f1ed7f8dd66668f0b62416b39 Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:17:18 -0500 Subject: [PATCH 010/103] add _get_shared() --- plotly/tools.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 14d221fd360..18b377bceb9 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -539,6 +539,44 @@ def get_subplots(rows=1, columns=1, x_space = horizontal_spacing * (columns - 1) / columns y_space = vertical_spacing * (rows - 1) / rows + # Handler for shared axes logic (return shared axis label or False) + def _get_shared(cell, x_or_y, shared_axes): + if isinstance(shared_axes, bool): + if not shared_axes: + return False + if x_or_y == 'x': + if cell[0] == 0: + return False + else: + return "x{0}".format(cell[1]+1) + elif x_or_y == 'y': + if cell[1] == 0: + return False + else: + return "y{0}".format(cell[0]+1) + elif isinstance(shared_axes, list): + if isinstance(shared_axes[0], tuple): + if cell in shared_axes and cell != shared_axes[0]: + return { + 'x': "x{0}".format(shared_axes[0][1]+1), + 'y': "y{0}".format(shared_axes[0][0]+1) + }[x_or_y] + else: + return False + elif isinstance(shared_axes[0], list): + for shared_axis in shared_axes: + if cell in shared_axis and cell != shared_axis[0]: + return { + 'x': "x{0}".format(shared_axis[0][1]+1), + 'y': "y{0}".format(shared_axis[0][0]+1) + }[x_or_y] + else: + return False + else: + return False + else: + return False + # Loop through 'specs' for row, spec_row in enumerate(specs): From 842ff88a9dcb715202828281e61e16844bededbc Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:17:38 -0500 Subject: [PATCH 011/103] add _add_domain() and _add_domain_is3D() --- plotly/tools.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 18b377bceb9..a7b9090d28f 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -577,6 +577,25 @@ def _get_shared(cell, x_or_y, shared_axes): else: return False + # Function pasting x/y domains in fig object (2d anchored case) + def _add_domain(fig, x_or_y, cnt, shared, domain): + num = cnt + 1 + y_or_x = {'x': 'y', 'y': 'x'}[x_or_y] + axis_name = '{x_or_y}axis{num}'.format(x_or_y=x_or_y, num=num) + graph_obj = '{X_or_Y}Axis'.format(X_or_Y=x_or_y.upper()) + axis = getattr(graph_objs, graph_obj)(domain=domain) + if not shared: # non-shared axes get an anchor + anchor = '{y_or_x}{num}'.format(y_or_x=y_or_x, num=num) + axis['anchor'] = anchor + fig['layout'][axis_name] = axis + + # Function pasting x/y domains in fig object (3d case) + def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): + num = s_cnt + 1 + scene_name = "scene{num}".format(num=num) + scene = graph_objs.Scene(domain={'x': x_domain, 'y': y_domain}) + fig['layout'][scene_name] = scene + # Loop through 'specs' for row, spec_row in enumerate(specs): From c55a92ff4a5c513700bc2dc5f1a2df5b91f18d41 Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:17:55 -0500 Subject: [PATCH 012/103] add _fill_grid() --- plotly/tools.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index a7b9090d28f..d15a31b5528 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -596,6 +596,34 @@ def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): scene = graph_objs.Scene(domain={'x': x_domain, 'y': y_domain}) fig['layout'][scene_name] = scene + # Function generating the grid's string repr + def _fill_grid(grid, cell, spec, cnt, shared): + if grid[cell[0]][cell[1]] != '': # Needed to make rowspan > 1 work + return + if spec['isEmpty']: + grid[cell[0]][cell[1]] = '{empty}' + return + if spec['is3D']: + grid[cell[0]][cell[1]] = ( + "[scene{0}".format(cnt) + + " -- " * (spec['colspan'] - 1) + + "]" + ) + else: + labels = ( + "x{0}".format(cnt[0]+1) if not shared[0] else shared[0], + "y{0}".format(cnt[1]+1) if not shared[1] else shared[1] + ) + grid[cell[0]][cell[1]] = ( + "[{0},{1}".format(*labels) + + " -- " * (spec['colspan'] - 1) + + "]" + ) + if spec['rowspan'] > 1: + for j in range(1, spec['rowspan']): + grid[cell[0]+j][cell[1]] = ' | ' + grid[cell[0]+j][cell[1]] = ' ^ ' + # Loop through 'specs' for row, spec_row in enumerate(specs): From 34ee502a8df4136dc0350e3d7a8bb86754881b43 Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:18:10 -0500 Subject: [PATCH 013/103] initialize looping variables --- plotly/tools.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index d15a31b5528..430d98baff7 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -624,6 +624,13 @@ def _fill_grid(grid, cell, spec, cnt, shared): grid[cell[0]+j][cell[1]] = ' | ' grid[cell[0]+j][cell[1]] = ' ^ ' + fig = dict(layout=graph_objs.Layout()) # init layout object + x_cnt = y_cnt = s_cnt = 0 # subplot counters + y = 0 # init y tracer + grid = [['' # init grid (for print_grid) + for row in spec_row] + for spec_row in specs] + # Loop through 'specs' for row, spec_row in enumerate(specs): From 97145a5293a56433c6a65d631531488d5128ae08 Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:18:46 -0500 Subject: [PATCH 014/103] (oops) get_shared -> _get_shared --- plotly/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 430d98baff7..5ceb3fa4b19 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -660,8 +660,8 @@ def _fill_grid(grid, cell, spec, cnt, shared): s_cnt += 1 else: - x_shared = get_shared((row, col), 'x', shared_xaxes) - y_shared = get_shared((row, col), 'y', shared_yaxes) + x_shared = _get_shared((row, col), 'x', shared_xaxes) + y_shared = _get_shared((row, col), 'y', shared_yaxes) _fill_grid(grid, (row, col), spec, (x_cnt, y_cnt), (x_shared, y_shared)) if not x_shared: From 5dfcfaded56ba61f396658c8f73c2c1b9dd6b97a Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:19:14 -0500 Subject: [PATCH 015/103] add print_grid --- plotly/tools.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 5ceb3fa4b19..f693d6e6913 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -680,7 +680,10 @@ def _fill_grid(grid, cell, spec, cnt, shared): if print_grid: print("This is the format of your plot grid!") grid_string = "" + for grid_row in grid: + grid_string = " ".join(grid_row) + '\n' + grid_string print(grid_string) + return graph_objs.Figure(fig) # forces us to validate what we just did... From f72a249fb78e8b041624b185e30e8add4cc85dc5 Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:20:54 -0500 Subject: [PATCH 016/103] modify computed domain in existing tests (because of rounding errors) --- .../test_core/test_tools/test_get_subplots.py | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 1d773d06012..f2d7293a9d3 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -73,7 +73,7 @@ def test_a_lot(): data=Data(), layout=Layout( xaxis1=XAxis( - domain=[0.0, 0.05714285714285713], + domain=[0.0, 0.05714285714285712], anchor='y1' ), xaxis10=XAxis( @@ -85,23 +85,23 @@ def test_a_lot(): anchor='y11' ), xaxis12=XAxis( - domain=[0.6285714285714286, 0.6857142857142857], + domain=[0.6285714285714286, 0.6857142857142856], anchor='y12' ), xaxis13=XAxis( - domain=[0.7857142857142857, 0.8428571428571429], + domain=[0.7857142857142856, 0.8428571428571426], anchor='y13' ), xaxis14=XAxis( - domain=[0.9428571428571428, 1.0], + domain=[0.9428571428571426, 0.9999999999999997], anchor='y14' ), xaxis15=XAxis( - domain=[0.0, 0.05714285714285713], + domain=[0.0, 0.05714285714285712], anchor='y15' ), xaxis16=XAxis( - domain=[0.15714285714285714, 0.21428571428571427], + domain=[0.15714285714285714, 0.21428571428571425], anchor='y16' ), xaxis17=XAxis( @@ -113,27 +113,27 @@ def test_a_lot(): anchor='y18' ), xaxis19=XAxis( - domain=[0.6285714285714286, 0.6857142857142857], + domain=[0.6285714285714286, 0.6857142857142856], anchor='y19' ), xaxis2=XAxis( - domain=[0.15714285714285714, 0.21428571428571427], + domain=[0.15714285714285714, 0.21428571428571425], anchor='y2' ), xaxis20=XAxis( - domain=[0.7857142857142857, 0.8428571428571429], + domain=[0.7857142857142856, 0.8428571428571426], anchor='y20' ), xaxis21=XAxis( - domain=[0.9428571428571428, 1.0], + domain=[0.9428571428571426, 0.9999999999999997], anchor='y21' ), xaxis22=XAxis( - domain=[0.0, 0.05714285714285713], + domain=[0.0, 0.05714285714285712], anchor='y22' ), xaxis23=XAxis( - domain=[0.15714285714285714, 0.21428571428571427], + domain=[0.15714285714285714, 0.21428571428571425], anchor='y23' ), xaxis24=XAxis( @@ -145,15 +145,15 @@ def test_a_lot(): anchor='y25' ), xaxis26=XAxis( - domain=[0.6285714285714286, 0.6857142857142857], + domain=[0.6285714285714286, 0.6857142857142856], anchor='y26' ), xaxis27=XAxis( - domain=[0.7857142857142857, 0.8428571428571429], + domain=[0.7857142857142856, 0.8428571428571426], anchor='y27' ), xaxis28=XAxis( - domain=[0.9428571428571428, 1.0], + domain=[0.9428571428571426, 0.9999999999999997], anchor='y28' ), xaxis3=XAxis( @@ -165,23 +165,23 @@ def test_a_lot(): anchor='y4' ), xaxis5=XAxis( - domain=[0.6285714285714286, 0.6857142857142857], + domain=[0.6285714285714286, 0.6857142857142856], anchor='y5' ), xaxis6=XAxis( - domain=[0.7857142857142857, 0.8428571428571429], + domain=[0.7857142857142856, 0.8428571428571426], anchor='y6' ), xaxis7=XAxis( - domain=[0.9428571428571428, 1.0], + domain=[0.9428571428571426, 0.9999999999999997], anchor='y7' ), xaxis8=XAxis( - domain=[0.0, 0.05714285714285713], + domain=[0.0, 0.05714285714285712], anchor='y8' ), xaxis9=XAxis( - domain=[0.15714285714285714, 0.21428571428571427], + domain=[0.15714285714285714, 0.21428571428571425], anchor='y9' ), yaxis1=YAxis( @@ -241,31 +241,31 @@ def test_a_lot(): anchor='x21' ), yaxis22=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.8624999999999999, 0.9999999999999998], anchor='x22' ), yaxis23=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.8624999999999999, 0.9999999999999998], anchor='x23' ), yaxis24=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.8624999999999999, 0.9999999999999998], anchor='x24' ), yaxis25=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.8624999999999999, 0.9999999999999998], anchor='x25' ), yaxis26=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.8624999999999999, 0.9999999999999998], anchor='x26' ), yaxis27=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.8624999999999999, 0.9999999999999998], anchor='x27' ), yaxis28=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.8624999999999999, 0.9999999999999998], anchor='x28' ), yaxis3=YAxis( @@ -309,11 +309,11 @@ def test_spacing(): anchor='y1' ), xaxis2=XAxis( - domain=[0.35, 0.6499999999999999], + domain=[0.35, 0.65], anchor='y2' ), xaxis3=XAxis( - domain=[0.7, 1.0], + domain=[0.7000000000000001, 1.0], anchor='y3' ), xaxis4=XAxis( @@ -321,11 +321,11 @@ def test_spacing(): anchor='y4' ), xaxis5=XAxis( - domain=[0.35, 0.6499999999999999], + domain=[0.35, 0.65], anchor='y5' ), xaxis6=XAxis( - domain=[0.7, 1.0], + domain=[0.7000000000000001, 1.0], anchor='y6' ), yaxis1=YAxis( From 857a7d77c182f546d2d461c8e37037e840e42eac Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:22:25 -0500 Subject: [PATCH 017/103] modify arguments in existing tests: - because of new horizontal_spacing and vertical_spacing defaults --- .../tests/test_core/test_tools/test_get_subplots.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index f2d7293a9d3..24d43c083d6 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -298,7 +298,11 @@ def test_a_lot(): ) ) ) - assert tls.get_subplots(4, 7) == expected + + fig = tls.get_subplots(4, 7, + horizontal_spacing=0.1, + vertical_spacing=0.15) + assert fig == expected def test_spacing(): expected = Figure( @@ -354,5 +358,8 @@ def test_spacing(): ) ) ) + fig = tls.get_subplots(2, 3, + horizontal_spacing=.05, + vertical_spacing=.1) - assert expected == tls.get_subplots(2, 3, .05, .1) + assert fig == expected From e64794b0255162d0765c5328343a86934f978afc Mon Sep 17 00:00:00 2001 From: etpinard Date: Mon, 29 Dec 2014 19:24:19 -0500 Subject: [PATCH 018/103] test galore git diff --cached ! --- .../test_core/test_tools/test_get_subplots.py | 694 ++++++++++++++++++ 1 file changed, 694 insertions(+) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 24d43c083d6..82ea0ebd277 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -1,6 +1,7 @@ import plotly from plotly.graph_objs import * import plotly.tools as tls +from nose.tools import raises def test_get_single_plot(): expected = Figure( @@ -363,3 +364,696 @@ def test_spacing(): vertical_spacing=.1) assert fig == expected + +@raises(Exception) +def test_non_integer_rows(): + fig = tls.get_subplots(rows=2.1) + +@raises(Exception) +def test_non_integer_columns(): + fig = tls.get_subplots(columns=2/3) + +def test_default_spacing(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.168], + anchor='y1' + ), + xaxis10=XAxis( + domain=[0.8320000000000001, 1.0], + anchor='y10' + ), + xaxis11=XAxis( + domain=[0.0, 0.168], + anchor='y11' + ), + xaxis12=XAxis( + domain=[0.20800000000000002, 0.376], + anchor='y12' + ), + xaxis13=XAxis( + domain=[0.416, 0.584], + anchor='y13' + ), + xaxis14=XAxis( + domain=[0.624, 0.792], + anchor='y14' + ), + xaxis15=XAxis( + domain=[0.8320000000000001, 1.0], + anchor='y15' + ), + xaxis16=XAxis( + domain=[0.0, 0.168], + anchor='y16' + ), + xaxis17=XAxis( + domain=[0.20800000000000002, 0.376], + anchor='y17' + ), + xaxis18=XAxis( + domain=[0.416, 0.584], + anchor='y18' + ), + xaxis19=XAxis( + domain=[0.624, 0.792], + anchor='y19' + ), + xaxis2=XAxis( + domain=[0.20800000000000002, 0.376], + anchor='y2' + ), + xaxis20=XAxis( + domain=[0.8320000000000001, 1.0], + anchor='y20' + ), + xaxis21=XAxis( + domain=[0.0, 0.168], + anchor='y21' + ), + xaxis22=XAxis( + domain=[0.20800000000000002, 0.376], + anchor='y22' + ), + xaxis23=XAxis( + domain=[0.416, 0.584], + anchor='y23' + ), + xaxis24=XAxis( + domain=[0.624, 0.792], + anchor='y24' + ), + xaxis25=XAxis( + domain=[0.8320000000000001, 1.0], + anchor='y25' + ), + xaxis26=XAxis( + domain=[0.0, 0.168], + anchor='y26' + ), + xaxis27=XAxis( + domain=[0.20800000000000002, 0.376], + anchor='y27' + ), + xaxis28=XAxis( + domain=[0.416, 0.584], + anchor='y28' + ), + xaxis29=XAxis( + domain=[0.624, 0.792], + anchor='y29' + ), + xaxis3=XAxis( + domain=[0.416, 0.584], + anchor='y3' + ), + xaxis30=XAxis( + domain=[0.8320000000000001, 1.0], + anchor='y30' + ), + xaxis4=XAxis( + domain=[0.624, 0.792], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.8320000000000001, 1.0], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.0, 0.168], + anchor='y6' + ), + xaxis7=XAxis( + domain=[0.20800000000000002, 0.376], + anchor='y7' + ), + xaxis8=XAxis( + domain=[0.416, 0.584], + anchor='y8' + ), + xaxis9=XAxis( + domain=[0.624, 0.792], + anchor='y9' + ), + yaxis1=YAxis( + domain=[0.0, 0.125], + anchor='x1' + ), + yaxis10=YAxis( + domain=[0.175, 0.3], + anchor='x10' + ), + yaxis11=YAxis( + domain=[0.35, 0.4749999999999999], + anchor='x11' + ), + yaxis12=YAxis( + domain=[0.35, 0.4749999999999999], + anchor='x12' + ), + yaxis13=YAxis( + domain=[0.35, 0.4749999999999999], + anchor='x13' + ), + yaxis14=YAxis( + domain=[0.35, 0.4749999999999999], + anchor='x14' + ), + yaxis15=YAxis( + domain=[0.35, 0.4749999999999999], + anchor='x15' + ), + yaxis16=YAxis( + domain=[0.5249999999999999, 0.6499999999999999], + anchor='x16' + ), + yaxis17=YAxis( + domain=[0.5249999999999999, 0.6499999999999999], + anchor='x17' + ), + yaxis18=YAxis( + domain=[0.5249999999999999, 0.6499999999999999], + anchor='x18' + ), + yaxis19=YAxis( + domain=[0.5249999999999999, 0.6499999999999999], + anchor='x19' + ), + yaxis2=YAxis( + domain=[0.0, 0.125], + anchor='x2' + ), + yaxis20=YAxis( + domain=[0.5249999999999999, 0.6499999999999999], + anchor='x20' + ), + yaxis21=YAxis( + domain=[0.7, 0.825], + anchor='x21' + ), + yaxis22=YAxis( + domain=[0.7, 0.825], + anchor='x22' + ), + yaxis23=YAxis( + domain=[0.7, 0.825], + anchor='x23' + ), + yaxis24=YAxis( + domain=[0.7, 0.825], + anchor='x24' + ), + yaxis25=YAxis( + domain=[0.7, 0.825], + anchor='x25' + ), + yaxis26=YAxis( + domain=[0.875, 1.0], + anchor='x26' + ), + yaxis27=YAxis( + domain=[0.875, 1.0], + anchor='x27' + ), + yaxis28=YAxis( + domain=[0.875, 1.0], + anchor='x28' + ), + yaxis29=YAxis( + domain=[0.875, 1.0], + anchor='x29' + ), + yaxis3=YAxis( + domain=[0.0, 0.125], + anchor='x3' + ), + yaxis30=YAxis( + domain=[0.875, 1.0], + anchor='x30' + ), + yaxis4=YAxis( + domain=[0.0, 0.125], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.0, 0.125], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.175, 0.3], + anchor='x6' + ), + yaxis7=YAxis( + domain=[0.175, 0.3], + anchor='x7' + ), + yaxis8=YAxis( + domain=[0.175, 0.3], + anchor='x8' + ), + yaxis9=YAxis( + domain=[0.175, 0.3], + anchor='x9' + ) + ) + ) + + fig = tls.get_subplots(rows=6, columns=5) + + assert fig == expected + +def test_subplot_specs(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.28888888888888886], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.0, 0.28888888888888886], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.3555555555555555, 0.6444444444444445], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y4' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.575, 1.0], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.575, 1.0], + anchor='x4' + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=3, specs=[[{}], [{}, {}, {}]]) + + assert fig == expected + +def test_subplot_specs_colspan(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 1.0], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.0, 0.45], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.55, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.0, 0.45], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.55, 1.0], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x5' + ) + ) + ) + + fig = tls.get_subplots(rows=3, columns=2, + specs=[[{'colspan':2}], + [{}, {}], + [{}, {}]]) + assert fig == expected + +def test_subplot_specs_rowspan(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.28888888888888886], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.3555555555555555, 0.6444444444444445], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.3555555555555555, 0.6444444444444445], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.3555555555555555, 1.0], + anchor='y6' + ), + yaxis1=YAxis( + domain=[0.0, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x6' + ) + ) + ) + + fig = tls.get_subplots(rows=3, columns=3, + specs=[[{'rowspan': 3}, {}, {}], + [{'isEmpty': True}, {}, {}], + [{'isEmpty': True}, {'colspan': 2}]]) + + assert fig == expected + +def test_subplot_specs_is3D(): + expected = Figure( + data=Data(), + layout=Layout( + scene1=Scene( + domain={'y': [0.0, 0.425], 'x': [0.0, 0.45]} + ), + scene2=Scene( + domain={'y': [0.575, 1.0], 'x': [0.0, 0.45]} + ), + xaxis1=XAxis( + domain=[0.55, 1.0], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.55, 1.0], + anchor='y2' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='x2' + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=2, + specs=[[{'is3D': True}, {}], [{'is3D': True}, {}]]) + + assert fig == expected + +def test_subplot_specs_padding(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.1, 0.5], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.5, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.0, 0.5], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.5, 0.9], + anchor='y4' + ), + yaxis1=YAxis( + domain=[0.0, 0.5], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.2, 0.49999999999999994], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.49999999999999994, 0.8], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.49999999999999994, 1.0], + anchor='x4' + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=2, + horizontal_spacing=0, vertical_spacing=0, + specs=[[{'l': 0.1}, {'b': 0.2}], + [{'t': 0.2}, {'r': 0.1}]]) + + assert fig == expected + +def test_subplot_shared_xaxes(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.28888888888888886], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.3555555555555555, 0.6444444444444445], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.425], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.425], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.575, 1.0] + ), + yaxis5=YAxis( + domain=[0.575, 1.0] + ), + yaxis6=YAxis( + domain=[0.575, 1.0] + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=3, shared_xaxes=True) + + assert fig == expected + +def test_subplot_shared_yaxes(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.45], + anchor='y1' + ), + xaxis10=XAxis( + domain=[0.55, 1.0] + ), + xaxis2=XAxis( + domain=[0.55, 1.0] + ), + xaxis3=XAxis( + domain=[0.0, 0.45], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.55, 1.0] + ), + xaxis5=XAxis( + domain=[0.0, 0.45], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.55, 1.0] + ), + xaxis7=XAxis( + domain=[0.0, 0.45], + anchor='y7' + ), + xaxis8=XAxis( + domain=[0.55, 1.0] + ), + xaxis9=XAxis( + domain=[0.0, 0.45], + anchor='y9' + ), + yaxis1=YAxis( + domain=[0.0, 0.15200000000000002], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.21200000000000002, 0.36400000000000005], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.42400000000000004, 0.5760000000000001], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.6360000000000001, 0.788], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.8480000000000001, 1.0], + anchor='x5' + ) + ) + ) + + fig = tls.get_subplots(rows=5, columns=2, shared_yaxes=True) + + assert fig == expected + +def test_subplot_shared_axes_list(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.45], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.55, 1.0] + ), + xaxis3=XAxis( + domain=[0.55, 1.0], + anchor='y3' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0] + ), + yaxis3=YAxis( + domain=[0.575, 1.0], + anchor='x3' + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=2, + shared_xaxes=[(0,0), (1,0)], + shared_yaxes=[(0,0), (0,1)]) + + assert fig == expected + + +def test_subplot_shared_axes_list_of_list(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.28888888888888886], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.3555555555555555, 0.6444444444444445], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.3555555555555555, 0.6444444444444445], + anchor='y4' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.425], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.425], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.575, 1.0] + ), + yaxis5=YAxis( + domain=[0.575, 1.0], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.575, 1.0] + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=3, + shared_xaxes=[[(0,0), (1,0)], [(0,2), (1,2)]]) + + assert fig == expected From ab661f4920051ad12900b4435af5fec9bc02ab62 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:36:22 -0500 Subject: [PATCH 019/103] improve algorithm (part 1): - built a grid list of lists (or exactly rows X columns cell) - grid stores the starting (x,y) pair of each cell - init grid_str used for the str representation --- plotly/tools.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index f693d6e6913..3c55ce32d19 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -531,13 +531,19 @@ def get_subplots(rows=1, columns=1, if k not in spec.keys(): spec[k] = SPEC_defaults[k] - # Width / Height of each subplot cell (exclud. spacing and padding) - width = 1. / columns - height = 1. / rows + # Set width & height of each subplot cell (excluding padding) + width = (1. - horizontal_spacing * (columns - 1)) / columns + height = (1. - vertical_spacing * (rows - 1)) / rows - # Spacing corrections between each subplot - x_space = horizontal_spacing * (columns - 1) / columns - y_space = vertical_spacing * (rows - 1) / rows + # Build subplot grid (tuple of starting coords for each cell) + grid = [[((width + horizontal_spacing) * column, + (height + vertical_spacing) * row) + for column in range(columns)] + for row in range(rows)] # all we need + + # Initialize the grid's string representation + if print_grid: + grid_str = [['' for column in range(columns)] for row in range(rows)] # Handler for shared axes logic (return shared axis label or False) def _get_shared(cell, x_or_y, shared_axes): @@ -627,9 +633,6 @@ def _fill_grid(grid, cell, spec, cnt, shared): fig = dict(layout=graph_objs.Layout()) # init layout object x_cnt = y_cnt = s_cnt = 0 # subplot counters y = 0 # init y tracer - grid = [['' # init grid (for print_grid) - for row in spec_row] - for spec_row in specs] # Loop through 'specs' for row, spec_row in enumerate(specs): From eced9a36853932674e0aa75a42292676158333de Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:38:23 -0500 Subject: [PATCH 020/103] improve algorithm (part 2): - init grid cell indices (i,j) - start subplot counters at 1 (the plotly way) --- plotly/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 3c55ce32d19..fbc63bf5737 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -602,6 +602,8 @@ def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): scene = graph_objs.Scene(domain={'x': x_domain, 'y': y_domain}) fig['layout'][scene_name] = scene + i = j = 0 # subplot grid indices + x_cnt = y_cnt = s_cnt = 1 # subplot axis/scene counters # Function generating the grid's string repr def _fill_grid(grid, cell, spec, cnt, shared): if grid[cell[0]][cell[1]] != '': # Needed to make rowspan > 1 work @@ -631,8 +633,6 @@ def _fill_grid(grid, cell, spec, cnt, shared): grid[cell[0]+j][cell[1]] = ' ^ ' fig = dict(layout=graph_objs.Layout()) # init layout object - x_cnt = y_cnt = s_cnt = 0 # subplot counters - y = 0 # init y tracer # Loop through 'specs' for row, spec_row in enumerate(specs): From 55a9b3f256c72d87d563498fd275f20b08852229 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:39:50 -0500 Subject: [PATCH 021/103] improve algorithm (part 3): - get x/y domains using grid and grid indices (instead of tracer pts) --- plotly/tools.py | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index fbc63bf5737..16dcc45c854 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -637,25 +637,19 @@ def _fill_grid(grid, cell, spec, cnt, shared): # Loop through 'specs' for row, spec_row in enumerate(specs): - x = 0 # init x tracer before row + j = 0 # start at leftmost grid cell for each spec_row for col, spec in enumerate(spec_row): - # Get x domain (and correct x_e for colspan > 1) - x_s = x + spec['l'] - x_e = x_s + spec['colspan'] * width - x_e -= (spec['l'] + spec['r'] + x_space) - if spec['colspan'] > 1: - x_e += (spec['colspan'] - 1) * (horizontal_spacing - x_space) - - # Get y domain (and correct y_e for rowspan > 1) - y_s = y + spec['b'] - y_e = y_s + spec['rowspan'] * height - y_e -= (spec['b'] + spec['t'] + y_space) - if spec['rowspan'] > 1: - y_e += (spec['rowspan'] - 1) * (vertical_spacing - y_space) - - # Add domains to fig! + # Get x domain using grid and colspan + x_s = grid[i][j][0] + spec['l'] + x_e = grid[i][j+(spec['colspan']-1)][0] + width - spec['r'] + x_domain = [x_s, x_e] + + # Get y domain using grid and rowspan + y_s = grid[i][j][1] + spec['b'] + y_e = grid[i+(spec['rowspan']-1)][j][1] + height - spec['t'] + y_domain = [y_s, y_e] if not spec['isEmpty']: if spec['is3D']: _add_domain_is3D(fig, s_cnt, [x_s, x_e], [y_s, y_e]) @@ -676,9 +670,9 @@ def _fill_grid(grid, cell, spec, cnt, shared): else: _fill_grid(grid, (row, col), spec, False, (False, False)) - x = x_e + horizontal_spacing # move tracer to next col + j += spec['colspan'] # move right by a colspan - y = y_e + vertical_spacing # move tracer to next row + i += 1 # move up by one row if print_grid: print("This is the format of your plot grid!") From 3eba0d7f3655adaf89901dd483928b645a7bfc19 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:45:06 -0500 Subject: [PATCH 022/103] improve algorithm (part 4): - remove _fill_grid() - fill grid_str within specs loop --- plotly/tools.py | 56 ++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 16dcc45c854..52054785a95 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -604,33 +604,6 @@ def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): i = j = 0 # subplot grid indices x_cnt = y_cnt = s_cnt = 1 # subplot axis/scene counters - # Function generating the grid's string repr - def _fill_grid(grid, cell, spec, cnt, shared): - if grid[cell[0]][cell[1]] != '': # Needed to make rowspan > 1 work - return - if spec['isEmpty']: - grid[cell[0]][cell[1]] = '{empty}' - return - if spec['is3D']: - grid[cell[0]][cell[1]] = ( - "[scene{0}".format(cnt) + - " -- " * (spec['colspan'] - 1) + - "]" - ) - else: - labels = ( - "x{0}".format(cnt[0]+1) if not shared[0] else shared[0], - "y{0}".format(cnt[1]+1) if not shared[1] else shared[1] - ) - grid[cell[0]][cell[1]] = ( - "[{0},{1}".format(*labels) + - " -- " * (spec['colspan'] - 1) + - "]" - ) - if spec['rowspan'] > 1: - for j in range(1, spec['rowspan']): - grid[cell[0]+j][cell[1]] = ' | ' - grid[cell[0]+j][cell[1]] = ' ^ ' fig = dict(layout=graph_objs.Layout()) # init layout object @@ -653,22 +626,39 @@ def _fill_grid(grid, cell, spec, cnt, shared): if not spec['isEmpty']: if spec['is3D']: _add_domain_is3D(fig, s_cnt, [x_s, x_e], [y_s, y_e]) - _fill_grid(grid, (row, col), spec, s_cnt, False) + if print_grid: + grid_str[i][j] = '[scene{}'.format(s_cnt) s_cnt += 1 else: x_shared = _get_shared((row, col), 'x', shared_xaxes) y_shared = _get_shared((row, col), 'y', shared_yaxes) - _fill_grid(grid, (row, col), spec, - (x_cnt, y_cnt), (x_shared, y_shared)) if not x_shared: _add_domain(fig, 'x', x_cnt, y_shared, [x_s, x_e]) x_cnt += 1 if not y_shared: _add_domain(fig, 'y', y_cnt, x_shared, [y_s, y_e]) y_cnt += 1 + if print_grid: + grid_str[i][j] = '[{},{}'.format(x_label, y_label) + + # String representation for spanned cells + # TODO more general spacing over spanned cells + if print_grid: + if spec['colspan'] > 1: + for c in range(1, spec['colspan']-1): + grid_str[i][j+c] = ' ' + grid_str[i][j+spec['colspan']-1] = ' ]' + else: + grid_str[i][j] += ']' + if spec['rowspan'] > 1: + for r in range(1, spec['rowspan']-1): + grid_str[i+r][j] = ' ' + grid_str[i+spec['rowspan']-1][j] = ' ^ ' else: - _fill_grid(grid, (row, col), spec, False, (False, False)) + # String representation for empty cells + if print_grid and grid_str[i][j] == '': + grid_str[i][j] = '{empty}' j += spec['colspan'] # move right by a colspan @@ -677,8 +667,8 @@ def _fill_grid(grid, cell, spec, cnt, shared): if print_grid: print("This is the format of your plot grid!") grid_string = "" - for grid_row in grid: - grid_string = " ".join(grid_row) + '\n' + grid_string + for grid_str_row in grid_str: + grid_string = " ".join(grid_str_row) + '\n' + grid_string print(grid_string) return graph_objs.Figure(fig) # forces us to validate what we just did... From 79c98681a808a976aaf8fd175f245e349fde2319 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:47:51 -0500 Subject: [PATCH 023/103] improve algorthm (part 5): - get axis labels (e.g. 'x1', 'y5') using _get_label - get axis anchors using _get_anchors - add axis to layout based on anchors - update add_domain & add_domain_is3D args --- plotly/tools.py | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 52054785a95..f36351b0244 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -623,22 +623,39 @@ def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): y_s = grid[i][j][1] + spec['b'] y_e = grid[i+(spec['rowspan']-1)][j][1] + height - spec['t'] y_domain = [y_s, y_e] + if not spec['isEmpty']: if spec['is3D']: - _add_domain_is3D(fig, s_cnt, [x_s, x_e], [y_s, y_e]) + # Add scene to layout + _add_domain_is3D(fig, s_cnt, x_domain, y_domain) if print_grid: grid_str[i][j] = '[scene{}'.format(s_cnt) s_cnt += 1 else: - x_shared = _get_shared((row, col), 'x', shared_xaxes) - y_shared = _get_shared((row, col), 'y', shared_yaxes) - if not x_shared: - _add_domain(fig, 'x', x_cnt, y_shared, [x_s, x_e]) + + # Get axis label and anchor + x_label = _get_label('x', row, col, x_cnt, shared_xaxes) + y_label = _get_label('y', row, col, y_cnt, shared_yaxes) + x_anchor, y_anchor = _get_anchors(row, col, + x_cnt, y_cnt, + shared_xaxes, + shared_yaxes) + + # Add a xaxis to layout (N.B anchor == False -> no axis) + if x_anchor: + x_position = y_domain[0] if x_anchor == 'free' else 0 + _add_domain(fig, 'x', x_label, x_domain, + x_anchor, x_position) x_cnt += 1 - if not y_shared: - _add_domain(fig, 'y', y_cnt, x_shared, [y_s, y_e]) + + # Add a yaxis to layout (N.B anchor == False -> no axis) + if y_anchor: + y_position = x_domain[0] if y_anchor == 'free' else 0 + _add_domain(fig, 'y', y_label, y_domain, + y_anchor, y_position) y_cnt += 1 + if print_grid: grid_str[i][j] = '[{},{}'.format(x_label, y_label) @@ -655,6 +672,7 @@ def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): for r in range(1, spec['rowspan']-1): grid_str[i+r][j] = ' ' grid_str[i+spec['rowspan']-1][j] = ' ^ ' + else: # String representation for empty cells if print_grid and grid_str[i][j] == '': From ca08227e4bdbcb1ea2770e93e3d470d9ebc5f0ec Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:48:46 -0500 Subject: [PATCH 024/103] remove _get_shared() --- plotly/tools.py | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index f36351b0244..2fe1ce68a8c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -545,43 +545,11 @@ def get_subplots(rows=1, columns=1, if print_grid: grid_str = [['' for column in range(columns)] for row in range(rows)] - # Handler for shared axes logic (return shared axis label or False) - def _get_shared(cell, x_or_y, shared_axes): if isinstance(shared_axes, bool): - if not shared_axes: - return False - if x_or_y == 'x': - if cell[0] == 0: - return False - else: - return "x{0}".format(cell[1]+1) - elif x_or_y == 'y': - if cell[1] == 0: - return False - else: - return "y{0}".format(cell[0]+1) - elif isinstance(shared_axes, list): if isinstance(shared_axes[0], tuple): - if cell in shared_axes and cell != shared_axes[0]: - return { - 'x': "x{0}".format(shared_axes[0][1]+1), - 'y': "y{0}".format(shared_axes[0][0]+1) }[x_or_y] else: - return False - elif isinstance(shared_axes[0], list): - for shared_axis in shared_axes: - if cell in shared_axis and cell != shared_axis[0]: - return { - 'x': "x{0}".format(shared_axis[0][1]+1), - 'y': "y{0}".format(shared_axis[0][0]+1) - }[x_or_y] else: - return False - else: - return False - else: - return False # Function pasting x/y domains in fig object (2d anchored case) def _add_domain(fig, x_or_y, cnt, shared, domain): From 4efbb4f57c5af18518378d20d8ada06e5dd7c01b Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:49:11 -0500 Subject: [PATCH 025/103] add _get_label() --- plotly/tools.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 2fe1ce68a8c..1bfd04195a3 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -545,9 +545,30 @@ def get_subplots(rows=1, columns=1, if print_grid: grid_str = [['' for column in range(columns)] for row in range(rows)] + # Function handling logic around 2d axis labels + # Returns 'x{}' | 'y{}' + def _get_label(x_or_y, row, col, cnt, shared_axes): + # Default label (given strictly by cnt) + label = "{x_or_y}{cnt}".format(x_or_y=x_or_y, cnt=cnt) + if isinstance(shared_axes, bool): + if shared_axes: + if x_or_y == 'x': + label = "{x_or_y}{c}".format(x_or_y=x_or_y, c=col+1) + if x_or_y == 'y': + label = "{x_or_y}{r}".format(x_or_y=x_or_y, r=row+1) + + if isinstance(shared_axes, list): if isinstance(shared_axes[0], tuple): + shared_axes = [shared_axes] # TODO put this elsewhere + for shared_axis in shared_axes: + if (row, col) in shared_axis: + label = { + 'x': "x{0}".format(shared_axis[0][1]+1), + 'y': "y{0}".format(shared_axis[0][0]+1) }[x_or_y] + + return label else: else: From 69214c669c07b1112b94ff45d5cce1543d9e0152 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:50:07 -0500 Subject: [PATCH 026/103] add _get_anchors --- plotly/tools.py | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index 1bfd04195a3..42c627c9bb7 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -569,9 +569,53 @@ def _get_label(x_or_y, row, col, cnt, shared_axes): }[x_or_y] return label + + # Function handling logic around 2d axis anchors + # Return 'x{}' | 'y{}' | 'free' | False + def _get_anchors(row, col, x_cnt, y_cnt, shared_xaxes, shared_yaxes): + # Default anchors (give strictly by cnt) + x_anchor = "y{y_cnt}".format(y_cnt=y_cnt) + y_anchor = "x{x_cnt}".format(x_cnt=x_cnt) + + if isinstance(shared_xaxes, bool): + if shared_xaxes: + if row == 0: + x_anchor = "y{col_cnt}".format(col_cnt=col+1) else: + x_anchor = False + y_anchor = 'free' + if shared_yaxes and col > 0: # TODO covers all cases? + y_anchor = False + return x_anchor, y_anchor + + elif isinstance(shared_xaxes, list): + if isinstance(shared_xaxes[0], tuple): + shared_xaxes = [shared_xaxes] # TODO put this elsewhere + for shared_xaxis in shared_xaxes: + if (row, col) in shared_xaxis[1:]: + x_anchor = False + y_anchor = 'free' # TODO covers all cases? + + if isinstance(shared_yaxes, bool): + if shared_yaxes: + if col == 0: + y_anchor = "x{row_cnt}".format(row_cnt=row+1) else: - + y_anchor = False + x_anchor = 'free' + if shared_xaxes and row > 0: # TODO covers all cases? + x_anchor = False + return x_anchor, y_anchor + + elif isinstance(shared_yaxes, list): + if isinstance(shared_yaxes[0], tuple): + shared_yaxes = [shared_yaxes] # TODO put this elsewhere + for shared_yaxis in shared_yaxes: + if (row, col) in shared_yaxis[1:]: + y_anchor = False + x_anchor = 'free' # TODO covers all cases? + + return x_anchor, y_anchor # Function pasting x/y domains in fig object (2d anchored case) def _add_domain(fig, x_or_y, cnt, shared, domain): num = cnt + 1 From 0497009bfe039343bd614c4eca4e8e9e0f1b1b1e Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:50:41 -0500 Subject: [PATCH 027/103] update _add_domain and _add_domain_is3D --- plotly/tools.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 42c627c9bb7..ade988dbd89 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -616,22 +616,21 @@ def _get_anchors(row, col, x_cnt, y_cnt, shared_xaxes, shared_yaxes): x_anchor = 'free' # TODO covers all cases? return x_anchor, y_anchor - # Function pasting x/y domains in fig object (2d anchored case) - def _add_domain(fig, x_or_y, cnt, shared, domain): - num = cnt + 1 - y_or_x = {'x': 'y', 'y': 'x'}[x_or_y] - axis_name = '{x_or_y}axis{num}'.format(x_or_y=x_or_y, num=num) + + # Function pasting x/y domains in fig object (2d case) + def _add_domain(fig, x_or_y, label, domain, anchor, position): + name = label[0] + 'axis' + label[1:] graph_obj = '{X_or_Y}Axis'.format(X_or_Y=x_or_y.upper()) axis = getattr(graph_objs, graph_obj)(domain=domain) - if not shared: # non-shared axes get an anchor - anchor = '{y_or_x}{num}'.format(y_or_x=y_or_x, num=num) + if anchor: axis['anchor'] = anchor - fig['layout'][axis_name] = axis + if position: # N.B. No need to add position == 0 to axis + axis['position'] = position + fig['layout'][name] = axis # Function pasting x/y domains in fig object (3d case) def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): - num = s_cnt + 1 - scene_name = "scene{num}".format(num=num) + scene_name = "scene{s_cnt}".format(s_cnt=s_cnt) scene = graph_objs.Scene(domain={'x': x_domain, 'y': y_domain}) fig['layout'][scene_name] = scene From a83ad48fd9b8944209623004c13de1b0a7afd25e Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:53:49 -0500 Subject: [PATCH 028/103] code aesthetics --- plotly/tools.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index ade988dbd89..4115a14cd80 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -524,7 +524,7 @@ def get_subplots(rows=1, columns=1, b=0.0 ) - # Fill in 'specs' with defaults + # Fill in specs with defaults for spec_row in specs: for spec in spec_row: for k in SPEC_defaults.keys(): @@ -545,6 +545,8 @@ def get_subplots(rows=1, columns=1, if print_grid: grid_str = [['' for column in range(columns)] for row in range(rows)] + fig = dict(layout=graph_objs.Layout()) # init layout object + # Function handling logic around 2d axis labels # Returns 'x{}' | 'y{}' def _get_label(x_or_y, row, col, cnt, shared_axes): @@ -637,9 +639,7 @@ def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): i = j = 0 # subplot grid indices x_cnt = y_cnt = s_cnt = 1 # subplot axis/scene counters - fig = dict(layout=graph_objs.Layout()) # init layout object - - # Loop through 'specs' + # Loop through specs for row, spec_row in enumerate(specs): j = 0 # start at leftmost grid cell for each spec_row @@ -663,7 +663,6 @@ def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): if print_grid: grid_str[i][j] = '[scene{}'.format(s_cnt) s_cnt += 1 - else: # Get axis label and anchor From 2d781f63fb453746a7dc9ca30304b369d97288f7 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:55:14 -0500 Subject: [PATCH 029/103] modify expected values in tests due to rounding errors --- .../test_core/test_tools/test_get_subplots.py | 130 +++++++++--------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 82ea0ebd277..1cfe30e6258 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -74,7 +74,7 @@ def test_a_lot(): data=Data(), layout=Layout( xaxis1=XAxis( - domain=[0.0, 0.05714285714285712], + domain=[0.0, 0.05714285714285713], anchor='y1' ), xaxis10=XAxis( @@ -86,23 +86,23 @@ def test_a_lot(): anchor='y11' ), xaxis12=XAxis( - domain=[0.6285714285714286, 0.6857142857142856], + domain=[0.6285714285714286, 0.6857142857142857], anchor='y12' ), xaxis13=XAxis( - domain=[0.7857142857142856, 0.8428571428571426], + domain=[0.7857142857142857, 0.8428571428571429], anchor='y13' ), xaxis14=XAxis( - domain=[0.9428571428571426, 0.9999999999999997], + domain=[0.9428571428571428, 1.0], anchor='y14' ), xaxis15=XAxis( - domain=[0.0, 0.05714285714285712], + domain=[0.0, 0.05714285714285713], anchor='y15' ), xaxis16=XAxis( - domain=[0.15714285714285714, 0.21428571428571425], + domain=[0.15714285714285714, 0.21428571428571427], anchor='y16' ), xaxis17=XAxis( @@ -114,27 +114,27 @@ def test_a_lot(): anchor='y18' ), xaxis19=XAxis( - domain=[0.6285714285714286, 0.6857142857142856], + domain=[0.6285714285714286, 0.6857142857142857], anchor='y19' ), xaxis2=XAxis( - domain=[0.15714285714285714, 0.21428571428571425], + domain=[0.15714285714285714, 0.21428571428571427], anchor='y2' ), xaxis20=XAxis( - domain=[0.7857142857142856, 0.8428571428571426], + domain=[0.7857142857142857, 0.8428571428571429], anchor='y20' ), xaxis21=XAxis( - domain=[0.9428571428571426, 0.9999999999999997], + domain=[0.9428571428571428, 1.0], anchor='y21' ), xaxis22=XAxis( - domain=[0.0, 0.05714285714285712], + domain=[0.0, 0.05714285714285713], anchor='y22' ), xaxis23=XAxis( - domain=[0.15714285714285714, 0.21428571428571425], + domain=[0.15714285714285714, 0.21428571428571427], anchor='y23' ), xaxis24=XAxis( @@ -146,15 +146,15 @@ def test_a_lot(): anchor='y25' ), xaxis26=XAxis( - domain=[0.6285714285714286, 0.6857142857142856], + domain=[0.6285714285714286, 0.6857142857142857], anchor='y26' ), xaxis27=XAxis( - domain=[0.7857142857142856, 0.8428571428571426], + domain=[0.7857142857142857, 0.8428571428571429], anchor='y27' ), xaxis28=XAxis( - domain=[0.9428571428571426, 0.9999999999999997], + domain=[0.9428571428571428, 1.0], anchor='y28' ), xaxis3=XAxis( @@ -166,23 +166,23 @@ def test_a_lot(): anchor='y4' ), xaxis5=XAxis( - domain=[0.6285714285714286, 0.6857142857142856], + domain=[0.6285714285714286, 0.6857142857142857], anchor='y5' ), xaxis6=XAxis( - domain=[0.7857142857142856, 0.8428571428571426], + domain=[0.7857142857142857, 0.8428571428571429], anchor='y6' ), xaxis7=XAxis( - domain=[0.9428571428571426, 0.9999999999999997], + domain=[0.9428571428571428, 1.0], anchor='y7' ), xaxis8=XAxis( - domain=[0.0, 0.05714285714285712], + domain=[0.0, 0.05714285714285713], anchor='y8' ), xaxis9=XAxis( - domain=[0.15714285714285714, 0.21428571428571425], + domain=[0.15714285714285714, 0.21428571428571427], anchor='y9' ), yaxis1=YAxis( @@ -242,31 +242,31 @@ def test_a_lot(): anchor='x21' ), yaxis22=YAxis( - domain=[0.8624999999999999, 0.9999999999999998], + domain=[0.8624999999999999, 1.0], anchor='x22' ), yaxis23=YAxis( - domain=[0.8624999999999999, 0.9999999999999998], + domain=[0.8624999999999999, 1.0], anchor='x23' ), yaxis24=YAxis( - domain=[0.8624999999999999, 0.9999999999999998], + domain=[0.8624999999999999, 1.0], anchor='x24' ), yaxis25=YAxis( - domain=[0.8624999999999999, 0.9999999999999998], + domain=[0.8624999999999999, 1.0], anchor='x25' ), yaxis26=YAxis( - domain=[0.8624999999999999, 0.9999999999999998], + domain=[0.8624999999999999, 1.0], anchor='x26' ), yaxis27=YAxis( - domain=[0.8624999999999999, 0.9999999999999998], + domain=[0.8624999999999999, 1.0], anchor='x27' ), yaxis28=YAxis( - domain=[0.8624999999999999, 0.9999999999999998], + domain=[0.8624999999999999, 1.0], anchor='x28' ), yaxis3=YAxis( @@ -303,6 +303,7 @@ def test_a_lot(): fig = tls.get_subplots(4, 7, horizontal_spacing=0.1, vertical_spacing=0.15) + assert fig == expected def test_spacing(): @@ -314,11 +315,11 @@ def test_spacing(): anchor='y1' ), xaxis2=XAxis( - domain=[0.35, 0.65], + domain=[0.35, 0.6499999999999999], anchor='y2' ), xaxis3=XAxis( - domain=[0.7000000000000001, 1.0], + domain=[0.7, 1.0], anchor='y3' ), xaxis4=XAxis( @@ -326,11 +327,11 @@ def test_spacing(): anchor='y4' ), xaxis5=XAxis( - domain=[0.35, 0.65], + domain=[0.35, 0.6499999999999999], anchor='y5' ), xaxis6=XAxis( - domain=[0.7000000000000001, 1.0], + domain=[0.7, 1.0], anchor='y6' ), yaxis1=YAxis( @@ -359,6 +360,7 @@ def test_spacing(): ) ) ) + fig = tls.get_subplots(2, 3, horizontal_spacing=.05, vertical_spacing=.1) @@ -378,19 +380,19 @@ def test_default_spacing(): data=Data(), layout=Layout( xaxis1=XAxis( - domain=[0.0, 0.168], + domain=[0.0, 0.16799999999999998], anchor='y1' ), xaxis10=XAxis( - domain=[0.8320000000000001, 1.0], + domain=[0.832, 1.0], anchor='y10' ), xaxis11=XAxis( - domain=[0.0, 0.168], + domain=[0.0, 0.16799999999999998], anchor='y11' ), xaxis12=XAxis( - domain=[0.20800000000000002, 0.376], + domain=[0.208, 0.376], anchor='y12' ), xaxis13=XAxis( @@ -402,15 +404,15 @@ def test_default_spacing(): anchor='y14' ), xaxis15=XAxis( - domain=[0.8320000000000001, 1.0], + domain=[0.832, 1.0], anchor='y15' ), xaxis16=XAxis( - domain=[0.0, 0.168], + domain=[0.0, 0.16799999999999998], anchor='y16' ), xaxis17=XAxis( - domain=[0.20800000000000002, 0.376], + domain=[0.208, 0.376], anchor='y17' ), xaxis18=XAxis( @@ -422,19 +424,19 @@ def test_default_spacing(): anchor='y19' ), xaxis2=XAxis( - domain=[0.20800000000000002, 0.376], + domain=[0.208, 0.376], anchor='y2' ), xaxis20=XAxis( - domain=[0.8320000000000001, 1.0], + domain=[0.832, 1.0], anchor='y20' ), xaxis21=XAxis( - domain=[0.0, 0.168], + domain=[0.0, 0.16799999999999998], anchor='y21' ), xaxis22=XAxis( - domain=[0.20800000000000002, 0.376], + domain=[0.208, 0.376], anchor='y22' ), xaxis23=XAxis( @@ -446,15 +448,15 @@ def test_default_spacing(): anchor='y24' ), xaxis25=XAxis( - domain=[0.8320000000000001, 1.0], + domain=[0.832, 1.0], anchor='y25' ), xaxis26=XAxis( - domain=[0.0, 0.168], + domain=[0.0, 0.16799999999999998], anchor='y26' ), xaxis27=XAxis( - domain=[0.20800000000000002, 0.376], + domain=[0.208, 0.376], anchor='y27' ), xaxis28=XAxis( @@ -470,7 +472,7 @@ def test_default_spacing(): anchor='y3' ), xaxis30=XAxis( - domain=[0.8320000000000001, 1.0], + domain=[0.832, 1.0], anchor='y30' ), xaxis4=XAxis( @@ -478,15 +480,15 @@ def test_default_spacing(): anchor='y4' ), xaxis5=XAxis( - domain=[0.8320000000000001, 1.0], + domain=[0.832, 1.0], anchor='y5' ), xaxis6=XAxis( - domain=[0.0, 0.168], + domain=[0.0, 0.16799999999999998], anchor='y6' ), xaxis7=XAxis( - domain=[0.20800000000000002, 0.376], + domain=[0.208, 0.376], anchor='y7' ), xaxis8=XAxis( @@ -506,23 +508,23 @@ def test_default_spacing(): anchor='x10' ), yaxis11=YAxis( - domain=[0.35, 0.4749999999999999], + domain=[0.35, 0.475], anchor='x11' ), yaxis12=YAxis( - domain=[0.35, 0.4749999999999999], + domain=[0.35, 0.475], anchor='x12' ), yaxis13=YAxis( - domain=[0.35, 0.4749999999999999], + domain=[0.35, 0.475], anchor='x13' ), yaxis14=YAxis( - domain=[0.35, 0.4749999999999999], + domain=[0.35, 0.475], anchor='x14' ), yaxis15=YAxis( - domain=[0.35, 0.4749999999999999], + domain=[0.35, 0.475], anchor='x15' ), yaxis16=YAxis( @@ -629,15 +631,15 @@ def test_subplot_specs(): data=Data(), layout=Layout( xaxis1=XAxis( - domain=[0.0, 0.28888888888888886], + domain=[0.0, 0.2888888888888889], anchor='y1' ), xaxis2=XAxis( - domain=[0.0, 0.28888888888888886], + domain=[0.0, 0.2888888888888889], anchor='y2' ), xaxis3=XAxis( - domain=[0.3555555555555555, 0.6444444444444445], + domain=[0.35555555555555557, 0.6444444444444445], anchor='y3' ), xaxis4=XAxis( @@ -725,11 +727,11 @@ def test_subplot_specs_rowspan(): data=Data(), layout=Layout( xaxis1=XAxis( - domain=[0.0, 0.28888888888888886], + domain=[0.0, 0.2888888888888889], anchor='y1' ), xaxis2=XAxis( - domain=[0.3555555555555555, 0.6444444444444445], + domain=[0.35555555555555557, 0.6444444444444445], anchor='y2' ), xaxis3=XAxis( @@ -737,7 +739,7 @@ def test_subplot_specs_rowspan(): anchor='y3' ), xaxis4=XAxis( - domain=[0.3555555555555555, 0.6444444444444445], + domain=[0.35555555555555557, 0.6444444444444445], anchor='y4' ), xaxis5=XAxis( @@ -745,7 +747,7 @@ def test_subplot_specs_rowspan(): anchor='y5' ), xaxis6=XAxis( - domain=[0.3555555555555555, 1.0], + domain=[0.35555555555555557, 1.0], anchor='y6' ), yaxis1=YAxis( @@ -841,15 +843,15 @@ def test_subplot_specs_padding(): anchor='x1' ), yaxis2=YAxis( - domain=[0.2, 0.49999999999999994], + domain=[0.2, 0.5], anchor='x2' ), yaxis3=YAxis( - domain=[0.49999999999999994, 0.8], + domain=[0.5, 0.8], anchor='x3' ), yaxis4=YAxis( - domain=[0.49999999999999994, 1.0], + domain=[0.5, 1.0], anchor='x4' ) ) From b8e9f539170569b3c25104737b6250e041cc2735 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:56:04 -0500 Subject: [PATCH 030/103] add 2nd rowspan test (that discovered the bug) --- .../test_core/test_tools/test_get_subplots.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 1cfe30e6258..39b9cdf6546 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -784,6 +784,60 @@ def test_subplot_specs_rowspan(): assert fig == expected +def test_subplot_specs_rowspan2(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.0, 0.6444444444444445], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.0, 1.0], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.6333333333333333], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x5' + ) + ) + ) + + fig = tls.get_subplots(rows=3, columns=3, + specs=[[{}, {}, {'rowspan': 2}], + [{'colspan': 2}], + [{'colspan': 3}]]) + + assert fig == expected + def test_subplot_specs_is3D(): expected = Figure( data=Data(), From 46ed9d7c1dddd032e6fe6ef3c2deb51fa090fb23 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:56:38 -0500 Subject: [PATCH 031/103] more test updating due to rounding errors --- .../test_core/test_tools/test_get_subplots.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 39b9cdf6546..28d2152fd6f 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -923,11 +923,11 @@ def test_subplot_shared_xaxes(): data=Data(), layout=Layout( xaxis1=XAxis( - domain=[0.0, 0.28888888888888886], + domain=[0.0, 0.2888888888888889], anchor='y1' ), xaxis2=XAxis( - domain=[0.3555555555555555, 0.6444444444444445], + domain=[0.35555555555555557, 0.6444444444444445], anchor='y2' ), xaxis3=XAxis( @@ -947,13 +947,18 @@ def test_subplot_shared_xaxes(): anchor='x3' ), yaxis4=YAxis( - domain=[0.575, 1.0] + domain=[0.575, 1.0], + anchor='free' ), yaxis5=YAxis( - domain=[0.575, 1.0] + domain=[0.575, 1.0], + anchor='free', + position=0.35555555555555557 ), yaxis6=YAxis( - domain=[0.575, 1.0] + domain=[0.575, 1.0], + anchor='free', + position=0.7111111111111111 ) ) ) From 540b7349de63ac2f1ceef401cbb9abe4ed431593 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:57:46 -0500 Subject: [PATCH 032/103] update shared axis tests --- .../test_core/test_tools/test_get_subplots.py | 65 ++++++++++++------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 28d2152fd6f..c45a8b78eff 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -976,54 +976,63 @@ def test_subplot_shared_yaxes(): anchor='y1' ), xaxis10=XAxis( - domain=[0.55, 1.0] + domain=[0.55, 1.0], + anchor='free', + position=0.848 ), xaxis2=XAxis( - domain=[0.55, 1.0] + domain=[0.55, 1.0], + anchor='free' ), xaxis3=XAxis( domain=[0.0, 0.45], - anchor='y3' + anchor='y2' ), xaxis4=XAxis( - domain=[0.55, 1.0] + domain=[0.55, 1.0], + anchor='free', + position=0.212 ), xaxis5=XAxis( domain=[0.0, 0.45], - anchor='y5' + anchor='y3' ), xaxis6=XAxis( - domain=[0.55, 1.0] + domain=[0.55, 1.0], + anchor='free', + position=0.424 ), xaxis7=XAxis( domain=[0.0, 0.45], - anchor='y7' + anchor='y4' ), xaxis8=XAxis( - domain=[0.55, 1.0] + domain=[0.55, 1.0], + anchor='free', + position=0.636 ), xaxis9=XAxis( domain=[0.0, 0.45], - anchor='y9' + anchor='y5' ), yaxis1=YAxis( - domain=[0.0, 0.15200000000000002], + domain=[0.0, 0.152], anchor='x1' ), yaxis2=YAxis( - domain=[0.21200000000000002, 0.36400000000000005], + domain=[0.212, 0.364], anchor='x2' ), yaxis3=YAxis( - domain=[0.42400000000000004, 0.5760000000000001], + domain=[0.424, 0.576], anchor='x3' ), yaxis4=YAxis( - domain=[0.6360000000000001, 0.788], + domain=[0.636, 0.788], anchor='x4' ), yaxis5=YAxis( - domain=[0.8480000000000001, 1.0], + domain=[0.848, 1.0], anchor='x5' ) ) @@ -1042,7 +1051,8 @@ def test_subplot_shared_axes_list(): anchor='y1' ), xaxis2=XAxis( - domain=[0.55, 1.0] + domain=[0.55, 1.0], + anchor='free' ), xaxis3=XAxis( domain=[0.55, 1.0], @@ -1053,7 +1063,8 @@ def test_subplot_shared_axes_list(): anchor='x1' ), yaxis2=YAxis( - domain=[0.575, 1.0] + domain=[0.575, 1.0], + anchor='free' ), yaxis3=YAxis( domain=[0.575, 1.0], @@ -1069,16 +1080,16 @@ def test_subplot_shared_axes_list(): assert fig == expected -def test_subplot_shared_axes_list_of_list(): +def test_subplot_shared_axes_list_of_lists(): expected = Figure( data=Data(), layout=Layout( xaxis1=XAxis( - domain=[0.0, 0.28888888888888886], + domain=[0.0, 0.2888888888888889], anchor='y1' ), xaxis2=XAxis( - domain=[0.3555555555555555, 0.6444444444444445], + domain=[0.35555555555555557, 0.6444444444444445], anchor='y2' ), xaxis3=XAxis( @@ -1086,8 +1097,8 @@ def test_subplot_shared_axes_list_of_list(): anchor='y3' ), xaxis4=XAxis( - domain=[0.3555555555555555, 0.6444444444444445], - anchor='y4' + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y5' ), yaxis1=YAxis( domain=[0.0, 0.425], @@ -1102,19 +1113,23 @@ def test_subplot_shared_axes_list_of_list(): anchor='x3' ), yaxis4=YAxis( - domain=[0.575, 1.0] + domain=[0.575, 1.0], + anchor='free' ), yaxis5=YAxis( domain=[0.575, 1.0], - anchor='x5' + anchor='x4' ), yaxis6=YAxis( - domain=[0.575, 1.0] + domain=[0.575, 1.0], + anchor='free', + position=0.7111111111111111 ) ) ) fig = tls.get_subplots(rows=2, columns=3, - shared_xaxes=[[(0,0), (1,0)], [(0,2), (1,2)]]) + shared_xaxes=[[(0,0), (1,0)], + [(0,2), (1,2)]]) assert fig == expected From 7b0e8fb6d31e60348fe0166d01faf20c2afe9994 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 31 Dec 2014 15:58:10 -0500 Subject: [PATCH 033/103] add 1 shared_xaxes=True, shared_yaxes=True test --- .../test_core/test_tools/test_get_subplots.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index c45a8b78eff..8f39194c30f 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -1042,6 +1042,41 @@ def test_subplot_shared_yaxes(): assert fig == expected +def test_subplot_shared_xaxes_yaxes(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='free' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='free' + ), + yaxis1=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='free' + ), + yaxis3=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='free' + ) + ) + ) + fig = tls.get_subplots(rows=3, columns=3, + shared_xaxes=True, shared_yaxes=True) + + assert fig == expected + def test_subplot_shared_axes_list(): expected = Figure( data=Data(), From f6a64d18c8acb626d27575f6ab1eb1f147e05b49 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 2 Jan 2015 16:22:35 -0500 Subject: [PATCH 034/103] sub isEmpty -> is_empyt , is3D -> is_3d --- .../test_core/test_tools/test_get_subplots.py | 9 +++++---- plotly/tools.py | 16 ++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 8f39194c30f..8b40b96b11d 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -779,8 +779,8 @@ def test_subplot_specs_rowspan(): fig = tls.get_subplots(rows=3, columns=3, specs=[[{'rowspan': 3}, {}, {}], - [{'isEmpty': True}, {}, {}], - [{'isEmpty': True}, {'colspan': 2}]]) + [{'is_empty': True}, {}, {}], + [{'is_empty': True}, {'colspan': 2}]]) assert fig == expected @@ -838,7 +838,7 @@ def test_subplot_specs_rowspan2(): assert fig == expected -def test_subplot_specs_is3D(): +def test_subplot_specs_is_3d(): expected = Figure( data=Data(), layout=Layout( @@ -868,7 +868,8 @@ def test_subplot_specs_is3D(): ) fig = tls.get_subplots(rows=2, columns=2, - specs=[[{'is3D': True}, {}], [{'is3D': True}, {}]]) + specs=[[{'is_3d': True}, {}], + [{'is_3d': True}, {}]]) assert fig == expected diff --git a/plotly/tools.py b/plotly/tools.py index 4115a14cd80..b62c18acd8a 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -471,8 +471,8 @@ def get_subplots(rows=1, columns=1, - Each item in the 'specs' is a dictionary. The available keys are: - * isEmpty (boolean, default=False): flag for empty grid cells - * is3D (boolean, default=False): flag for 3d scenes + * is_empty (boolean, default=False): flag for empty grid cells + * is_3d (boolean, default=False): flag for 3d scenes * colspan (int, default=1): span across grid columns from left to right * rowspan (int, default=1): span across grid rows @@ -514,8 +514,8 @@ def get_subplots(rows=1, columns=1, # Default spec key-values SPEC_defaults = dict( - isEmpty=False, - is3D=False, + is_empty=False, + is_3d=False, colspan=1, rowspan=1, l=0.0, @@ -631,7 +631,7 @@ def _add_domain(fig, x_or_y, label, domain, anchor, position): fig['layout'][name] = axis # Function pasting x/y domains in fig object (3d case) - def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): + def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): scene_name = "scene{s_cnt}".format(s_cnt=s_cnt) scene = graph_objs.Scene(domain={'x': x_domain, 'y': y_domain}) fig['layout'][scene_name] = scene @@ -656,10 +656,10 @@ def _add_domain_is3D(fig, s_cnt, x_domain, y_domain): y_e = grid[i+(spec['rowspan']-1)][j][1] + height - spec['t'] y_domain = [y_s, y_e] - if not spec['isEmpty']: - if spec['is3D']: + if not spec['is_empty']: + if spec['is_3d']: # Add scene to layout - _add_domain_is3D(fig, s_cnt, x_domain, y_domain) + _add_domain_is_3d(fig, s_cnt, x_domain, y_domain) if print_grid: grid_str[i][j] = '[scene{}'.format(s_cnt) s_cnt += 1 From bed60db81517f4a41736722c8829d38b7453c106 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:25:12 -0500 Subject: [PATCH 035/103] add info about 'insets' in docstring --- plotly/tools.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index b62c18acd8a..1e0b0f1a264 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -413,6 +413,12 @@ def get_subplots(rows=1, columns=1, fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x3', yaxis='y3')] Example 4: + # insets + fig = tools.get_subplots(insets=[{'cell':(0,0), 'l': 0.7, 'b': 0.3}]) + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2])] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + + Example 5: # print out string showing the subplot grid you've put in the layout fig = tools.get_subplots(rows=3, columns=2, print_grid=True) @@ -481,6 +487,23 @@ def get_subplots(rows=1, columns=1, * r (float, default=0.0): padding right of cell * t (float, default=0.0): padding right of cell * b (float, default=0.0): padding bottom of cell + + insets (kwarg, list of dictionaries): + Inset specifications. + + - Each item in 'insets' is a dictionary. + The available keys are: + + * cell (tuple, default=(0,0)) subplot cell indices + * is_3d (boolean, default=False): flag for 3d scenes + * l (float, default=0.0): padding left of inset + in fraction of cell width + * w (float or 'to_end', default='to_end') inset width + in fraction of cell width ('to_end': to cell right edge) + * b (float, default=0.0): padding bottom of inset + in fraction of cell height + * h (float or 'to_end', default='to_end') inset height + in fraction of cell height ('to_end': to cell top edge) """ # Throw exception for non-integer rows and columns From 50eeb8fc18fede5895ea92e104e72c8a21215ecd Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:25:59 -0500 Subject: [PATCH 036/103] add try/except block for kwargs['insets'] --- plotly/tools.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 1e0b0f1a264..1a85ab1a0cf 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -535,6 +535,14 @@ def get_subplots(rows=1, columns=1, for col in range(columns)] for row in range(rows)] # default 'specs' + # Sanitize 'insets' + try: + insets = kwargs['insets'] + if not isinstance(insets, list): + raise Exception("Keyword argument 'insets' must be a list") + except KeyError: + insets = False + # Default spec key-values SPEC_defaults = dict( is_empty=False, From 1a2192e634c2f9ed1c5164c3be2282169c61dad7 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:26:40 -0500 Subject: [PATCH 037/103] aesthestic in SPEC_defaults --- plotly/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 1a85ab1a0cf..8039ea07d96 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -551,8 +551,8 @@ def get_subplots(rows=1, columns=1, rowspan=1, l=0.0, r=0.0, - t=0.0, - b=0.0 + b=0.0, + t=0.0 ) # Fill in specs with defaults From 443217e14f0c85bd506a02a4fe71bb7e3212550e Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:27:29 -0500 Subject: [PATCH 038/103] add functions checking and filling keys (for 'specs' & 'insets') --- plotly/tools.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 8039ea07d96..9ae3988447b 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -543,6 +543,23 @@ def get_subplots(rows=1, columns=1, except KeyError: insets = False + # Throw exception if non-valid key / fill in defaults + def _check_keys_and_fill(name, arg, defaults): + def _checks(item, defaults): + for k in item.keys(): + if k not in defaults.keys(): + raise Exception("Invalid key '{k}' in keyword " + "argument '{name}'".format(k=k, name=name)) + for k in defaults.keys(): + if k not in item.keys(): + item[k] = defaults[k] + for arg_i in arg: + if isinstance(arg_i, list): + for arg_ii in arg_i: + _checks(arg_ii, defaults) + elif isinstance(arg_i, dict): + _checks(arg_i, defaults) + # Default spec key-values SPEC_defaults = dict( is_empty=False, From a72f0434942bd35933b1f78802e23e0aa671babb Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:28:40 -0500 Subject: [PATCH 039/103] send specs to _check_keys_and_fill() --- plotly/tools.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 9ae3988447b..bc89ef533c8 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -522,7 +522,7 @@ def get_subplots(rows=1, columns=1, except KeyError: vertical_spacing = 0.3 / rows - # Sanitize 'specs' -- TODO more sanitizing? + # Sanitize 'specs' try: specs = kwargs['specs'] if not isinstance(specs, list): @@ -571,13 +571,7 @@ def _checks(item, defaults): b=0.0, t=0.0 ) - - # Fill in specs with defaults - for spec_row in specs: - for spec in spec_row: - for k in SPEC_defaults.keys(): - if k not in spec.keys(): - spec[k] = SPEC_defaults[k] + _check_keys_and_fill('specs', specs, SPEC_defaults) # Set width & height of each subplot cell (excluding padding) width = (1. - horizontal_spacing * (columns - 1)) / columns From 8e3e83d425aff957e998fc075a5dc6fb3574b64d Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:29:04 -0500 Subject: [PATCH 040/103] check and fill 'insets' --- plotly/tools.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index bc89ef533c8..ca1f0213a1c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -573,6 +573,18 @@ def _checks(item, defaults): ) _check_keys_and_fill('specs', specs, SPEC_defaults) + # Default inset key-values + if insets: + INSET_defaults = dict( + cell=(0,0), + is_3d=False, + l=0.0, + w='to_end', + b=0.0, + h='to_end' + ) + _check_keys_and_fill('insets', insets, INSET_defaults) + # Set width & height of each subplot cell (excluding padding) width = (1. - horizontal_spacing * (columns - 1)) / columns height = (1. - vertical_spacing * (rows - 1)) / rows @@ -586,6 +598,8 @@ def _checks(item, defaults): # Initialize the grid's string representation if print_grid: grid_str = [['' for column in range(columns)] for row in range(rows)] + if insets: + insets_str = ['' for inset in range(len(insets))] fig = dict(layout=graph_objs.Layout()) # init layout object From d30e7b9176c396d5ad166849a6604fd905a1c5e8 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:29:41 -0500 Subject: [PATCH 041/103] raise Exception if non-valid kwarg is sent to get_subplots --- plotly/tools.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index ca1f0213a1c..1b1bc4b8fd9 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -512,6 +512,13 @@ def get_subplots(rows=1, columns=1, if not isinstance(columns, int): raise Exception("Keyword argument 'columns' must be an int") + # Throw exception if non-valid kwarg is sent + VALID_KWARGS = ['horizontal_spacing', 'vertical_spacing', + 'specs', 'insets'] + for key in kwargs.keys(): + if key not in VALID_KWARGS: + raise Exception("Invalid keyword argument: '{0}'".format(key)) + # Set 'horizontal_spacing' / 'vertical_spacing' w.r.t. rows / columns try: horizontal_spacing = float(kwargs['horizontal_spacing']) From f9235b30c60893aafa5930939a5ff41d2b3d3946 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:30:54 -0500 Subject: [PATCH 042/103] loop through insets! --- plotly/tools.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 1b1bc4b8fd9..26de7c6420d 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -776,6 +776,55 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): i += 1 # move up by one row + # Loop through insets + if insets: + for i_inset, inset in enumerate(insets): + + i = inset['cell'][0] + j = inset['cell'][1] + + # Get inset x domain using grid + x_s = grid[i][j][0] + inset['l'] * width + if inset['w'] == 'to_end': + x_e = grid[i][j][0] + width + else: + x_e = x_s + inset['w'] * width + x_domain = [x_s, x_e] + + # Get inset y domain using grid + y_s = grid[i][j][1] + inset['b'] * height + if inset['h'] == 'to_end': + y_e = grid[i][j][1] + height + else: + y_e = y_s + inset['h'] * height + y_domain = [y_s, y_e] + + if inset['is_3d']: + # Add scene to layout + _add_domain_is_3d(fig, s_cnt, x_domain, y_domain) + if print_grid: + insets_str[i_inset] = '[scene{}]'.format(s_cnt) + s_cnt += 1 + else: + + # Get axis label and anchor + x_label = _get_label('x', False, False, x_cnt, False) + y_label = _get_label('y', False, False, y_cnt, False) + x_anchor, y_anchor = _get_anchors(row, col, + x_cnt, y_cnt, + False, False) + + # Add a xaxis to layout (N.B insets always have anchors) + _add_domain(fig, 'x', x_label, x_domain, x_anchor, False) + x_cnt += 1 + + # Add a yayis to layout (N.B insets always have anchors) + _add_domain(fig, 'y', y_label, y_domain, y_anchor, False) + y_cnt += 1 + + if print_grid: + insets_str[i_inset] = '[{},{}]'.format(x_label, y_label) + if print_grid: print("This is the format of your plot grid!") grid_string = "" From dfa47f756e7f825def0b0c519a8e6245f1e94012 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:31:13 -0500 Subject: [PATCH 043/103] add inset str reprensation --- plotly/tools.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 26de7c6420d..1fd105b1bd5 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -831,6 +831,13 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): for grid_str_row in grid_str: grid_string = " ".join(grid_str_row) + '\n' + grid_string print(grid_string) + if insets: + print("With insets:") + for i_inset, inset in enumerate(insets): + print( + insets_str[i_inset] + ' over ' + + grid_str[inset['cell'][0]][inset['cell'][1]]) + print('') return graph_objs.Figure(fig) # forces us to validate what we just did... From 3ef811673282cc61401504980718e063209fe72b Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:32:03 -0500 Subject: [PATCH 044/103] add wrong key & wrong type tests --- .../test_core/test_tools/test_get_subplots.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 8b40b96b11d..1e4662426b6 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -375,6 +375,10 @@ def test_non_integer_rows(): def test_non_integer_columns(): fig = tls.get_subplots(columns=2/3) +@raises(Exception) +def test_wrong_kwarg(): + fig = tls.get_subplots(stuff='no gonna work') + def test_default_spacing(): expected = Figure( data=Data(), @@ -626,6 +630,14 @@ def test_default_spacing(): assert fig == expected +@raises(Exception) +def test_subplot_specs_wrong_type(): + fig = tls.get_subplots(specs="not going to work") + +@raises(Exception) +def test_subplot_specs_wrong_item(): + fig = tls.get_subplots(specs=[{'not': "going to work"}]) + def test_subplot_specs(): expected = Figure( data=Data(), @@ -1169,3 +1181,11 @@ def test_subplot_shared_axes_list_of_lists(): [(0,2), (1,2)]]) assert fig == expected + +@raises(Exception) +def test_subplot_insets_wrong_type(): + fig = tls.get_subplots(insets="not going to work") + +@raises(Exception) +def test_subplot_insets_wrong_item(): + fig = tls.get_subplots(insets=[{'not': "going to work"}]) From ec2525896c25f9639e0ba838aa155e48896de744 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 17:32:25 -0500 Subject: [PATCH 045/103] add 2 insets tests --- .../test_core/test_tools/test_get_subplots.py | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 1e4662426b6..bc5f036dba1 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -1189,3 +1189,100 @@ def test_subplot_insets_wrong_type(): @raises(Exception) def test_subplot_insets_wrong_item(): fig = tls.get_subplots(insets=[{'not': "going to work"}]) + +def test_subplot_insets(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.45], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.55, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.0, 0.45], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.55, 1.0], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.865, 0.955], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.425], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.575, 1.0], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.575, 1.0], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.6599999999999999, 0.8724999999999999], + anchor='x5' + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=2, + insets=[{'cell': (1,1), + 'l': 0.7, 'w': 0.2, + 'b': 0.2, 'h': 0.5}]) + + assert fig == expected + +def test_subplot_insets_multiple(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 1.0], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.0, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.8, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.8, 1.0], + anchor='y4' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.425], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.575, 1.0], + anchor='x4' + ) + ) + ) + + fig = tls.get_subplots(rows=2, + insets=[{'cell': (0,0), 'l':0.8}, + {'cell': (1,0), 'l':0.8}]) From 25c8d85a40e03575ea053de555d407934561cae3 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 18:56:23 -0500 Subject: [PATCH 046/103] update 'specs' docstring --- plotly/tools.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 1fd105b1bd5..0d403295be2 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -463,21 +463,24 @@ def get_subplots(rows=1, columns=1, Subplot specifications. - Indices of the outer list correspond to subplot grid rows - starting from the bottom. + starting from the bottom. The number of rows in 'specs' + must be equal to 'rows'. - Indices of the inner lists correspond to subplot grid columns - starting from the left - - - Note that specs[0][0] has the specs for the bottom-left subplot + starting from the left. The number of columns in 'specs' + must be equal to 'columns'. - Each item in the 'specs' list corresponds to one subplot - in a subplot grid. The subplot grid has 'rows' times 'columns' - cells. + in a subplot grid. The subplot grid has exactly 'rows' + times 'columns' cells. + + - Use None for blank a subplot cell (or to move pass a col/row span). + + - Note that specs[0][0] has the specs for the bottom-left subplot - - Each item in the 'specs' is a dictionary. + - Each item in 'specs' is a dictionary. The available keys are: - * is_empty (boolean, default=False): flag for empty grid cells * is_3d (boolean, default=False): flag for 3d scenes * colspan (int, default=1): span across grid columns from left to right From 330eeacc2f9c10eb0a7985fb0e613b07c32ac363 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 18:57:39 -0500 Subject: [PATCH 047/103] raise Exception if specs dims not equal to 'rows' and 'columns' --- plotly/tools.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 0d403295be2..4057d15c143 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -545,6 +545,15 @@ def get_subplots(rows=1, columns=1, for col in range(columns)] for row in range(rows)] # default 'specs' + # Throw exception if specs is over or under specified + if len(specs) != rows: + raise Exception("The number of rows in 'specs' " + "must be equal to 'rows'") + for spec_row in specs: + if len(spec_row) != columns: + raise Exception("The number of columns in 'specs' " + "must be equal to 'columns'") + # Sanitize 'insets' try: insets = kwargs['insets'] From 12cdffd419afe859890d153c128190c22f9e2d5a Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 18:58:16 -0500 Subject: [PATCH 048/103] rm 'is_empty' from SPEC_defaults --- plotly/tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index 4057d15c143..ef8500ee81d 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -581,7 +581,6 @@ def _checks(item, defaults): # Default spec key-values SPEC_defaults = dict( - is_empty=False, is_3d=False, colspan=1, rowspan=1, From e52ff4f8ebd0df7f5095ee6a077331d000f4aefc Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 18:58:46 -0500 Subject: [PATCH 049/103] add cond for spec == None case in check_and_fill --- plotly/tools.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index ef8500ee81d..34ee30b96c7 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -575,7 +575,8 @@ def _checks(item, defaults): for arg_i in arg: if isinstance(arg_i, list): for arg_ii in arg_i: - _checks(arg_ii, defaults) + if arg_ii is not None: # for specs[i][j] == None + _checks(arg_ii, defaults) elif isinstance(arg_i, dict): _checks(arg_i, defaults) From bad84cd476906566cc9e3bb1fda95e9b94d31756 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 18:59:29 -0500 Subject: [PATCH 050/103] pep8 tuple --- plotly/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index 34ee30b96c7..04d69681e21 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -595,7 +595,7 @@ def _checks(item, defaults): # Default inset key-values if insets: INSET_defaults = dict( - cell=(0,0), + cell=(0, 0), is_3d=False, l=0.0, w='to_end', From 0c83b9d8148670f19947d554a47ba9aa56a010b8 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 18:59:52 -0500 Subject: [PATCH 051/103] move spec is None cond at top of spec loop --- plotly/tools.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index 04d69681e21..022448ef3fb 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -721,6 +721,13 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): for col, spec in enumerate(spec_row): + # String representation for empty cells + if spec is None: + if print_grid and grid_str[i][j] == '': + grid_str[i][j] = '{none}' + j += 1 + continue + # Get x domain using grid and colspan x_s = grid[i][j][0] + spec['l'] x_e = grid[i][j+(spec['colspan']-1)][0] + width - spec['r'] From 45229d270d726e7e00a8dcab4a4bf0ef6af2dfdf Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 19:00:41 -0500 Subject: [PATCH 052/103] re-align specs loop --- plotly/tools.py | 94 +++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 50 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 022448ef3fb..2568c3b161c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -738,60 +738,54 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): y_e = grid[i+(spec['rowspan']-1)][j][1] + height - spec['t'] y_domain = [y_s, y_e] - if not spec['is_empty']: - if spec['is_3d']: - # Add scene to layout - _add_domain_is_3d(fig, s_cnt, x_domain, y_domain) - if print_grid: - grid_str[i][j] = '[scene{}'.format(s_cnt) - s_cnt += 1 - else: - - # Get axis label and anchor - x_label = _get_label('x', row, col, x_cnt, shared_xaxes) - y_label = _get_label('y', row, col, y_cnt, shared_yaxes) - x_anchor, y_anchor = _get_anchors(row, col, - x_cnt, y_cnt, - shared_xaxes, - shared_yaxes) - - # Add a xaxis to layout (N.B anchor == False -> no axis) - if x_anchor: - x_position = y_domain[0] if x_anchor == 'free' else 0 - _add_domain(fig, 'x', x_label, x_domain, - x_anchor, x_position) - x_cnt += 1 - - # Add a yaxis to layout (N.B anchor == False -> no axis) - if y_anchor: - y_position = x_domain[0] if y_anchor == 'free' else 0 - _add_domain(fig, 'y', y_label, y_domain, - y_anchor, y_position) - y_cnt += 1 - - if print_grid: - grid_str[i][j] = '[{},{}'.format(x_label, y_label) - - # String representation for spanned cells - # TODO more general spacing over spanned cells + if spec['is_3d']: + # Add scene to layout + _add_domain_is_3d(fig, s_cnt, x_domain, y_domain) if print_grid: - if spec['colspan'] > 1: - for c in range(1, spec['colspan']-1): - grid_str[i][j+c] = ' ' - grid_str[i][j+spec['colspan']-1] = ' ]' - else: - grid_str[i][j] += ']' - if spec['rowspan'] > 1: - for r in range(1, spec['rowspan']-1): - grid_str[i+r][j] = ' ' - grid_str[i+spec['rowspan']-1][j] = ' ^ ' - + grid_str[i][j] = '[scene{}'.format(s_cnt) + s_cnt += 1 else: - # String representation for empty cells - if print_grid and grid_str[i][j] == '': - grid_str[i][j] = '{empty}' j += spec['colspan'] # move right by a colspan + x_label = _get_label('x', row, col, x_cnt, shared_xaxes) + y_label = _get_label('y', row, col, y_cnt, shared_yaxes) + x_anchor, y_anchor = _get_anchors(row, col, + x_cnt, y_cnt, + shared_xaxes, + shared_yaxes) + + # Add a xaxis to layout (N.B anchor == False -> no axis) + if x_anchor: + x_position = y_domain[0] if x_anchor == 'free' else 0 + _add_domain(fig, 'x', x_label, x_domain, + x_anchor, x_position) + x_cnt += 1 + + # Add a yaxis to layout (N.B anchor == False -> no axis) + if y_anchor: + y_position = x_domain[0] if y_anchor == 'free' else 0 + _add_domain(fig, 'y', y_label, y_domain, + y_anchor, y_position) + y_cnt += 1 + + if print_grid: + grid_str[i][j] = '[{},{}'.format(x_label, y_label) + + # String representation for spanned cells + # TODO more general spacing over spanned cells + if print_grid: + if spec['colspan'] > 1: + for c in range(1, spec['colspan']-1): + grid_str[i][j+c] = ' ' + grid_str[i][j+spec['colspan']-1] = ' ]' + else: + grid_str[i][j] += ']' + if spec['rowspan'] > 1: + for r in range(1, spec['rowspan']-1): + grid_str[i+r][j] = ' ' + for c in range(0, spec['colspan']): + grid_str[i+spec['rowspan']-1][j+c] = ' ^ ' + i += 1 # move up by one row From fa286a0c652ecb595c61f91141c59d7129aadfc1 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 19:01:26 -0500 Subject: [PATCH 053/103] move right by one column: - instead of one colspan - now symmetric with rowspan --- plotly/tools.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index 2568c3b161c..0cde1c1193e 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -746,7 +746,7 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): s_cnt += 1 else: - j += spec['colspan'] # move right by a colspan + # Get axis label and anchor x_label = _get_label('x', row, col, x_cnt, shared_xaxes) y_label = _get_label('y', row, col, y_cnt, shared_yaxes) x_anchor, y_anchor = _get_anchors(row, col, @@ -786,6 +786,7 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): for c in range(0, spec['colspan']): grid_str[i+spec['rowspan']-1][j+c] = ' ^ ' + j += 1 # move right by one column i += 1 # move up by one row From f0c3997ae6510ee159a8461e3bb7137d1997e5b0 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 19:01:45 -0500 Subject: [PATCH 054/103] add under/over-specified specs tests --- plotly/tests/test_core/test_tools/test_get_subplots.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index bc5f036dba1..c4f0a0b4f70 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -638,6 +638,16 @@ def test_subplot_specs_wrong_type(): def test_subplot_specs_wrong_item(): fig = tls.get_subplots(specs=[{'not': "going to work"}]) +@raises(Exception) +def test_subplot_specs_underspecified(): + fig = tls.get_subplots(rows=2, specs=[{}]) + fig = tls.get_subplots(rows=2, columns=2, specs=[[{}, {}], [{}]]) + +@raises(Exception) +def test_subplot_specs_overspecified(): + fig = tls.get_subplots(rows=2, specs=[[{}], [{}], [{}]]) + fig = tls.get_subplots(columns=2, specs=[{}, {}, {}]) + def test_subplot_specs(): expected = Figure( data=Data(), From feea0cc9303b8e08fbafaafcfb46de399e939d9f Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 19:03:18 -0500 Subject: [PATCH 055/103] update tests: - {'is_empty': True} -> None - fill is specs with None after colspans --- .../test_core/test_tools/test_get_subplots.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index c4f0a0b4f70..28049ef87c2 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -687,7 +687,9 @@ def test_subplot_specs(): ) ) - fig = tls.get_subplots(rows=2, columns=3, specs=[[{}], [{}, {}, {}]]) + fig = tls.get_subplots(rows=2, columns=3, + specs=[[{}, None, None], + [{}, {}, {}]]) assert fig == expected @@ -739,7 +741,7 @@ def test_subplot_specs_colspan(): ) fig = tls.get_subplots(rows=3, columns=2, - specs=[[{'colspan':2}], + specs=[[{'colspan':2}, None], [{}, {}], [{}, {}]]) assert fig == expected @@ -801,8 +803,8 @@ def test_subplot_specs_rowspan(): fig = tls.get_subplots(rows=3, columns=3, specs=[[{'rowspan': 3}, {}, {}], - [{'is_empty': True}, {}, {}], - [{'is_empty': True}, {'colspan': 2}]]) + [None, {}, {}], + [None, {'colspan': 2}, None]]) assert fig == expected @@ -855,8 +857,10 @@ def test_subplot_specs_rowspan2(): fig = tls.get_subplots(rows=3, columns=3, specs=[[{}, {}, {'rowspan': 2}], - [{'colspan': 2}], - [{'colspan': 3}]]) + [{'colspan': 2}, None, None], + [{'colspan': 3}, None, None]]) + + assert fig == expected assert fig == expected From 98b955ff816c1bd6486b76d9fcdcdedecc5fc872 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 19:03:49 -0500 Subject: [PATCH 056/103] add a colspan>1 + rowspan>1 in the same cell test --- .../test_core/test_tools/test_get_subplots.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 28049ef87c2..2a211a78ae6 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -862,6 +862,66 @@ def test_subplot_specs_rowspan2(): assert fig == expected +def test_subplot_specs_colspan_rowpan(): + + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.6444444444444445], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y6' + ), + yaxis1=YAxis( + domain=[0.0, 0.6333333333333333], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x6' + ) + ) + ) + + fig = tls.get_subplots(rows=3, columns=3, + specs=[[{'colspan': 2, 'rowspan': 2}, None, {}], + [None, None, {}], + [{}, {}, {}]]) assert fig == expected def test_subplot_specs_is_3d(): From e5d753f1155039c31886fb5ef2638ae1dd3b451c Mon Sep 17 00:00:00 2001 From: etpinard Date: Sun, 4 Jan 2015 20:24:06 -0500 Subject: [PATCH 057/103] [need some feedback] add start_cell docstring --- plotly/tools.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index 0cde1c1193e..5b073cf1d26 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -388,7 +388,8 @@ def mpl_to_plotly(fig, resize=False, strip_style=False, verbose=False): def get_subplots(rows=1, columns=1, shared_xaxes=False, shared_yaxes=False, - print_grid=False, **kwargs): + start_cell='bottom-left', print_grid=False, + **kwargs): """Return an instance of plotly.graph_objs.Figure with the subplots domain set in 'layout'. @@ -444,6 +445,10 @@ def get_subplots(rows=1, columns=1, send list (or list of lists, one list per shared axis) of cell index tuples. + start_cell (kwarg, 'bottom-left' or 'top-left', default='bottom-left') + Choose the starting cell in the subplot grid used to set the + domains of the subplots. + print_grid (kwarg, boolean, default=False): If True, prints a tab-delimited string representation of your plot grid. From 0d74fb3f33588ae75bdf0f26ac5b3ad966dfdf65 Mon Sep 17 00:00:00 2001 From: etpinard Date: Tue, 6 Jan 2015 17:52:06 -0500 Subject: [PATCH 058/103] add back the old get_subplots : - rename new get_subplot make_subplots - add deprecation warning - keep checks for int 'rows' and 'columns' - keep variable 'horizontal_spacing' (function of 'columns') - keep variable 'vertical_spacing' (function of 'rows') --- plotly/tools.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index 5b073cf1d26..d9988a8cdf6 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -386,7 +386,111 @@ def mpl_to_plotly(fig, resize=False, strip_style=False, verbose=False): ### graph_objs related tools ### -def get_subplots(rows=1, columns=1, +def get_subplots(rows=1, columns=1, print_grid=False, **kwargs): + """Return a dictionary instance with the subplots set in 'layout'. + + Example 1: + # stack two subplots vertically + >>> fig = tools.get_subplots(rows=2) + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + + Example 2: + # print out string showing the subplot grid you've put in the layout + >>> fig = tools.get_subplots(rows=3, columns=2, print_grid=True) + + Keywords arguments with constant defaults: + + rows (int, default=1): + Number of rows, evenly spaced vertically on the figure. + + columns (int, default=1): + Number of columns, evenly spaced horizontally on the figure. + + horizontal_spacing (float in [0,1], default=0.1): + Space between subplot columns. Applied to all columns. + + vertical_spacing (float in [0,1], default=0.05): + Space between subplot rows. Applied to all rows. + + print_grid (True | False, default=False): + If True, prints a tab-delimited string representation of your plot grid. + + Keyword arguments with variable defaults: + + horizontal_spacing (kwarg, float in [0,1], default=0.2 / columns): + Space between subplot columns. + + vertical_spacing (kwarg, float in [0,1], default=0.3 / rows): + Space between subplot rows. + + """ + + warnings.warn( + "tools.get_subplots is depreciated. " + "Please use tools.make_subplots instead." + ) + + # Throw exception for non-integer rows and columns + if not isinstance(rows, int): + raise Exception("Keyword argument 'rows' must be an int") + if not isinstance(columns, int): + raise Exception("Keyword argument 'columns' must be an int") + + # Throw exception if non-valid kwarg is sent + VALID_KWARGS = ['horizontal_spacing', 'vertical_spacing'] + for key in kwargs.keys(): + if key not in VALID_KWARGS: + raise Exception("Invalid keyword argument: '{0}'".format(key)) + + # Set 'horizontal_spacing' / 'vertical_spacing' w.r.t. rows / columns + try: + horizontal_spacing = float(kwargs['horizontal_spacing']) + except KeyError: + horizontal_spacing = 0.2 / columns + try: + vertical_spacing = float(kwargs['vertical_spacing']) + except KeyError: + vertical_spacing = 0.3 / rows + + fig = dict(layout=graph_objs.Layout()) # will return this at the end + plot_width = (1 - horizontal_spacing * (columns - 1)) / columns + plot_height = (1 - vertical_spacing * (rows - 1)) / rows + plot_num = 0 + for rrr in range(rows): + for ccc in range(columns): + xaxis_name = 'xaxis{0}'.format(plot_num + 1) + x_anchor = 'y{0}'.format(plot_num + 1) + x_start = (plot_width + horizontal_spacing) * ccc + x_end = x_start + plot_width + + yaxis_name = 'yaxis{0}'.format(plot_num + 1) + y_anchor = 'x{0}'.format(plot_num + 1) + 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) + fig['layout'][xaxis_name] = xaxis + yaxis = graph_objs.YAxis(domain=[y_start, y_end], anchor=y_anchor) + fig['layout'][yaxis_name] = yaxis + plot_num += 1 + + if print_grid: + print("This is the format of your plot grid!") + grid_string = "" + plot = 1 + for rrr in range(rows): + grid_line = "" + for ccc in range(columns): + grid_line += "[{0}]\t".format(plot) + plot += 1 + grid_string = grid_line + '\n' + grid_string + print(grid_string) + + return graph_objs.Figure(fig) # forces us to validate what we just did... + + +def make_subplots(rows=1, cols=1, shared_xaxes=False, shared_yaxes=False, start_cell='bottom-left', print_grid=False, **kwargs): From 6df366ec822ebbe0716b176ff6222e371d84f200 Mon Sep 17 00:00:00 2001 From: etpinard Date: Tue, 6 Jan 2015 17:53:24 -0500 Subject: [PATCH 059/103] mv get_subplots argument checks tests to the top of file --- .../test_core/test_tools/test_get_subplots.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index 2a211a78ae6..c1743e3839c 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -3,6 +3,19 @@ import plotly.tools as tls from nose.tools import raises + +@raises(Exception) +def test_non_integer_rows(): + fig = tls.get_subplots(rows=2.1) + +@raises(Exception) +def test_non_integer_columns(): + fig = tls.get_subplots(columns=2/3) + +@raises(Exception) +def test_wrong_kwarg(): + fig = tls.get_subplots(stuff='no gonna work') + def test_get_single_plot(): expected = Figure( data=Data(), @@ -367,18 +380,6 @@ def test_spacing(): assert fig == expected -@raises(Exception) -def test_non_integer_rows(): - fig = tls.get_subplots(rows=2.1) - -@raises(Exception) -def test_non_integer_columns(): - fig = tls.get_subplots(columns=2/3) - -@raises(Exception) -def test_wrong_kwarg(): - fig = tls.get_subplots(stuff='no gonna work') - def test_default_spacing(): expected = Figure( data=Data(), From 5bcbaaae3c0184d03040ea8faa32f178b58be9ab Mon Sep 17 00:00:00 2001 From: etpinard Date: Tue, 6 Jan 2015 17:55:52 -0500 Subject: [PATCH 060/103] move make_subplots tests to test_make_subplots.py --- .../test_core/test_tools/test_get_subplots.py | 731 --------- .../test_tools/test_make_subplots.py | 1381 +++++++++++++++++ 2 files changed, 1381 insertions(+), 731 deletions(-) create mode 100644 plotly/tests/test_core/test_tools/test_make_subplots.py diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index c1743e3839c..b5df85ceadc 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -630,734 +630,3 @@ def test_default_spacing(): fig = tls.get_subplots(rows=6, columns=5) assert fig == expected - -@raises(Exception) -def test_subplot_specs_wrong_type(): - fig = tls.get_subplots(specs="not going to work") - -@raises(Exception) -def test_subplot_specs_wrong_item(): - fig = tls.get_subplots(specs=[{'not': "going to work"}]) - -@raises(Exception) -def test_subplot_specs_underspecified(): - fig = tls.get_subplots(rows=2, specs=[{}]) - fig = tls.get_subplots(rows=2, columns=2, specs=[[{}, {}], [{}]]) - -@raises(Exception) -def test_subplot_specs_overspecified(): - fig = tls.get_subplots(rows=2, specs=[[{}], [{}], [{}]]) - fig = tls.get_subplots(columns=2, specs=[{}, {}, {}]) - -def test_subplot_specs(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.2888888888888889], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.0, 0.2888888888888889], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.35555555555555557, 0.6444444444444445], - anchor='y3' - ), - xaxis4=XAxis( - domain=[0.7111111111111111, 1.0], - anchor='y4' - ), - yaxis1=YAxis( - domain=[0.0, 0.425], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.575, 1.0], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.575, 1.0], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.575, 1.0], - anchor='x4' - ) - ) - ) - - fig = tls.get_subplots(rows=2, columns=3, - specs=[[{}, None, None], - [{}, {}, {}]]) - - assert fig == expected - -def test_subplot_specs_colspan(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 1.0], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.0, 0.45], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.55, 1.0], - anchor='y3' - ), - xaxis4=XAxis( - domain=[0.0, 0.45], - anchor='y4' - ), - xaxis5=XAxis( - domain=[0.55, 1.0], - anchor='y5' - ), - yaxis1=YAxis( - domain=[0.0, 0.26666666666666666], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.36666666666666664, 0.6333333333333333], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.36666666666666664, 0.6333333333333333], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.7333333333333333, 1.0], - anchor='x4' - ), - yaxis5=YAxis( - domain=[0.7333333333333333, 1.0], - anchor='x5' - ) - ) - ) - - fig = tls.get_subplots(rows=3, columns=2, - specs=[[{'colspan':2}, None], - [{}, {}], - [{}, {}]]) - assert fig == expected - -def test_subplot_specs_rowspan(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.2888888888888889], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.35555555555555557, 0.6444444444444445], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.7111111111111111, 1.0], - anchor='y3' - ), - xaxis4=XAxis( - domain=[0.35555555555555557, 0.6444444444444445], - anchor='y4' - ), - xaxis5=XAxis( - domain=[0.7111111111111111, 1.0], - anchor='y5' - ), - xaxis6=XAxis( - domain=[0.35555555555555557, 1.0], - anchor='y6' - ), - yaxis1=YAxis( - domain=[0.0, 1.0], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.0, 0.26666666666666666], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.0, 0.26666666666666666], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.36666666666666664, 0.6333333333333333], - anchor='x4' - ), - yaxis5=YAxis( - domain=[0.36666666666666664, 0.6333333333333333], - anchor='x5' - ), - yaxis6=YAxis( - domain=[0.7333333333333333, 1.0], - anchor='x6' - ) - ) - ) - - fig = tls.get_subplots(rows=3, columns=3, - specs=[[{'rowspan': 3}, {}, {}], - [None, {}, {}], - [None, {'colspan': 2}, None]]) - - assert fig == expected - -def test_subplot_specs_rowspan2(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.2888888888888889], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.35555555555555557, 0.6444444444444445], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.7111111111111111, 1.0], - anchor='y3' - ), - xaxis4=XAxis( - domain=[0.0, 0.6444444444444445], - anchor='y4' - ), - xaxis5=XAxis( - domain=[0.0, 1.0], - anchor='y5' - ), - yaxis1=YAxis( - domain=[0.0, 0.26666666666666666], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.0, 0.26666666666666666], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.0, 0.6333333333333333], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.36666666666666664, 0.6333333333333333], - anchor='x4' - ), - yaxis5=YAxis( - domain=[0.7333333333333333, 1.0], - anchor='x5' - ) - ) - ) - - fig = tls.get_subplots(rows=3, columns=3, - specs=[[{}, {}, {'rowspan': 2}], - [{'colspan': 2}, None, None], - [{'colspan': 3}, None, None]]) - - assert fig == expected - -def test_subplot_specs_colspan_rowpan(): - - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.6444444444444445], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.7111111111111111, 1.0], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.7111111111111111, 1.0], - anchor='y3' - ), - xaxis4=XAxis( - domain=[0.0, 0.2888888888888889], - anchor='y4' - ), - xaxis5=XAxis( - domain=[0.35555555555555557, 0.6444444444444445], - anchor='y5' - ), - xaxis6=XAxis( - domain=[0.7111111111111111, 1.0], - anchor='y6' - ), - yaxis1=YAxis( - domain=[0.0, 0.6333333333333333], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.0, 0.26666666666666666], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.36666666666666664, 0.6333333333333333], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.7333333333333333, 1.0], - anchor='x4' - ), - yaxis5=YAxis( - domain=[0.7333333333333333, 1.0], - anchor='x5' - ), - yaxis6=YAxis( - domain=[0.7333333333333333, 1.0], - anchor='x6' - ) - ) - ) - - fig = tls.get_subplots(rows=3, columns=3, - specs=[[{'colspan': 2, 'rowspan': 2}, None, {}], - [None, None, {}], - [{}, {}, {}]]) - assert fig == expected - -def test_subplot_specs_is_3d(): - expected = Figure( - data=Data(), - layout=Layout( - scene1=Scene( - domain={'y': [0.0, 0.425], 'x': [0.0, 0.45]} - ), - scene2=Scene( - domain={'y': [0.575, 1.0], 'x': [0.0, 0.45]} - ), - xaxis1=XAxis( - domain=[0.55, 1.0], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.55, 1.0], - anchor='y2' - ), - yaxis1=YAxis( - domain=[0.0, 0.425], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.575, 1.0], - anchor='x2' - ) - ) - ) - - fig = tls.get_subplots(rows=2, columns=2, - specs=[[{'is_3d': True}, {}], - [{'is_3d': True}, {}]]) - - assert fig == expected - -def test_subplot_specs_padding(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.1, 0.5], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.5, 1.0], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.0, 0.5], - anchor='y3' - ), - xaxis4=XAxis( - domain=[0.5, 0.9], - anchor='y4' - ), - yaxis1=YAxis( - domain=[0.0, 0.5], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.2, 0.5], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.5, 0.8], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.5, 1.0], - anchor='x4' - ) - ) - ) - - fig = tls.get_subplots(rows=2, columns=2, - horizontal_spacing=0, vertical_spacing=0, - specs=[[{'l': 0.1}, {'b': 0.2}], - [{'t': 0.2}, {'r': 0.1}]]) - - assert fig == expected - -def test_subplot_shared_xaxes(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.2888888888888889], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.35555555555555557, 0.6444444444444445], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.7111111111111111, 1.0], - anchor='y3' - ), - yaxis1=YAxis( - domain=[0.0, 0.425], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.0, 0.425], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.0, 0.425], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.575, 1.0], - anchor='free' - ), - yaxis5=YAxis( - domain=[0.575, 1.0], - anchor='free', - position=0.35555555555555557 - ), - yaxis6=YAxis( - domain=[0.575, 1.0], - anchor='free', - position=0.7111111111111111 - ) - ) - ) - - fig = tls.get_subplots(rows=2, columns=3, shared_xaxes=True) - - assert fig == expected - -def test_subplot_shared_yaxes(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.45], - anchor='y1' - ), - xaxis10=XAxis( - domain=[0.55, 1.0], - anchor='free', - position=0.848 - ), - xaxis2=XAxis( - domain=[0.55, 1.0], - anchor='free' - ), - xaxis3=XAxis( - domain=[0.0, 0.45], - anchor='y2' - ), - xaxis4=XAxis( - domain=[0.55, 1.0], - anchor='free', - position=0.212 - ), - xaxis5=XAxis( - domain=[0.0, 0.45], - anchor='y3' - ), - xaxis6=XAxis( - domain=[0.55, 1.0], - anchor='free', - position=0.424 - ), - xaxis7=XAxis( - domain=[0.0, 0.45], - anchor='y4' - ), - xaxis8=XAxis( - domain=[0.55, 1.0], - anchor='free', - position=0.636 - ), - xaxis9=XAxis( - domain=[0.0, 0.45], - anchor='y5' - ), - yaxis1=YAxis( - domain=[0.0, 0.152], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.212, 0.364], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.424, 0.576], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.636, 0.788], - anchor='x4' - ), - yaxis5=YAxis( - domain=[0.848, 1.0], - anchor='x5' - ) - ) - ) - - fig = tls.get_subplots(rows=5, columns=2, shared_yaxes=True) - - assert fig == expected - -def test_subplot_shared_xaxes_yaxes(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.2888888888888889], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.35555555555555557, 0.6444444444444445], - anchor='free' - ), - xaxis3=XAxis( - domain=[0.7111111111111111, 1.0], - anchor='free' - ), - yaxis1=YAxis( - domain=[0.0, 0.26666666666666666], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.36666666666666664, 0.6333333333333333], - anchor='free' - ), - yaxis3=YAxis( - domain=[0.7333333333333333, 1.0], - anchor='free' - ) - ) - ) - fig = tls.get_subplots(rows=3, columns=3, - shared_xaxes=True, shared_yaxes=True) - - assert fig == expected - -def test_subplot_shared_axes_list(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.45], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.55, 1.0], - anchor='free' - ), - xaxis3=XAxis( - domain=[0.55, 1.0], - anchor='y3' - ), - yaxis1=YAxis( - domain=[0.0, 0.425], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.575, 1.0], - anchor='free' - ), - yaxis3=YAxis( - domain=[0.575, 1.0], - anchor='x3' - ) - ) - ) - - fig = tls.get_subplots(rows=2, columns=2, - shared_xaxes=[(0,0), (1,0)], - shared_yaxes=[(0,0), (0,1)]) - - assert fig == expected - - -def test_subplot_shared_axes_list_of_lists(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.2888888888888889], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.35555555555555557, 0.6444444444444445], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.7111111111111111, 1.0], - anchor='y3' - ), - xaxis4=XAxis( - domain=[0.35555555555555557, 0.6444444444444445], - anchor='y5' - ), - yaxis1=YAxis( - domain=[0.0, 0.425], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.0, 0.425], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.0, 0.425], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.575, 1.0], - anchor='free' - ), - yaxis5=YAxis( - domain=[0.575, 1.0], - anchor='x4' - ), - yaxis6=YAxis( - domain=[0.575, 1.0], - anchor='free', - position=0.7111111111111111 - ) - ) - ) - - fig = tls.get_subplots(rows=2, columns=3, - shared_xaxes=[[(0,0), (1,0)], - [(0,2), (1,2)]]) - - assert fig == expected - -@raises(Exception) -def test_subplot_insets_wrong_type(): - fig = tls.get_subplots(insets="not going to work") - -@raises(Exception) -def test_subplot_insets_wrong_item(): - fig = tls.get_subplots(insets=[{'not': "going to work"}]) - -def test_subplot_insets(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.45], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.55, 1.0], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.0, 0.45], - anchor='y3' - ), - xaxis4=XAxis( - domain=[0.55, 1.0], - anchor='y4' - ), - xaxis5=XAxis( - domain=[0.865, 0.955], - anchor='y5' - ), - yaxis1=YAxis( - domain=[0.0, 0.425], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.0, 0.425], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.575, 1.0], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.575, 1.0], - anchor='x4' - ), - yaxis5=YAxis( - domain=[0.6599999999999999, 0.8724999999999999], - anchor='x5' - ) - ) - ) - - fig = tls.get_subplots(rows=2, columns=2, - insets=[{'cell': (1,1), - 'l': 0.7, 'w': 0.2, - 'b': 0.2, 'h': 0.5}]) - - assert fig == expected - -def test_subplot_insets_multiple(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 1.0], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.0, 1.0], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.8, 1.0], - anchor='y3' - ), - xaxis4=XAxis( - domain=[0.8, 1.0], - anchor='y4' - ), - yaxis1=YAxis( - domain=[0.0, 0.425], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.575, 1.0], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.0, 0.425], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.575, 1.0], - anchor='x4' - ) - ) - ) - - fig = tls.get_subplots(rows=2, - insets=[{'cell': (0,0), 'l':0.8}, - {'cell': (1,0), 'l':0.8}]) diff --git a/plotly/tests/test_core/test_tools/test_make_subplots.py b/plotly/tests/test_core/test_tools/test_make_subplots.py new file mode 100644 index 00000000000..3ba9109ff4f --- /dev/null +++ b/plotly/tests/test_core/test_tools/test_make_subplots.py @@ -0,0 +1,1381 @@ +import plotly +from plotly.graph_objs import * +import plotly.tools as tls +from nose.tools import raises + +def test_get_single_plot(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 1.0], + anchor='y1' + ), + yaxis1=YAxis( + domain=[0.0, 1.0], + anchor='x1' + ) + ) + ) + assert tls.get_subplots() == expected + +def test_two_row(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 1.0], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.0, 1.0], + anchor='y2' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='x2' + ) + ) + ) + assert tls.get_subplots(2) == expected + + +def test_two_column(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.45], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.55, 1.0], + anchor='y2' + ), + yaxis1=YAxis( + domain=[0.0, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 1.0], + anchor='x2' + ) + ) + ) + + assert tls.get_subplots(1, 2) == expected + +def test_a_lot(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.05714285714285713], + anchor='y1' + ), + xaxis10=XAxis( + domain=[0.3142857142857143, 0.3714285714285714], + anchor='y10' + ), + xaxis11=XAxis( + domain=[0.4714285714285714, 0.5285714285714286], + anchor='y11' + ), + xaxis12=XAxis( + domain=[0.6285714285714286, 0.6857142857142857], + anchor='y12' + ), + xaxis13=XAxis( + domain=[0.7857142857142857, 0.8428571428571429], + anchor='y13' + ), + xaxis14=XAxis( + domain=[0.9428571428571428, 1.0], + anchor='y14' + ), + xaxis15=XAxis( + domain=[0.0, 0.05714285714285713], + anchor='y15' + ), + xaxis16=XAxis( + domain=[0.15714285714285714, 0.21428571428571427], + anchor='y16' + ), + xaxis17=XAxis( + domain=[0.3142857142857143, 0.3714285714285714], + anchor='y17' + ), + xaxis18=XAxis( + domain=[0.4714285714285714, 0.5285714285714286], + anchor='y18' + ), + xaxis19=XAxis( + domain=[0.6285714285714286, 0.6857142857142857], + anchor='y19' + ), + xaxis2=XAxis( + domain=[0.15714285714285714, 0.21428571428571427], + anchor='y2' + ), + xaxis20=XAxis( + domain=[0.7857142857142857, 0.8428571428571429], + anchor='y20' + ), + xaxis21=XAxis( + domain=[0.9428571428571428, 1.0], + anchor='y21' + ), + xaxis22=XAxis( + domain=[0.0, 0.05714285714285713], + anchor='y22' + ), + xaxis23=XAxis( + domain=[0.15714285714285714, 0.21428571428571427], + anchor='y23' + ), + xaxis24=XAxis( + domain=[0.3142857142857143, 0.3714285714285714], + anchor='y24' + ), + xaxis25=XAxis( + domain=[0.4714285714285714, 0.5285714285714286], + anchor='y25' + ), + xaxis26=XAxis( + domain=[0.6285714285714286, 0.6857142857142857], + anchor='y26' + ), + xaxis27=XAxis( + domain=[0.7857142857142857, 0.8428571428571429], + anchor='y27' + ), + xaxis28=XAxis( + domain=[0.9428571428571428, 1.0], + anchor='y28' + ), + xaxis3=XAxis( + domain=[0.3142857142857143, 0.3714285714285714], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.4714285714285714, 0.5285714285714286], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.6285714285714286, 0.6857142857142857], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.7857142857142857, 0.8428571428571429], + anchor='y6' + ), + xaxis7=XAxis( + domain=[0.9428571428571428, 1.0], + anchor='y7' + ), + xaxis8=XAxis( + domain=[0.0, 0.05714285714285713], + anchor='y8' + ), + xaxis9=XAxis( + domain=[0.15714285714285714, 0.21428571428571427], + anchor='y9' + ), + yaxis1=YAxis( + domain=[0.0, 0.1375], + anchor='x1' + ), + yaxis10=YAxis( + domain=[0.2875, 0.425], + anchor='x10' + ), + yaxis11=YAxis( + domain=[0.2875, 0.425], + anchor='x11' + ), + yaxis12=YAxis( + domain=[0.2875, 0.425], + anchor='x12' + ), + yaxis13=YAxis( + domain=[0.2875, 0.425], + anchor='x13' + ), + yaxis14=YAxis( + domain=[0.2875, 0.425], + anchor='x14' + ), + yaxis15=YAxis( + domain=[0.575, 0.7124999999999999], + anchor='x15' + ), + yaxis16=YAxis( + domain=[0.575, 0.7124999999999999], + anchor='x16' + ), + yaxis17=YAxis( + domain=[0.575, 0.7124999999999999], + anchor='x17' + ), + yaxis18=YAxis( + domain=[0.575, 0.7124999999999999], + anchor='x18' + ), + yaxis19=YAxis( + domain=[0.575, 0.7124999999999999], + anchor='x19' + ), + yaxis2=YAxis( + domain=[0.0, 0.1375], + anchor='x2' + ), + yaxis20=YAxis( + domain=[0.575, 0.7124999999999999], + anchor='x20' + ), + yaxis21=YAxis( + domain=[0.575, 0.7124999999999999], + anchor='x21' + ), + yaxis22=YAxis( + domain=[0.8624999999999999, 1.0], + anchor='x22' + ), + yaxis23=YAxis( + domain=[0.8624999999999999, 1.0], + anchor='x23' + ), + yaxis24=YAxis( + domain=[0.8624999999999999, 1.0], + anchor='x24' + ), + yaxis25=YAxis( + domain=[0.8624999999999999, 1.0], + anchor='x25' + ), + yaxis26=YAxis( + domain=[0.8624999999999999, 1.0], + anchor='x26' + ), + yaxis27=YAxis( + domain=[0.8624999999999999, 1.0], + anchor='x27' + ), + yaxis28=YAxis( + domain=[0.8624999999999999, 1.0], + anchor='x28' + ), + yaxis3=YAxis( + domain=[0.0, 0.1375], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.0, 0.1375], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.0, 0.1375], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.0, 0.1375], + anchor='x6' + ), + yaxis7=YAxis( + domain=[0.0, 0.1375], + anchor='x7' + ), + yaxis8=YAxis( + domain=[0.2875, 0.425], + anchor='x8' + ), + yaxis9=YAxis( + domain=[0.2875, 0.425], + anchor='x9' + ) + ) + ) + + fig = tls.get_subplots(4, 7, + horizontal_spacing=0.1, + vertical_spacing=0.15) + + assert fig == expected + +def test_spacing(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.3], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35, 0.6499999999999999], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.0, 0.3], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.35, 0.6499999999999999], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.7, 1.0], + anchor='y6' + ), + yaxis1=YAxis( + domain=[0.0, 0.45], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.45], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.45], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.55, 1.0], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.55, 1.0], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.55, 1.0], + anchor='x6' + ) + ) + ) + + fig = tls.get_subplots(2, 3, + horizontal_spacing=.05, + vertical_spacing=.1) + + assert fig == expected + +@raises(Exception) +def test_non_integer_rows(): + fig = tls.get_subplots(rows=2.1) + +@raises(Exception) +def test_non_integer_columns(): + fig = tls.get_subplots(columns=2/3) + +@raises(Exception) +def test_wrong_kwarg(): + fig = tls.get_subplots(stuff='no gonna work') + +def test_default_spacing(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.16799999999999998], + anchor='y1' + ), + xaxis10=XAxis( + domain=[0.832, 1.0], + anchor='y10' + ), + xaxis11=XAxis( + domain=[0.0, 0.16799999999999998], + anchor='y11' + ), + xaxis12=XAxis( + domain=[0.208, 0.376], + anchor='y12' + ), + xaxis13=XAxis( + domain=[0.416, 0.584], + anchor='y13' + ), + xaxis14=XAxis( + domain=[0.624, 0.792], + anchor='y14' + ), + xaxis15=XAxis( + domain=[0.832, 1.0], + anchor='y15' + ), + xaxis16=XAxis( + domain=[0.0, 0.16799999999999998], + anchor='y16' + ), + xaxis17=XAxis( + domain=[0.208, 0.376], + anchor='y17' + ), + xaxis18=XAxis( + domain=[0.416, 0.584], + anchor='y18' + ), + xaxis19=XAxis( + domain=[0.624, 0.792], + anchor='y19' + ), + xaxis2=XAxis( + domain=[0.208, 0.376], + anchor='y2' + ), + xaxis20=XAxis( + domain=[0.832, 1.0], + anchor='y20' + ), + xaxis21=XAxis( + domain=[0.0, 0.16799999999999998], + anchor='y21' + ), + xaxis22=XAxis( + domain=[0.208, 0.376], + anchor='y22' + ), + xaxis23=XAxis( + domain=[0.416, 0.584], + anchor='y23' + ), + xaxis24=XAxis( + domain=[0.624, 0.792], + anchor='y24' + ), + xaxis25=XAxis( + domain=[0.832, 1.0], + anchor='y25' + ), + xaxis26=XAxis( + domain=[0.0, 0.16799999999999998], + anchor='y26' + ), + xaxis27=XAxis( + domain=[0.208, 0.376], + anchor='y27' + ), + xaxis28=XAxis( + domain=[0.416, 0.584], + anchor='y28' + ), + xaxis29=XAxis( + domain=[0.624, 0.792], + anchor='y29' + ), + xaxis3=XAxis( + domain=[0.416, 0.584], + anchor='y3' + ), + xaxis30=XAxis( + domain=[0.832, 1.0], + anchor='y30' + ), + xaxis4=XAxis( + domain=[0.624, 0.792], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.832, 1.0], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.0, 0.16799999999999998], + anchor='y6' + ), + xaxis7=XAxis( + domain=[0.208, 0.376], + anchor='y7' + ), + xaxis8=XAxis( + domain=[0.416, 0.584], + anchor='y8' + ), + xaxis9=XAxis( + domain=[0.624, 0.792], + anchor='y9' + ), + yaxis1=YAxis( + domain=[0.0, 0.125], + anchor='x1' + ), + yaxis10=YAxis( + domain=[0.175, 0.3], + anchor='x10' + ), + yaxis11=YAxis( + domain=[0.35, 0.475], + anchor='x11' + ), + yaxis12=YAxis( + domain=[0.35, 0.475], + anchor='x12' + ), + yaxis13=YAxis( + domain=[0.35, 0.475], + anchor='x13' + ), + yaxis14=YAxis( + domain=[0.35, 0.475], + anchor='x14' + ), + yaxis15=YAxis( + domain=[0.35, 0.475], + anchor='x15' + ), + yaxis16=YAxis( + domain=[0.5249999999999999, 0.6499999999999999], + anchor='x16' + ), + yaxis17=YAxis( + domain=[0.5249999999999999, 0.6499999999999999], + anchor='x17' + ), + yaxis18=YAxis( + domain=[0.5249999999999999, 0.6499999999999999], + anchor='x18' + ), + yaxis19=YAxis( + domain=[0.5249999999999999, 0.6499999999999999], + anchor='x19' + ), + yaxis2=YAxis( + domain=[0.0, 0.125], + anchor='x2' + ), + yaxis20=YAxis( + domain=[0.5249999999999999, 0.6499999999999999], + anchor='x20' + ), + yaxis21=YAxis( + domain=[0.7, 0.825], + anchor='x21' + ), + yaxis22=YAxis( + domain=[0.7, 0.825], + anchor='x22' + ), + yaxis23=YAxis( + domain=[0.7, 0.825], + anchor='x23' + ), + yaxis24=YAxis( + domain=[0.7, 0.825], + anchor='x24' + ), + yaxis25=YAxis( + domain=[0.7, 0.825], + anchor='x25' + ), + yaxis26=YAxis( + domain=[0.875, 1.0], + anchor='x26' + ), + yaxis27=YAxis( + domain=[0.875, 1.0], + anchor='x27' + ), + yaxis28=YAxis( + domain=[0.875, 1.0], + anchor='x28' + ), + yaxis29=YAxis( + domain=[0.875, 1.0], + anchor='x29' + ), + yaxis3=YAxis( + domain=[0.0, 0.125], + anchor='x3' + ), + yaxis30=YAxis( + domain=[0.875, 1.0], + anchor='x30' + ), + yaxis4=YAxis( + domain=[0.0, 0.125], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.0, 0.125], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.175, 0.3], + anchor='x6' + ), + yaxis7=YAxis( + domain=[0.175, 0.3], + anchor='x7' + ), + yaxis8=YAxis( + domain=[0.175, 0.3], + anchor='x8' + ), + yaxis9=YAxis( + domain=[0.175, 0.3], + anchor='x9' + ) + ) + ) + + fig = tls.get_subplots(rows=6, columns=5) + + assert fig == expected + +@raises(Exception) +def test_subplot_specs_wrong_type(): + fig = tls.get_subplots(specs="not going to work") + +@raises(Exception) +def test_subplot_specs_wrong_inner_type(): + fig = tls.get_subplots(specs=[{}]) + +@raises(Exception) +def test_subplot_specs_wrong_item_type(): + fig = tls.get_subplots(specs=[[('not', 'going to work')]]) + +@raises(Exception) +def test_subplot_specs_wrong_item_key(): + fig = tls.get_subplots(specs=[{'not': "going to work"}]) + +@raises(Exception) +def test_subplot_specs_underspecified(): + fig = tls.get_subplots(rows=2, specs=[{}]) + fig = tls.get_subplots(rows=2, columns=2, specs=[[{}, {}], [{}]]) + +# @raises(Exception) +# def test_subplot_specs_underspecified2(): + + +@raises(Exception) +def test_subplot_specs_overspecified(): + fig = tls.get_subplots(rows=2, specs=[[{}], [{}], [{}]]) + fig = tls.get_subplots(columns=2, specs=[{}, {}, {}]) + +def test_subplot_specs(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y4' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.575, 1.0], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.575, 1.0], + anchor='x4' + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=3, + specs=[[{}, None, None], + [{}, {}, {}]]) + + assert fig == expected + +def test_subplot_specs_colspan(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 1.0], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.0, 0.45], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.55, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.0, 0.45], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.55, 1.0], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x5' + ) + ) + ) + + fig = tls.get_subplots(rows=3, columns=2, + specs=[[{'colspan':2}, None], + [{}, {}], + [{}, {}]]) + assert fig == expected + +def test_subplot_specs_rowspan(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.35555555555555557, 1.0], + anchor='y6' + ), + yaxis1=YAxis( + domain=[0.0, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x6' + ) + ) + ) + + fig = tls.get_subplots(rows=3, columns=3, + specs=[[{'rowspan': 3}, {}, {}], + [None, {}, {}], + [None, {'colspan': 2}, None]]) + + assert fig == expected + +def test_subplot_specs_rowspan2(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.0, 0.6444444444444445], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.0, 1.0], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.6333333333333333], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x5' + ) + ) + ) + + fig = tls.get_subplots(rows=3, columns=3, + specs=[[{}, {}, {'rowspan': 2}], + [{'colspan': 2}, None, None], + [{'colspan': 3}, None, None]]) + + assert fig == expected + +def test_subplot_specs_colspan_rowpan(): + + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.6444444444444445], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y6' + ), + yaxis1=YAxis( + domain=[0.0, 0.6333333333333333], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x6' + ) + ) + ) + + fig = tls.get_subplots(rows=3, columns=3, + specs=[[{'colspan': 2, 'rowspan': 2}, None, {}], + [None, None, {}], + [{}, {}, {}]]) + assert fig == expected + +def test_subplot_specs_is_3d(): + expected = Figure( + data=Data(), + layout=Layout( + scene1=Scene( + domain={'y': [0.0, 0.425], 'x': [0.0, 0.45]} + ), + scene2=Scene( + domain={'y': [0.575, 1.0], 'x': [0.0, 0.45]} + ), + xaxis1=XAxis( + domain=[0.55, 1.0], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.55, 1.0], + anchor='y2' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='x2' + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=2, + specs=[[{'is_3d': True}, {}], + [{'is_3d': True}, {}]]) + + assert fig == expected + +def test_subplot_specs_padding(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.1, 0.5], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.5, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.0, 0.5], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.5, 0.9], + anchor='y4' + ), + yaxis1=YAxis( + domain=[0.0, 0.5], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.2, 0.5], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.5, 0.8], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.5, 1.0], + anchor='x4' + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=2, + horizontal_spacing=0, vertical_spacing=0, + specs=[[{'l': 0.1}, {'b': 0.2}], + [{'t': 0.2}, {'r': 0.1}]]) + + assert fig == expected + +def test_subplot_shared_xaxes(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.425], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.425], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.575, 1.0], + anchor='free' + ), + yaxis5=YAxis( + domain=[0.575, 1.0], + anchor='free', + position=0.35555555555555557 + ), + yaxis6=YAxis( + domain=[0.575, 1.0], + anchor='free', + position=0.7111111111111111 + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=3, shared_xaxes=True) + + assert fig == expected + +def test_subplot_shared_yaxes(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.45], + anchor='y1' + ), + xaxis10=XAxis( + domain=[0.55, 1.0], + anchor='free', + position=0.848 + ), + xaxis2=XAxis( + domain=[0.55, 1.0], + anchor='free' + ), + xaxis3=XAxis( + domain=[0.0, 0.45], + anchor='y2' + ), + xaxis4=XAxis( + domain=[0.55, 1.0], + anchor='free', + position=0.212 + ), + xaxis5=XAxis( + domain=[0.0, 0.45], + anchor='y3' + ), + xaxis6=XAxis( + domain=[0.55, 1.0], + anchor='free', + position=0.424 + ), + xaxis7=XAxis( + domain=[0.0, 0.45], + anchor='y4' + ), + xaxis8=XAxis( + domain=[0.55, 1.0], + anchor='free', + position=0.636 + ), + xaxis9=XAxis( + domain=[0.0, 0.45], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.0, 0.152], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.212, 0.364], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.424, 0.576], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.636, 0.788], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.848, 1.0], + anchor='x5' + ) + ) + ) + + fig = tls.get_subplots(rows=5, columns=2, shared_yaxes=True) + + assert fig == expected + +def test_subplot_shared_xaxes_yaxes(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='free' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='free' + ), + yaxis1=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='free' + ), + yaxis3=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='free' + ) + ) + ) + fig = tls.get_subplots(rows=3, columns=3, + shared_xaxes=True, shared_yaxes=True) + + assert fig == expected + +def test_subplot_shared_axes_list(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.45], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.55, 1.0], + anchor='free' + ), + xaxis3=XAxis( + domain=[0.55, 1.0], + anchor='y3' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='free' + ), + yaxis3=YAxis( + domain=[0.575, 1.0], + anchor='x3' + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=2, + shared_xaxes=[(1,1), (2,1)], + shared_yaxes=[(1,1), (1,2)]) + + assert fig == expected + + +def test_subplot_shared_axes_list_of_lists(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.425], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.425], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.575, 1.0], + anchor='free' + ), + yaxis5=YAxis( + domain=[0.575, 1.0], + anchor='x4' + ), + yaxis6=YAxis( + domain=[0.575, 1.0], + anchor='free', + position=0.7111111111111111 + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=3, + shared_xaxes=[[(1,1), (2,1)], + [(1,3), (2,3)]]) + + assert fig == expected + +@raises(Exception) +def test_subplot_insets_wrong_type(): + fig = tls.get_subplots(insets="not going to work") + +@raises(Exception) +def test_subplot_insets_wrong_item(): + fig = tls.get_subplots(insets=[{'not': "going to work"}]) + +def test_subplot_insets(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.45], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.55, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.0, 0.45], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.55, 1.0], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.865, 0.955], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.425], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.575, 1.0], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.575, 1.0], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.6599999999999999, 0.8724999999999999], + anchor='x5' + ) + ) + ) + + fig = tls.get_subplots(rows=2, columns=2, + insets=[{'cell': (2,2), + 'l': 0.7, 'w': 0.2, + 'b': 0.2, 'h': 0.5}]) + + assert fig == expected + +def test_subplot_insets_multiple(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 1.0], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.0, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.8, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.8, 1.0], + anchor='y4' + ), + yaxis1=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.425], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.575, 1.0], + anchor='x4' + ) + ) + ) + + fig = tls.get_subplots(rows=2, + insets=[{'cell': (1,1), 'l':0.8}, + {'cell': (2,1), 'l':0.8}]) + + +@raises(Exception) +def test_start_cell_wrong_values(): + fig = tls.get_subplots(rows=2, columns=2, start_cell='not gonna work') + + From 2edb810828e10ddea5b9cca81c82c91e565c95c6 Mon Sep 17 00:00:00 2001 From: etpinard Date: Tue, 6 Jan 2015 18:11:32 -0500 Subject: [PATCH 061/103] pep8 get_subplots docstring --- plotly/tools.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index d9988a8cdf6..94180144c55 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -414,7 +414,8 @@ def get_subplots(rows=1, columns=1, print_grid=False, **kwargs): Space between subplot rows. Applied to all rows. print_grid (True | False, default=False): - If True, prints a tab-delimited string representation of your plot grid. + If True, prints a tab-delimited string representation + of your plot grid. Keyword arguments with variable defaults: From 421dd5fba5b7859fb2c5fa66b89e678c4c0dff36 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 12:42:15 -0500 Subject: [PATCH 062/103] improve make_subplots docstring --- plotly/tools.py | 99 +++++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 37 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 94180144c55..ae37c1878b4 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -499,46 +499,63 @@ def make_subplots(rows=1, cols=1, with the subplots domain set in 'layout'. Example 1: - # stack two subplots vertically - fig = tools.get_subplots(rows=2) - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + # stack two subplots vertically + >>> fig = tools.make_subplots(rows=2, print_grid=True) + This is the format of your plot grid! + [ (1,1) x1,y1 ] + [ (2,1) x2,y2 ] + + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] Example 2: - # subplots with shared x axes - fig = tools.get_subplots(rows=2, shared_xaxes=True) - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y1')] - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y2')] + # subplots with shared x axes + >>> fig = tools.make_subplots(rows=2, shared_xaxes=True, print_grid=True) + + TODO What's the default behavior here?? + + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y1')] + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y2')] Example 3: - # irregular subplot layout - fig = tools.get_subplots(rows=2, columns=2, - specs=[[{}, {}], [{'colspan': 2}]]) - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x3', yaxis='y3')] + # irregular subplot layout (more examples below under 'specs') + >>> fig = tools.make_subplots(rows=2, cols=2, + specs=[[{}, {}], + [{'colspan': 2}, None]], + print_grid=True) + This is the format of your plot grid! + [ (1,1) x1,y1 ] [ (1,2) x2,y2 ] + [ (2,1) x3,y3 - ] + + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x3', yaxis='y3')] Example 4: - # insets - fig = tools.get_subplots(insets=[{'cell':(0,0), 'l': 0.7, 'b': 0.3}]) - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2])] - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + # insets + >>> fig = tools.make_subplots(insets=[{'cell': (1,1), 'l': 0.7, 'b': 0.3}], + print_grid=True) + This is the format of your plot grid! + [ (1,1) x1,y1 ] - Example 5: - # print out string showing the subplot grid you've put in the layout - fig = tools.get_subplots(rows=3, columns=2, print_grid=True) + With insets: + [ x2,y2 ] over [ (1,1) x1,y1 ] + + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2])] + >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] Keywords arguments with constant defaults: rows (kwarg, int, default=1): - Number of rows on the figure. + Number of rows in the subplot grid. - columns (kwarg, int, default=1): - Number of columns on the figure. + cols (kwarg, int, default=1): + Number of columns in the subplot grid. shared_xaxes (kwarg, boolean or list, default=False) Assign shared x axes. If True, all x axes are shared. + To assign shared x axes per subplot grid cell (see 'specs'), send list (or list of lists, one list per shared axis) of cell index tuples. @@ -546,11 +563,12 @@ def make_subplots(rows=1, cols=1, shared_yaxes (kwarg, boolean or list, default=False) Assign shared y axes. If True, all y axes are shared. + To assign shared y axes per subplot grid cell (see 'specs'), send list (or list of lists, one list per shared axis) of cell index tuples. - start_cell (kwarg, 'bottom-left' or 'top-left', default='bottom-left') + start_cell (kwarg, 'bottom-left' or 'top-left', default='top-left') Choose the starting cell in the subplot grid used to set the domains of the subplots. @@ -560,54 +578,61 @@ def make_subplots(rows=1, cols=1, Keyword arguments with variable defaults: - horizontal_spacing (kwarg, float in [0,1], default=0.2 / columns): + horizontal_spacing (kwarg, float in [0,1], default=0.2 / cols): Space between subplot columns. - Applies to all columns (use 'specs' subplot-dependents spacing) vertical_spacing (kwarg, float in [0,1], default=0.3 / rows): Space between subplot rows. Applies to all rows (use 'specs' subplot-dependents spacing) - specs (kwarg, list (of lists) of dictionaries): + specs (kwarg, list of lists of dictionaries): Subplot specifications. + ex1: specs=[[{}, {}], [{'colspan': 2}, None]] + + ex2: specs=[[{'rowspan': 2}, {}], [None, {}]] + - Indices of the outer list correspond to subplot grid rows starting from the bottom. The number of rows in 'specs' must be equal to 'rows'. - Indices of the inner lists correspond to subplot grid columns starting from the left. The number of columns in 'specs' - must be equal to 'columns'. + must be equal to 'cols'. - Each item in the 'specs' list corresponds to one subplot - in a subplot grid. The subplot grid has exactly 'rows' - times 'columns' cells. + in a subplot grid. (N.B. The subplot grid has exactly 'rows' + times 'cols' cells.) - Use None for blank a subplot cell (or to move pass a col/row span). - - Note that specs[0][0] has the specs for the bottom-left subplot + - Note that specs[0][0] has the specs of the 'start_cell' subplot. - Each item in 'specs' is a dictionary. The available keys are: * is_3d (boolean, default=False): flag for 3d scenes - * colspan (int, default=1): span across grid columns - from left to right - * rowspan (int, default=1): span across grid rows - from bottom to top + * colspan (int, default=1): number of subplot columns + for this subplot to span. + * rowspan (int, default=1): number of subplot rows + for this subplot to span. * l (float, default=0.0): padding left of cell * r (float, default=0.0): padding right of cell * t (float, default=0.0): padding right of cell * b (float, default=0.0): padding bottom of cell + - Use 'horizontal_spacing' and 'vertical_spacing' to adjust + the spacing in between the subplots. + insets (kwarg, list of dictionaries): Inset specifications. - Each item in 'insets' is a dictionary. The available keys are: - * cell (tuple, default=(0,0)) subplot cell indices + * cell (tuple, default=(1,1)): (row, col) index of the + subplot cell to overlay inset axes onto. * is_3d (boolean, default=False): flag for 3d scenes * l (float, default=0.0): padding left of inset in fraction of cell width From af67e906147b9426afed7e97bec67503624db0b0 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:25:55 -0500 Subject: [PATCH 063/103] replace 'columns' with 'cols' --- plotly/tools.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index ae37c1878b4..dd8368f0f2c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -644,11 +644,11 @@ def make_subplots(rows=1, cols=1, in fraction of cell height ('to_end': to cell top edge) """ - # Throw exception for non-integer rows and columns + # Throw exception for non-integer rows and cols if not isinstance(rows, int): raise Exception("Keyword argument 'rows' must be an int") - if not isinstance(columns, int): - raise Exception("Keyword argument 'columns' must be an int") + if not isinstance(cols, int): + raise Exception("Keyword argument 'cols' must be an int") # Throw exception if non-valid kwarg is sent VALID_KWARGS = ['horizontal_spacing', 'vertical_spacing', @@ -657,11 +657,11 @@ def make_subplots(rows=1, cols=1, if key not in VALID_KWARGS: raise Exception("Invalid keyword argument: '{0}'".format(key)) - # Set 'horizontal_spacing' / 'vertical_spacing' w.r.t. rows / columns + # Set 'horizontal_spacing' / 'vertical_spacing' w.r.t. rows / cols try: horizontal_spacing = float(kwargs['horizontal_spacing']) except KeyError: - horizontal_spacing = 0.2 / columns + horizontal_spacing = 0.2 / cols try: vertical_spacing = float(kwargs['vertical_spacing']) except KeyError: @@ -677,17 +677,17 @@ def make_subplots(rows=1, cols=1, specs = [specs] except KeyError: specs = [[{} - for col in range(columns)] - for row in range(rows)] # default 'specs' + for c in range(cols)] + for r in range(rows)] # default 'specs' # Throw exception if specs is over or under specified if len(specs) != rows: raise Exception("The number of rows in 'specs' " "must be equal to 'rows'") - for spec_row in specs: - if len(spec_row) != columns: + for r, spec_row in enumerate(specs): + if len(spec_row) != cols: raise Exception("The number of columns in 'specs' " - "must be equal to 'columns'") + "must be equal to 'cols'") # Sanitize 'insets' try: @@ -740,7 +740,7 @@ def _checks(item, defaults): _check_keys_and_fill('insets', insets, INSET_defaults) # Set width & height of each subplot cell (excluding padding) - width = (1. - horizontal_spacing * (columns - 1)) / columns + width = (1. - horizontal_spacing * (cols - 1)) / cols height = (1. - vertical_spacing * (rows - 1)) / rows # Build subplot grid (tuple of starting coords for each cell) From 7bb6ba23e0e7392715bd8d519be19e77eb186d0b Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:27:25 -0500 Subject: [PATCH 064/103] rm '>>>' in docstring --- plotly/tools.py | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index dd8368f0f2c..72fcf3a7544 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -391,13 +391,13 @@ def get_subplots(rows=1, columns=1, print_grid=False, **kwargs): Example 1: # stack two subplots vertically - >>> fig = tools.get_subplots(rows=2) - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + fig = tools.get_subplots(rows=2) + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] Example 2: # print out string showing the subplot grid you've put in the layout - >>> fig = tools.get_subplots(rows=3, columns=2, print_grid=True) + fig = tools.get_subplots(rows=3, columns=2, print_grid=True) Keywords arguments with constant defaults: @@ -500,49 +500,52 @@ def make_subplots(rows=1, cols=1, Example 1: # stack two subplots vertically - >>> fig = tools.make_subplots(rows=2, print_grid=True) + fig = tools.make_subplots(rows=2, print_grid=True) + This is the format of your plot grid! [ (1,1) x1,y1 ] [ (2,1) x2,y2 ] - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] Example 2: # subplots with shared x axes - >>> fig = tools.make_subplots(rows=2, shared_xaxes=True, print_grid=True) + fig = tools.make_subplots(rows=2, shared_xaxes=True, print_grid=True) TODO What's the default behavior here?? - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y1')] - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y2')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y1')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y2')] Example 3: # irregular subplot layout (more examples below under 'specs') - >>> fig = tools.make_subplots(rows=2, cols=2, - specs=[[{}, {}], - [{'colspan': 2}, None]], - print_grid=True) + fig = tools.make_subplots(rows=2, cols=2, + specs=[[{}, {}], + [{'colspan': 2}, None]], + print_grid=True) + This is the format of your plot grid! [ (1,1) x1,y1 ] [ (1,2) x2,y2 ] [ (2,1) x3,y3 - ] - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x3', yaxis='y3')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x3', yaxis='y3')] Example 4: # insets - >>> fig = tools.make_subplots(insets=[{'cell': (1,1), 'l': 0.7, 'b': 0.3}], - print_grid=True) + fig = tools.make_subplots(insets=[{'cell': (1,1), 'l': 0.7, 'b': 0.3}], + print_grid=True) + This is the format of your plot grid! [ (1,1) x1,y1 ] With insets: [ x2,y2 ] over [ (1,1) x1,y1 ] - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2])] - >>> fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2])] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] Keywords arguments with constant defaults: From 67672dda3f556b0f7f8076f963eca48d8565a03e Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:28:35 -0500 Subject: [PATCH 065/103] force 'specs' to be list of lists --- plotly/tools.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 72fcf3a7544..9c106245a67 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -670,14 +670,16 @@ def make_subplots(rows=1, cols=1, except KeyError: vertical_spacing = 0.3 / rows - # Sanitize 'specs' + # Sanitize 'specs' (must be a list of lists) + exception_msg = "Keyword argument 'specs' must be a list of lists" try: specs = kwargs['specs'] if not isinstance(specs, list): - raise Exception("Keyword argument 'specs' must be a list") - elif isinstance(specs[0], dict): - # To support one-row specs=[{},{}] - specs = [specs] + raise Exception(exception_msg) + else: + for spec_row in specs: + if not isinstance(spec_row, list): + raise Exception(exception_msg) except KeyError: specs = [[{} for c in range(cols)] From a3117196c3336eca54ab874c97aa1e684ea4b8b6 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:30:12 -0500 Subject: [PATCH 066/103] force items in 'specs' to be either dicts or None --- plotly/tools.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 9c106245a67..f3bd294e004 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -705,6 +705,11 @@ def make_subplots(rows=1, cols=1, # Throw exception if non-valid key / fill in defaults def _check_keys_and_fill(name, arg, defaults): def _checks(item, defaults): + if item is None: + return + if not isinstance(item, dict): + raise Exception("Items in keyword argument '{name}' must be " + "dictionaries or None".format(name=name)) for k in item.keys(): if k not in defaults.keys(): raise Exception("Invalid key '{k}' in keyword " @@ -715,8 +720,7 @@ def _checks(item, defaults): for arg_i in arg: if isinstance(arg_i, list): for arg_ii in arg_i: - if arg_ii is not None: # for specs[i][j] == None - _checks(arg_ii, defaults) + _checks(arg_ii, defaults) elif isinstance(arg_i, dict): _checks(arg_i, defaults) From dd16fa295e6b7ff07cfa01db02e165e51852d222 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:38:06 -0500 Subject: [PATCH 067/103] define START_CELL and build grid using it --- plotly/tools.py | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index f3bd294e004..b55b2b361ed 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -653,6 +653,27 @@ def make_subplots(rows=1, cols=1, if not isinstance(cols, int): raise Exception("Keyword argument 'cols' must be an int") + # Dictionary of things start_cell + START_CELL_all = { + 'bottom-left': { + # 'natural' setup where x & y domains increase monotonically + 'col_dir': 1, + 'row_dir': 1 + }, + 'top-left': { + # 'default' setup visually matching the 'specs' list of lists + 'col_dir': 1, + 'row_dir': -1 + } + # TODO maybe add 'bottom-right' and 'top-right' + } + + # Throw exception for invalid 'start_cell' values + try: + START_CELL = START_CELL_all[start_cell] + except KeyError: + raise Exception("Invalid 'start_cell' value") + # Throw exception if non-valid kwarg is sent VALID_KWARGS = ['horizontal_spacing', 'vertical_spacing', 'specs', 'insets'] @@ -752,12 +773,17 @@ def _checks(item, defaults): width = (1. - horizontal_spacing * (cols - 1)) / cols height = (1. - vertical_spacing * (rows - 1)) / rows - # Build subplot grid (tuple of starting coords for each cell) - grid = [[((width + horizontal_spacing) * column, - (height + vertical_spacing) * row) - for column in range(columns)] - for row in range(rows)] # all we need - + # Built row/col sequence using 'row_dir' and 'col_dir' + col_dir = START_CELL['col_dir'] + col_seq = range(cols)[::col_dir] + row_dir = START_CELL['row_dir'] + row_seq = range(rows)[::row_dir] + + # [grid] Build subplot grid (coord tuple of cell) + grid = [[((width + horizontal_spacing) * c, + (height + vertical_spacing) * r) + for c in col_seq] + for r in row_seq] # Initialize the grid's string representation if print_grid: grid_str = [['' for column in range(columns)] for row in range(rows)] From 254cf139cb1c4a907c1b461dfa2c60c85bd93abf Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:41:06 -0500 Subject: [PATCH 068/103] sub 'grid_str' -> 'grid_ref': - 'grid_ref' stores tuples of axis (x{},y{}) one per grid cell - thinking about using 'grid_ref' for fig._append_trace --- plotly/tools.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index b55b2b361ed..792c3ffb30f 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -784,12 +784,10 @@ def _checks(item, defaults): (height + vertical_spacing) * r) for c in col_seq] for r in row_seq] - # Initialize the grid's string representation - if print_grid: - grid_str = [['' for column in range(columns)] for row in range(rows)] - if insets: - insets_str = ['' for inset in range(len(insets))] + # [grid_ref] Initialize the grid and insets' axis-reference lists + grid_ref = [[None for c in range(cols)] for r in range(rows)] + insets_ref = [None for inset in range(len(insets))] if insets else None fig = dict(layout=graph_objs.Layout()) # init layout object # Function handling logic around 2d axis labels From 46b6c73ae87908746fcd526caf91828c65edef81 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:42:16 -0500 Subject: [PATCH 069/103] fill in a layout obj, not a figure obj directly --- plotly/tools.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 792c3ffb30f..bdd994c709e 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -788,7 +788,8 @@ def _checks(item, defaults): # [grid_ref] Initialize the grid and insets' axis-reference lists grid_ref = [[None for c in range(cols)] for r in range(rows)] insets_ref = [None for inset in range(len(insets))] if insets else None - fig = dict(layout=graph_objs.Layout()) # init layout object + + layout = graph_objs.Layout() # init layout object # Function handling logic around 2d axis labels # Returns 'x{}' | 'y{}' @@ -1021,7 +1022,8 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): grid_str[inset['cell'][0]][inset['cell'][1]]) print('') - return graph_objs.Figure(fig) # forces us to validate what we just did... + fig = graph_objs.Figure(layout=layout) + return fig def get_valid_graph_obj(obj, obj_type=None): From 4a6b7184a502b03af7c8d65178aa8eb8104722a2 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:42:59 -0500 Subject: [PATCH 070/103] remove i, j indices --- plotly/tools.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index bdd994c709e..f8016b88f27 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -880,13 +880,11 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): scene = graph_objs.Scene(domain={'x': x_domain, 'y': y_domain}) fig['layout'][scene_name] = scene - i = j = 0 # subplot grid indices x_cnt = y_cnt = s_cnt = 1 # subplot axis/scene counters # Loop through specs for row, spec_row in enumerate(specs): - j = 0 # start at leftmost grid cell for each spec_row for col, spec in enumerate(spec_row): @@ -955,10 +953,6 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): for c in range(0, spec['colspan']): grid_str[i+spec['rowspan']-1][j+c] = ' ^ ' - j += 1 # move right by one column - - i += 1 # move up by one row - # Loop through insets if insets: for i_inset, inset in enumerate(insets): From 1cb00263a1d44356d1b62a1fac48b174778f2118 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:46:18 -0500 Subject: [PATCH 071/103] improve and generalize algorithm: - loop through specs with r, c indices - compute c_spanned and r_spanned up front - throw exception if c_spanned or r_spanned is too long for grid - add cond around row_dir (for 'start_cell='top-left' | 'bottom-left') - pass layout (not fig) to _add_domain - fill in grid_ref ! --- plotly/tools.py | 56 ++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index f8016b88f27..6c52650858d 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -882,41 +882,52 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): x_cnt = y_cnt = s_cnt = 1 # subplot axis/scene counters - # Loop through specs - for row, spec_row in enumerate(specs): + # Loop through specs -- (r, c) <-> (row, col) + for r, spec_row in enumerate(specs): + for c, spec in enumerate(spec_row): + if spec is None: # skip over None cells + continue - for col, spec in enumerate(spec_row): + c_spanned = c + spec['colspan'] - 1 # get spanned c + r_spanned = r + spec['rowspan'] - 1 # get spanned r - # String representation for empty cells - if spec is None: - if print_grid and grid_str[i][j] == '': - grid_str[i][j] = '{none}' - j += 1 - continue + # Throw exception if 'colspan' | 'rowspan' is too large for grid + if c_spanned >= cols: + raise Exception("Some 'colspan' value is too large for " + "this subplot grid.") + if r_spanned >= rows: + raise Exception("Some 'rowspan' value is too large for " + "this subplot grid.") # Get x domain using grid and colspan - x_s = grid[i][j][0] + spec['l'] - x_e = grid[i][j+(spec['colspan']-1)][0] + width - spec['r'] + x_s = grid[r][c][0] + spec['l'] + x_e = grid[r][c_spanned][0] + width - spec['r'] x_domain = [x_s, x_e] - # Get y domain using grid and rowspan - y_s = grid[i][j][1] + spec['b'] - y_e = grid[i+(spec['rowspan']-1)][j][1] + height - spec['t'] + # Get y domain (dep. on row_dir) using grid & r_spanned + if row_dir > 0: + y_s = grid[r][c][1] + spec['b'] + y_e = grid[r_spanned][c][1] + height - spec['t'] + else: + y_s = grid[r_spanned][c][1] + spec['b'] + y_e = grid[r][c][1] + height - spec['t'] y_domain = [y_s, y_e] if spec['is_3d']: + # Add scene to layout - _add_domain_is_3d(fig, s_cnt, x_domain, y_domain) - if print_grid: - grid_str[i][j] = '[scene{}'.format(s_cnt) + s_label = 'scene{0}'.format(s_cnt) + _add_domain_is_3d(layout, s_label, x_domain, y_domain) + grid_ref[r][c] = (s_label, ) s_cnt += 1 + else: # Get axis label and anchor - x_label = _get_label('x', row, col, x_cnt, shared_xaxes) - y_label = _get_label('y', row, col, y_cnt, shared_yaxes) - x_anchor, y_anchor = _get_anchors(row, col, + x_label = _get_label('x', r, c, x_cnt, shared_xaxes) + y_label = _get_label('y', r, c, y_cnt, shared_yaxes) + x_anchor, y_anchor = _get_anchors(r, c, x_cnt, y_cnt, shared_xaxes, shared_yaxes) @@ -924,17 +935,18 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): # Add a xaxis to layout (N.B anchor == False -> no axis) if x_anchor: x_position = y_domain[0] if x_anchor == 'free' else 0 - _add_domain(fig, 'x', x_label, x_domain, + _add_domain(layout, 'x', x_label, x_domain, x_anchor, x_position) x_cnt += 1 # Add a yaxis to layout (N.B anchor == False -> no axis) if y_anchor: y_position = x_domain[0] if y_anchor == 'free' else 0 - _add_domain(fig, 'y', y_label, y_domain, + _add_domain(layout, 'y', y_label, y_domain, y_anchor, y_position) y_cnt += 1 + grid_ref[r][c] = (x_label, y_label) # fill in ref if print_grid: grid_str[i][j] = '[{},{}'.format(x_label, y_label) From 964daffad46c23ac6520269f363c66d2f65a5ae8 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:47:07 -0500 Subject: [PATCH 072/103] rm grid_str code (taken care outside specs loop now) --- plotly/tools.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 6c52650858d..7a57e7e42b6 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -947,23 +947,6 @@ def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): y_cnt += 1 grid_ref[r][c] = (x_label, y_label) # fill in ref - if print_grid: - grid_str[i][j] = '[{},{}'.format(x_label, y_label) - - # String representation for spanned cells - # TODO more general spacing over spanned cells - if print_grid: - if spec['colspan'] > 1: - for c in range(1, spec['colspan']-1): - grid_str[i][j+c] = ' ' - grid_str[i][j+spec['colspan']-1] = ' ]' - else: - grid_str[i][j] += ']' - if spec['rowspan'] > 1: - for r in range(1, spec['rowspan']-1): - grid_str[i+r][j] = ' ' - for c in range(0, spec['colspan']): - grid_str[i+spec['rowspan']-1][j+c] = ' ^ ' # Loop through insets if insets: From 7463f60687735be6c8b0f1422a1d2313f279a7fc Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:48:38 -0500 Subject: [PATCH 073/103] update (r, c) convention: - make start cell corresp to (1, 1) --- plotly/tools.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 7a57e7e42b6..ff817c8c503 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -760,7 +760,7 @@ def _checks(item, defaults): # Default inset key-values if insets: INSET_defaults = dict( - cell=(0, 0), + cell=(1, 1), is_3d=False, l=0.0, w='to_end', @@ -793,44 +793,44 @@ def _checks(item, defaults): # Function handling logic around 2d axis labels # Returns 'x{}' | 'y{}' - def _get_label(x_or_y, row, col, cnt, shared_axes): + def _get_label(x_or_y, r, c, cnt, shared_axes): # Default label (given strictly by cnt) label = "{x_or_y}{cnt}".format(x_or_y=x_or_y, cnt=cnt) if isinstance(shared_axes, bool): if shared_axes: if x_or_y == 'x': - label = "{x_or_y}{c}".format(x_or_y=x_or_y, c=col+1) + label = "{x_or_y}{c}".format(x_or_y=x_or_y, c=c+1) if x_or_y == 'y': - label = "{x_or_y}{r}".format(x_or_y=x_or_y, r=row+1) + label = "{x_or_y}{r}".format(x_or_y=x_or_y, r=r+1) if isinstance(shared_axes, list): if isinstance(shared_axes[0], tuple): shared_axes = [shared_axes] # TODO put this elsewhere for shared_axis in shared_axes: - if (row, col) in shared_axis: + if (r+1, c+1) in shared_axis: label = { - 'x': "x{0}".format(shared_axis[0][1]+1), - 'y': "y{0}".format(shared_axis[0][0]+1) + 'x': "x{0}".format(shared_axis[0][1]), + 'y': "y{0}".format(shared_axis[0][0]) }[x_or_y] return label # Function handling logic around 2d axis anchors # Return 'x{}' | 'y{}' | 'free' | False - def _get_anchors(row, col, x_cnt, y_cnt, shared_xaxes, shared_yaxes): + def _get_anchors(r, c, x_cnt, y_cnt, shared_xaxes, shared_yaxes): # Default anchors (give strictly by cnt) x_anchor = "y{y_cnt}".format(y_cnt=y_cnt) y_anchor = "x{x_cnt}".format(x_cnt=x_cnt) if isinstance(shared_xaxes, bool): if shared_xaxes: - if row == 0: - x_anchor = "y{col_cnt}".format(col_cnt=col+1) + if r == 0: + x_anchor = "y{col_cnt}".format(col_cnt=c+1) else: x_anchor = False y_anchor = 'free' - if shared_yaxes and col > 0: # TODO covers all cases? + if shared_yaxes and c > 0: # TODO covers all cases? y_anchor = False return x_anchor, y_anchor @@ -838,18 +838,18 @@ def _get_anchors(row, col, x_cnt, y_cnt, shared_xaxes, shared_yaxes): if isinstance(shared_xaxes[0], tuple): shared_xaxes = [shared_xaxes] # TODO put this elsewhere for shared_xaxis in shared_xaxes: - if (row, col) in shared_xaxis[1:]: + if (r+1, c+1) in shared_xaxis[1:]: x_anchor = False y_anchor = 'free' # TODO covers all cases? if isinstance(shared_yaxes, bool): if shared_yaxes: - if col == 0: - y_anchor = "x{row_cnt}".format(row_cnt=row+1) + if c == 0: + y_anchor = "x{row_cnt}".format(row_cnt=r+1) else: y_anchor = False x_anchor = 'free' - if shared_xaxes and row > 0: # TODO covers all cases? + if shared_xaxes and r > 0: # TODO covers all cases? x_anchor = False return x_anchor, y_anchor @@ -857,7 +857,7 @@ def _get_anchors(row, col, x_cnt, y_cnt, shared_xaxes, shared_yaxes): if isinstance(shared_yaxes[0], tuple): shared_yaxes = [shared_yaxes] # TODO put this elsewhere for shared_yaxis in shared_yaxes: - if (row, col) in shared_yaxis[1:]: + if (r+1, c+1) in shared_yaxis[1:]: y_anchor = False x_anchor = 'free' # TODO covers all cases? From f0eea1544af0fecc705aabb97c84e6c85022cf37 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:49:06 -0500 Subject: [PATCH 074/103] update _add_domain (accepts layout not fig) --- plotly/tools.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index ff817c8c503..6f99f88493d 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -863,8 +863,8 @@ def _get_anchors(r, c, x_cnt, y_cnt, shared_xaxes, shared_yaxes): return x_anchor, y_anchor - # Function pasting x/y domains in fig object (2d case) - def _add_domain(fig, x_or_y, label, domain, anchor, position): + # Function pasting x/y domains in layout object (2d case) + def _add_domain(layout, x_or_y, label, domain, anchor, position): name = label[0] + 'axis' + label[1:] graph_obj = '{X_or_Y}Axis'.format(X_or_Y=x_or_y.upper()) axis = getattr(graph_objs, graph_obj)(domain=domain) @@ -872,13 +872,12 @@ def _add_domain(fig, x_or_y, label, domain, anchor, position): axis['anchor'] = anchor if position: # N.B. No need to add position == 0 to axis axis['position'] = position - fig['layout'][name] = axis + layout[name] = axis - # Function pasting x/y domains in fig object (3d case) - def _add_domain_is_3d(fig, s_cnt, x_domain, y_domain): - scene_name = "scene{s_cnt}".format(s_cnt=s_cnt) + # Function pasting x/y domains in layout object (3d case) + def _add_domain_is_3d(layout, s_label, x_domain, y_domain): scene = graph_objs.Scene(domain={'x': x_domain, 'y': y_domain}) - fig['layout'][scene_name] = scene + layout[s_label] = scene x_cnt = y_cnt = s_cnt = 1 # subplot axis/scene counters From 95cded879ba880e31d4039bc54a0ac8e723b37c4 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:50:08 -0500 Subject: [PATCH 075/103] update insets loop: - update start cell convention - i,j -> r.c - fill in insets_ref ! --- plotly/tools.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 6f99f88493d..32fdea0b8f5 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -951,50 +951,51 @@ def _add_domain_is_3d(layout, s_label, x_domain, y_domain): if insets: for i_inset, inset in enumerate(insets): - i = inset['cell'][0] - j = inset['cell'][1] + r = inset['cell'][0] - 1 + c = inset['cell'][1] - 1 # Get inset x domain using grid - x_s = grid[i][j][0] + inset['l'] * width + x_s = grid[r][c][0] + inset['l'] * width if inset['w'] == 'to_end': - x_e = grid[i][j][0] + width + x_e = grid[r][c][0] + width else: x_e = x_s + inset['w'] * width x_domain = [x_s, x_e] # Get inset y domain using grid - y_s = grid[i][j][1] + inset['b'] * height + y_s = grid[r][c][1] + inset['b'] * height if inset['h'] == 'to_end': - y_e = grid[i][j][1] + height + y_e = grid[r][c][1] + height else: y_e = y_s + inset['h'] * height y_domain = [y_s, y_e] if inset['is_3d']: + # Add scene to layout - _add_domain_is_3d(fig, s_cnt, x_domain, y_domain) - if print_grid: - insets_str[i_inset] = '[scene{}]'.format(s_cnt) + s_label = 'scene{0}'.format(s_cnt) + _add_domain_is_3d(layout, s_label, x_domain, y_domain) + insets_ref[i_inset] = (s_label, ) s_cnt += 1 + else: # Get axis label and anchor x_label = _get_label('x', False, False, x_cnt, False) y_label = _get_label('y', False, False, y_cnt, False) - x_anchor, y_anchor = _get_anchors(row, col, + x_anchor, y_anchor = _get_anchors(r, c, x_cnt, y_cnt, False, False) # Add a xaxis to layout (N.B insets always have anchors) - _add_domain(fig, 'x', x_label, x_domain, x_anchor, False) + _add_domain(layout, 'x', x_label, x_domain, x_anchor, False) x_cnt += 1 # Add a yayis to layout (N.B insets always have anchors) - _add_domain(fig, 'y', y_label, y_domain, y_anchor, False) + _add_domain(layout, 'y', y_label, y_domain, y_anchor, False) y_cnt += 1 - if print_grid: - insets_str[i_inset] = '[{},{}]'.format(x_label, y_label) + insets_ref[i_inset] = (x_label, y_label) # fill in ref if print_grid: print("This is the format of your plot grid!") From 7b0e5d16af31f21552dcdf0b9a4042b3da9a130e Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:50:34 -0500 Subject: [PATCH 076/103] improve 'grid_str' code --- plotly/tools.py | 88 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 32fdea0b8f5..07100029149 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -997,19 +997,83 @@ def _add_domain_is_3d(layout, s_label, x_domain, y_domain): insets_ref[i_inset] = (x_label, y_label) # fill in ref + # [grid_str] Set the grid's string representation + sp = " " # space between cell + s_str = "[ " # cell start string + e_str = " ]" # cell end string + colspan_str = ' -' # colspan string + rowspan_str = ' |' # rowspan string + empty_str = ' (empty) ' # empty cell string + + # Init grid_str with intro message + grid_str = "This is the format of your plot grid!\n" + + # Init tmp list of lists of strings (sorta like 'grid_ref' but w/ strings) + _tmp = [['' for c in range(cols)] for r in range(rows)] + + # Define cell string as function of (r, c) and grid_ref + def _get_cell_str(r, c, ref): + return '({r},{c}) {ref}'.format(r=r+1, c=c+1, ref=','.join(ref)) + + # Find max len of _cell_str, add define a padding function + cell_len = max([len(_get_cell_str(r, c, ref)) + for r, row_ref in enumerate(grid_ref) + for c, ref in enumerate(row_ref) + if ref]) + len(s_str) + len(e_str) + + def _pad(s, cell_len=cell_len): + return ' ' * (cell_len - len(s)) + + # Loop through specs, fill in _tmp + for r, spec_row in enumerate(specs): + for c, spec in enumerate(spec_row): + + ref = grid_ref[r][c] + if ref is None: + if _tmp[r][c] == '': + _tmp[r][c] = empty_str + _pad(empty_str) + continue + + cell_str = s_str + _get_cell_str(r, c, ref) + + if spec['colspan'] > 1: + for cc in range(1, spec['colspan']-1): + _tmp[r][c+cc] = colspan_str + _pad(colspan_str) + _tmp[r][c+spec['colspan']-1] = ( + colspan_str + _pad(colspan_str + e_str)) + e_str + else: + cell_str += e_str + + if spec['rowspan'] > 1: + for rr in range(1, spec['rowspan']-1): + _tmp[r+rr][c] = rowspan_str + _pad(rowspan_str) + for cc in range(spec['colspan']): + _tmp[r+spec['rowspan']-1][c+cc] = ( + rowspan_str + _pad(rowspan_str)) + + _tmp[r][c] = cell_str + _pad(cell_str) + + # Append grid_str using data from _tmp in the correct order + for r in row_seq[::-1]: + grid_str += sp.join(_tmp[r]) + '\n' + + # Append grid_str to include insets info + if insets: + grid_str += "\nWith insets:\n" + for i_inset, inset in enumerate(insets): + + r = inset['cell'][0] - 1 + c = inset['cell'][1] - 1 + ref = grid_ref[r][c] + + grid_str += ( + s_str + ','.join(insets_ref[i_inset]) + e_str + + ' over ' + + s_str + _get_cell_str(r, c, ref) + e_str + '\n' + ) + if print_grid: - print("This is the format of your plot grid!") - grid_string = "" - for grid_str_row in grid_str: - grid_string = " ".join(grid_str_row) + '\n' + grid_string - print(grid_string) - if insets: - print("With insets:") - for i_inset, inset in enumerate(insets): - print( - insets_str[i_inset] + ' over ' + - grid_str[inset['cell'][0]][inset['cell'][1]]) - print('') + print(grid_str) fig = graph_objs.Figure(layout=layout) return fig From 3f7572a3134aa91fa7eb7bd71975d7b070df4ffe Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:50:49 -0500 Subject: [PATCH 077/103] pep8 and add TODO --- plotly/tools.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 07100029149..cc1e9a5a6a3 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -492,9 +492,9 @@ def get_subplots(rows=1, columns=1, print_grid=False, **kwargs): def make_subplots(rows=1, cols=1, - shared_xaxes=False, shared_yaxes=False, - start_cell='bottom-left', print_grid=False, - **kwargs): + shared_xaxes=False, shared_yaxes=False, + start_cell='top-left', print_grid=False, + **kwargs): """Return an instance of plotly.graph_objs.Figure with the subplots domain set in 'layout'. @@ -754,6 +754,7 @@ def _checks(item, defaults): r=0.0, b=0.0, t=0.0 + # TODO add support for 'w' and 'h' ) _check_keys_and_fill('specs', specs, SPEC_defaults) From e9c2e01d5559379b480a8e57fd498b3608a34558 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 13:52:24 -0500 Subject: [PATCH 078/103] test galore !!! - No shared axes with start_cell='top-left' yet (need feedback) --- .../test_tools/test_make_subplots.py | 924 ++++++++++++------ 1 file changed, 599 insertions(+), 325 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_make_subplots.py b/plotly/tests/test_core/test_tools/test_make_subplots.py index 3ba9109ff4f..8cb793f7b70 100644 --- a/plotly/tests/test_core/test_tools/test_make_subplots.py +++ b/plotly/tests/test_core/test_tools/test_make_subplots.py @@ -3,6 +3,81 @@ import plotly.tools as tls from nose.tools import raises + +@raises(Exception) +def test_non_integer_rows(): + fig = tls.make_subplots(rows=2.1) + +@raises(Exception) +def test_non_integer_columns(): + fig = tls.make_subplots(columns=2/3) + +@raises(Exception) +def test_wrong_kwarg(): + fig = tls.make_subplots(stuff='no gonna work') + +@raises(Exception) +def test_non_integer_rows(): + fig = tls.make_subplots(rows=2.1) + +@raises(Exception) +def test_non_integer_columns(): + fig = tls.make_subplots(columns=2/3) + +@raises(Exception) +def test_wrong_kwarg(): + fig = tls.make_subplots(stuff='no gonna work') + +@raises(Exception) +def test_start_cell_wrong_values(): + fig = tls.make_subplots(rows=2, columns=2, start_cell='not gonna work') + +@raises(Exception) +def test_specs_wrong_type(): + fig = tls.make_subplots(specs="not going to work") + +@raises(Exception) +def test_specs_wrong_inner_type(): + fig = tls.make_subplots(specs=[{}]) + +@raises(Exception) +def test_specs_wrong_item_type(): + fig = tls.make_subplots(specs=[[('not', 'going to work')]]) + +@raises(Exception) +def test_specs_wrong_item_key(): + fig = tls.make_subplots(specs=[{'not': "going to work"}]) + +@raises(Exception) +def test_specs_underspecified(): + fig = tls.make_subplots(rows=2, specs=[{}]) + fig = tls.make_subplots(rows=2, columns=2, specs=[[{}, {}], [{}]]) + +@raises(Exception) +def test_specs_overspecified(): + fig = tls.make_subplots(rows=2, specs=[[{}], [{}], [{}]]) + fig = tls.make_subplots(columns=2, specs=[{}, {}, {}]) + +@raises(Exception) +def test_specs_colspan_too_big(): + fig = tls.make_subplots(cols=3, + specs=[[{}, None, {'colspan': 2}]]) + +@raises(Exception) +def test_specs_rowspan_too_big(): + fig = tls.make_subplots(rows=3, + specs=[[{}], + [None], + [{'rowspan': 2}]]) + +@raises(Exception) +def test_subplot_insets_wrong_type(): + fig = tls.make_subplots(insets="not going to work") + +@raises(Exception) +def test_subplot_insets_wrong_item(): + fig = tls.make_subplots(insets=[{'not': "going to work"}]) + def test_get_single_plot(): expected = Figure( data=Data(), @@ -17,9 +92,33 @@ def test_get_single_plot(): ) ) ) - assert tls.get_subplots() == expected + assert tls.make_subplots() == expected def test_two_row(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 1.0], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.0, 1.0], + anchor='y2' + ), + yaxis1=YAxis( + domain=[0.575, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.425], + anchor='x2' + ) + ) + ) + assert tls.make_subplots(rows=2) == expected + +def test_two_row_bottom_left(): expected = Figure( data=Data(), layout=Layout( @@ -41,8 +140,9 @@ def test_two_row(): ) ) ) - assert tls.get_subplots(2) == expected + fig = tls.make_subplots(rows=2, start_cell='bottom-left') + assert fig == expected def test_two_column(): expected = Figure( @@ -66,601 +166,581 @@ def test_two_column(): ) ) ) - - assert tls.get_subplots(1, 2) == expected + assert tls.make_subplots(cols=2) == expected def test_a_lot(): expected = Figure( data=Data(), layout=Layout( xaxis1=XAxis( - domain=[0.0, 0.05714285714285713], + domain=[0.0, 0.1183673469387755], anchor='y1' ), xaxis10=XAxis( - domain=[0.3142857142857143, 0.3714285714285714], + domain=[0.29387755102040813, 0.4122448979591836], anchor='y10' ), xaxis11=XAxis( - domain=[0.4714285714285714, 0.5285714285714286], + domain=[0.4408163265306122, 0.5591836734693877], anchor='y11' ), xaxis12=XAxis( - domain=[0.6285714285714286, 0.6857142857142857], + domain=[0.5877551020408163, 0.7061224489795918], anchor='y12' ), xaxis13=XAxis( - domain=[0.7857142857142857, 0.8428571428571429], + domain=[0.7346938775510203, 0.8530612244897958], anchor='y13' ), xaxis14=XAxis( - domain=[0.9428571428571428, 1.0], + domain=[0.8816326530612244, 0.9999999999999999], anchor='y14' ), xaxis15=XAxis( - domain=[0.0, 0.05714285714285713], + domain=[0.0, 0.1183673469387755], anchor='y15' ), xaxis16=XAxis( - domain=[0.15714285714285714, 0.21428571428571427], + domain=[0.14693877551020407, 0.26530612244897955], anchor='y16' ), xaxis17=XAxis( - domain=[0.3142857142857143, 0.3714285714285714], + domain=[0.29387755102040813, 0.4122448979591836], anchor='y17' ), xaxis18=XAxis( - domain=[0.4714285714285714, 0.5285714285714286], + domain=[0.4408163265306122, 0.5591836734693877], anchor='y18' ), xaxis19=XAxis( - domain=[0.6285714285714286, 0.6857142857142857], + domain=[0.5877551020408163, 0.7061224489795918], anchor='y19' ), xaxis2=XAxis( - domain=[0.15714285714285714, 0.21428571428571427], + domain=[0.14693877551020407, 0.26530612244897955], anchor='y2' ), xaxis20=XAxis( - domain=[0.7857142857142857, 0.8428571428571429], + domain=[0.7346938775510203, 0.8530612244897958], anchor='y20' ), xaxis21=XAxis( - domain=[0.9428571428571428, 1.0], + domain=[0.8816326530612244, 0.9999999999999999], anchor='y21' ), xaxis22=XAxis( - domain=[0.0, 0.05714285714285713], + domain=[0.0, 0.1183673469387755], anchor='y22' ), xaxis23=XAxis( - domain=[0.15714285714285714, 0.21428571428571427], + domain=[0.14693877551020407, 0.26530612244897955], anchor='y23' ), xaxis24=XAxis( - domain=[0.3142857142857143, 0.3714285714285714], + domain=[0.29387755102040813, 0.4122448979591836], anchor='y24' ), xaxis25=XAxis( - domain=[0.4714285714285714, 0.5285714285714286], + domain=[0.4408163265306122, 0.5591836734693877], anchor='y25' ), xaxis26=XAxis( - domain=[0.6285714285714286, 0.6857142857142857], + domain=[0.5877551020408163, 0.7061224489795918], anchor='y26' ), xaxis27=XAxis( - domain=[0.7857142857142857, 0.8428571428571429], + domain=[0.7346938775510203, 0.8530612244897958], anchor='y27' ), xaxis28=XAxis( - domain=[0.9428571428571428, 1.0], + domain=[0.8816326530612244, 0.9999999999999999], anchor='y28' ), xaxis3=XAxis( - domain=[0.3142857142857143, 0.3714285714285714], + domain=[0.29387755102040813, 0.4122448979591836], anchor='y3' ), xaxis4=XAxis( - domain=[0.4714285714285714, 0.5285714285714286], + domain=[0.4408163265306122, 0.5591836734693877], anchor='y4' ), xaxis5=XAxis( - domain=[0.6285714285714286, 0.6857142857142857], + domain=[0.5877551020408163, 0.7061224489795918], anchor='y5' ), xaxis6=XAxis( - domain=[0.7857142857142857, 0.8428571428571429], + domain=[0.7346938775510203, 0.8530612244897958], anchor='y6' ), xaxis7=XAxis( - domain=[0.9428571428571428, 1.0], + domain=[0.8816326530612244, 0.9999999999999999], anchor='y7' ), xaxis8=XAxis( - domain=[0.0, 0.05714285714285713], + domain=[0.0, 0.1183673469387755], anchor='y8' ), xaxis9=XAxis( - domain=[0.15714285714285714, 0.21428571428571427], + domain=[0.14693877551020407, 0.26530612244897955], anchor='y9' ), yaxis1=YAxis( - domain=[0.0, 0.1375], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x1' ), yaxis10=YAxis( - domain=[0.2875, 0.425], + domain=[0.5375, 0.73125], anchor='x10' ), yaxis11=YAxis( - domain=[0.2875, 0.425], + domain=[0.5375, 0.73125], anchor='x11' ), yaxis12=YAxis( - domain=[0.2875, 0.425], + domain=[0.5375, 0.73125], anchor='x12' ), yaxis13=YAxis( - domain=[0.2875, 0.425], + domain=[0.5375, 0.73125], anchor='x13' ), yaxis14=YAxis( - domain=[0.2875, 0.425], + domain=[0.5375, 0.73125], anchor='x14' ), yaxis15=YAxis( - domain=[0.575, 0.7124999999999999], + domain=[0.26875, 0.4625], anchor='x15' ), yaxis16=YAxis( - domain=[0.575, 0.7124999999999999], + domain=[0.26875, 0.4625], anchor='x16' ), yaxis17=YAxis( - domain=[0.575, 0.7124999999999999], + domain=[0.26875, 0.4625], anchor='x17' ), yaxis18=YAxis( - domain=[0.575, 0.7124999999999999], + domain=[0.26875, 0.4625], anchor='x18' ), yaxis19=YAxis( - domain=[0.575, 0.7124999999999999], + domain=[0.26875, 0.4625], anchor='x19' ), yaxis2=YAxis( - domain=[0.0, 0.1375], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x2' ), yaxis20=YAxis( - domain=[0.575, 0.7124999999999999], + domain=[0.26875, 0.4625], anchor='x20' ), yaxis21=YAxis( - domain=[0.575, 0.7124999999999999], + domain=[0.26875, 0.4625], anchor='x21' ), yaxis22=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.0, 0.19375], anchor='x22' ), yaxis23=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.0, 0.19375], anchor='x23' ), yaxis24=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.0, 0.19375], anchor='x24' ), yaxis25=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.0, 0.19375], anchor='x25' ), yaxis26=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.0, 0.19375], anchor='x26' ), yaxis27=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.0, 0.19375], anchor='x27' ), yaxis28=YAxis( - domain=[0.8624999999999999, 1.0], + domain=[0.0, 0.19375], anchor='x28' ), yaxis3=YAxis( - domain=[0.0, 0.1375], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x3' ), yaxis4=YAxis( - domain=[0.0, 0.1375], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x4' ), yaxis5=YAxis( - domain=[0.0, 0.1375], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x5' ), yaxis6=YAxis( - domain=[0.0, 0.1375], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x6' ), yaxis7=YAxis( - domain=[0.0, 0.1375], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x7' ), yaxis8=YAxis( - domain=[0.2875, 0.425], + domain=[0.5375, 0.73125], anchor='x8' ), yaxis9=YAxis( - domain=[0.2875, 0.425], + domain=[0.5375, 0.73125], anchor='x9' ) ) ) - fig = tls.get_subplots(4, 7, - horizontal_spacing=0.1, - vertical_spacing=0.15) - - assert fig == expected - -def test_spacing(): - expected = Figure( - data=Data(), - layout=Layout( - xaxis1=XAxis( - domain=[0.0, 0.3], - anchor='y1' - ), - xaxis2=XAxis( - domain=[0.35, 0.6499999999999999], - anchor='y2' - ), - xaxis3=XAxis( - domain=[0.7, 1.0], - anchor='y3' - ), - xaxis4=XAxis( - domain=[0.0, 0.3], - anchor='y4' - ), - xaxis5=XAxis( - domain=[0.35, 0.6499999999999999], - anchor='y5' - ), - xaxis6=XAxis( - domain=[0.7, 1.0], - anchor='y6' - ), - yaxis1=YAxis( - domain=[0.0, 0.45], - anchor='x1' - ), - yaxis2=YAxis( - domain=[0.0, 0.45], - anchor='x2' - ), - yaxis3=YAxis( - domain=[0.0, 0.45], - anchor='x3' - ), - yaxis4=YAxis( - domain=[0.55, 1.0], - anchor='x4' - ), - yaxis5=YAxis( - domain=[0.55, 1.0], - anchor='x5' - ), - yaxis6=YAxis( - domain=[0.55, 1.0], - anchor='x6' - ) - ) - ) - - fig = tls.get_subplots(2, 3, - horizontal_spacing=.05, - vertical_spacing=.1) - + fig = tls.make_subplots(rows=4, cols=7) assert fig == expected -@raises(Exception) -def test_non_integer_rows(): - fig = tls.get_subplots(rows=2.1) - -@raises(Exception) -def test_non_integer_columns(): - fig = tls.get_subplots(columns=2/3) - -@raises(Exception) -def test_wrong_kwarg(): - fig = tls.get_subplots(stuff='no gonna work') - -def test_default_spacing(): +def test_a_lot_bottom_left(): expected = Figure( data=Data(), layout=Layout( xaxis1=XAxis( - domain=[0.0, 0.16799999999999998], + domain=[0.0, 0.1183673469387755], anchor='y1' ), xaxis10=XAxis( - domain=[0.832, 1.0], + domain=[0.29387755102040813, 0.4122448979591836], anchor='y10' ), xaxis11=XAxis( - domain=[0.0, 0.16799999999999998], + domain=[0.4408163265306122, 0.5591836734693877], anchor='y11' ), xaxis12=XAxis( - domain=[0.208, 0.376], + domain=[0.5877551020408163, 0.7061224489795918], anchor='y12' ), xaxis13=XAxis( - domain=[0.416, 0.584], + domain=[0.7346938775510203, 0.8530612244897958], anchor='y13' ), xaxis14=XAxis( - domain=[0.624, 0.792], + domain=[0.8816326530612244, 0.9999999999999999], anchor='y14' ), xaxis15=XAxis( - domain=[0.832, 1.0], + domain=[0.0, 0.1183673469387755], anchor='y15' ), xaxis16=XAxis( - domain=[0.0, 0.16799999999999998], + domain=[0.14693877551020407, 0.26530612244897955], anchor='y16' ), xaxis17=XAxis( - domain=[0.208, 0.376], + domain=[0.29387755102040813, 0.4122448979591836], anchor='y17' ), xaxis18=XAxis( - domain=[0.416, 0.584], + domain=[0.4408163265306122, 0.5591836734693877], anchor='y18' ), xaxis19=XAxis( - domain=[0.624, 0.792], + domain=[0.5877551020408163, 0.7061224489795918], anchor='y19' ), xaxis2=XAxis( - domain=[0.208, 0.376], + domain=[0.14693877551020407, 0.26530612244897955], anchor='y2' ), xaxis20=XAxis( - domain=[0.832, 1.0], + domain=[0.7346938775510203, 0.8530612244897958], anchor='y20' ), xaxis21=XAxis( - domain=[0.0, 0.16799999999999998], + domain=[0.8816326530612244, 0.9999999999999999], anchor='y21' ), xaxis22=XAxis( - domain=[0.208, 0.376], + domain=[0.0, 0.1183673469387755], anchor='y22' ), xaxis23=XAxis( - domain=[0.416, 0.584], + domain=[0.14693877551020407, 0.26530612244897955], anchor='y23' ), xaxis24=XAxis( - domain=[0.624, 0.792], + domain=[0.29387755102040813, 0.4122448979591836], anchor='y24' ), xaxis25=XAxis( - domain=[0.832, 1.0], + domain=[0.4408163265306122, 0.5591836734693877], anchor='y25' ), xaxis26=XAxis( - domain=[0.0, 0.16799999999999998], + domain=[0.5877551020408163, 0.7061224489795918], anchor='y26' ), xaxis27=XAxis( - domain=[0.208, 0.376], + domain=[0.7346938775510203, 0.8530612244897958], anchor='y27' ), xaxis28=XAxis( - domain=[0.416, 0.584], + domain=[0.8816326530612244, 0.9999999999999999], anchor='y28' ), - xaxis29=XAxis( - domain=[0.624, 0.792], - anchor='y29' - ), xaxis3=XAxis( - domain=[0.416, 0.584], + domain=[0.29387755102040813, 0.4122448979591836], anchor='y3' ), - xaxis30=XAxis( - domain=[0.832, 1.0], - anchor='y30' - ), xaxis4=XAxis( - domain=[0.624, 0.792], + domain=[0.4408163265306122, 0.5591836734693877], anchor='y4' ), xaxis5=XAxis( - domain=[0.832, 1.0], + domain=[0.5877551020408163, 0.7061224489795918], anchor='y5' ), xaxis6=XAxis( - domain=[0.0, 0.16799999999999998], + domain=[0.7346938775510203, 0.8530612244897958], anchor='y6' ), xaxis7=XAxis( - domain=[0.208, 0.376], + domain=[0.8816326530612244, 0.9999999999999999], anchor='y7' ), xaxis8=XAxis( - domain=[0.416, 0.584], + domain=[0.0, 0.1183673469387755], anchor='y8' ), xaxis9=XAxis( - domain=[0.624, 0.792], + domain=[0.14693877551020407, 0.26530612244897955], anchor='y9' ), yaxis1=YAxis( - domain=[0.0, 0.125], + domain=[0.0, 0.19375], anchor='x1' ), yaxis10=YAxis( - domain=[0.175, 0.3], + domain=[0.26875, 0.4625], anchor='x10' ), yaxis11=YAxis( - domain=[0.35, 0.475], + domain=[0.26875, 0.4625], anchor='x11' ), yaxis12=YAxis( - domain=[0.35, 0.475], + domain=[0.26875, 0.4625], anchor='x12' ), yaxis13=YAxis( - domain=[0.35, 0.475], + domain=[0.26875, 0.4625], anchor='x13' ), yaxis14=YAxis( - domain=[0.35, 0.475], + domain=[0.26875, 0.4625], anchor='x14' ), yaxis15=YAxis( - domain=[0.35, 0.475], + domain=[0.5375, 0.73125], anchor='x15' ), yaxis16=YAxis( - domain=[0.5249999999999999, 0.6499999999999999], + domain=[0.5375, 0.73125], anchor='x16' ), yaxis17=YAxis( - domain=[0.5249999999999999, 0.6499999999999999], + domain=[0.5375, 0.73125], anchor='x17' ), yaxis18=YAxis( - domain=[0.5249999999999999, 0.6499999999999999], + domain=[0.5375, 0.73125], anchor='x18' ), yaxis19=YAxis( - domain=[0.5249999999999999, 0.6499999999999999], + domain=[0.5375, 0.73125], anchor='x19' ), yaxis2=YAxis( - domain=[0.0, 0.125], + domain=[0.0, 0.19375], anchor='x2' ), yaxis20=YAxis( - domain=[0.5249999999999999, 0.6499999999999999], + domain=[0.5375, 0.73125], anchor='x20' ), yaxis21=YAxis( - domain=[0.7, 0.825], + domain=[0.5375, 0.73125], anchor='x21' ), yaxis22=YAxis( - domain=[0.7, 0.825], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x22' ), yaxis23=YAxis( - domain=[0.7, 0.825], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x23' ), yaxis24=YAxis( - domain=[0.7, 0.825], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x24' ), yaxis25=YAxis( - domain=[0.7, 0.825], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x25' ), yaxis26=YAxis( - domain=[0.875, 1.0], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x26' ), yaxis27=YAxis( - domain=[0.875, 1.0], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x27' ), yaxis28=YAxis( - domain=[0.875, 1.0], + domain=[0.8062499999999999, 0.9999999999999999], anchor='x28' ), - yaxis29=YAxis( - domain=[0.875, 1.0], - anchor='x29' - ), yaxis3=YAxis( - domain=[0.0, 0.125], + domain=[0.0, 0.19375], anchor='x3' ), - yaxis30=YAxis( - domain=[0.875, 1.0], - anchor='x30' - ), yaxis4=YAxis( - domain=[0.0, 0.125], + domain=[0.0, 0.19375], anchor='x4' ), yaxis5=YAxis( - domain=[0.0, 0.125], + domain=[0.0, 0.19375], anchor='x5' ), yaxis6=YAxis( - domain=[0.175, 0.3], + domain=[0.0, 0.19375], anchor='x6' ), yaxis7=YAxis( - domain=[0.175, 0.3], + domain=[0.0, 0.19375], anchor='x7' ), yaxis8=YAxis( - domain=[0.175, 0.3], + domain=[0.26875, 0.4625], anchor='x8' ), yaxis9=YAxis( - domain=[0.175, 0.3], + domain=[0.26875, 0.4625], anchor='x9' ) ) ) - fig = tls.get_subplots(rows=6, columns=5) - + fig = tls.make_subplots(rows=4, cols=7, start_cell='bottom-left') assert fig == expected -@raises(Exception) -def test_subplot_specs_wrong_type(): - fig = tls.get_subplots(specs="not going to work") - -@raises(Exception) -def test_subplot_specs_wrong_inner_type(): - fig = tls.get_subplots(specs=[{}]) - -@raises(Exception) -def test_subplot_specs_wrong_item_type(): - fig = tls.get_subplots(specs=[[('not', 'going to work')]]) - -@raises(Exception) -def test_subplot_specs_wrong_item_key(): - fig = tls.get_subplots(specs=[{'not': "going to work"}]) +def test_spacing(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.3], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35, 0.6499999999999999], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.0, 0.3], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.35, 0.6499999999999999], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.7, 1.0], + anchor='y6' + ), + yaxis1=YAxis( + domain=[0.55, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.55, 1.0], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.55, 1.0], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.0, 0.45], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.0, 0.45], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.0, 0.45], + anchor='x6' + ) + ) + ) -@raises(Exception) -def test_subplot_specs_underspecified(): - fig = tls.get_subplots(rows=2, specs=[{}]) - fig = tls.get_subplots(rows=2, columns=2, specs=[[{}, {}], [{}]]) + fig = tls.make_subplots(rows=2, cols=3, + horizontal_spacing=.05, + vertical_spacing=.1) + assert fig == expected -# @raises(Exception) -# def test_subplot_specs_underspecified2(): - +def test_specs(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y4' + ), + yaxis1=YAxis( + domain=[0.575, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.425], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.425], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.0, 0.425], + anchor='x4' + ) + ) + ) -@raises(Exception) -def test_subplot_specs_overspecified(): - fig = tls.get_subplots(rows=2, specs=[[{}], [{}], [{}]]) - fig = tls.get_subplots(columns=2, specs=[{}, {}, {}]) + fig = tls.make_subplots(rows=2, cols=3, + specs=[[{}, None, None], + [{}, {}, {}]]) + assert fig == expected -def test_subplot_specs(): +def test_specs_bottom_left(): expected = Figure( data=Data(), layout=Layout( @@ -699,13 +779,13 @@ def test_subplot_specs(): ) ) - fig = tls.get_subplots(rows=2, columns=3, - specs=[[{}, None, None], - [{}, {}, {}]]) - + fig = tls.make_subplots(rows=2, cols=3, + specs=[[{}, None, None], + [{}, {}, {}]], + start_cell='bottom-left') assert fig == expected -def test_subplot_specs_colspan(): +def test_specs_colspan(): expected = Figure( data=Data(), layout=Layout( @@ -730,7 +810,7 @@ def test_subplot_specs_colspan(): anchor='y5' ), yaxis1=YAxis( - domain=[0.0, 0.26666666666666666], + domain=[0.7333333333333333, 1.0], anchor='x1' ), yaxis2=YAxis( @@ -742,23 +822,23 @@ def test_subplot_specs_colspan(): anchor='x3' ), yaxis4=YAxis( - domain=[0.7333333333333333, 1.0], + domain=[0.0, 0.26666666666666666], anchor='x4' ), yaxis5=YAxis( - domain=[0.7333333333333333, 1.0], + domain=[0.0, 0.26666666666666666], anchor='x5' ) ) ) - fig = tls.get_subplots(rows=3, columns=2, + fig = tls.make_subplots(rows=3, cols=2, specs=[[{'colspan':2}, None], [{}, {}], [{}, {}]]) assert fig == expected -def test_subplot_specs_rowspan(): +def test_specs_rowspan(): expected = Figure( data=Data(), layout=Layout( @@ -791,11 +871,11 @@ def test_subplot_specs_rowspan(): anchor='x1' ), yaxis2=YAxis( - domain=[0.0, 0.26666666666666666], + domain=[0.7333333333333333, 1.0], anchor='x2' ), yaxis3=YAxis( - domain=[0.0, 0.26666666666666666], + domain=[0.7333333333333333, 1.0], anchor='x3' ), yaxis4=YAxis( @@ -807,20 +887,19 @@ def test_subplot_specs_rowspan(): anchor='x5' ), yaxis6=YAxis( - domain=[0.7333333333333333, 1.0], + domain=[0.0, 0.26666666666666666], anchor='x6' ) ) ) - fig = tls.get_subplots(rows=3, columns=3, + fig = tls.make_subplots(rows=3, cols=3, specs=[[{'rowspan': 3}, {}, {}], [None, {}, {}], [None, {'colspan': 2}, None]]) - assert fig == expected -def test_subplot_specs_rowspan2(): +def test_specs_rowspan2(): expected = Figure( data=Data(), layout=Layout( @@ -845,15 +924,15 @@ def test_subplot_specs_rowspan2(): anchor='y5' ), yaxis1=YAxis( - domain=[0.0, 0.26666666666666666], + domain=[0.7333333333333333, 1.0], anchor='x1' ), yaxis2=YAxis( - domain=[0.0, 0.26666666666666666], + domain=[0.7333333333333333, 1.0], anchor='x2' ), yaxis3=YAxis( - domain=[0.0, 0.6333333333333333], + domain=[0.36666666666666664, 1.0], anchor='x3' ), yaxis4=YAxis( @@ -861,21 +940,80 @@ def test_subplot_specs_rowspan2(): anchor='x4' ), yaxis5=YAxis( - domain=[0.7333333333333333, 1.0], + domain=[0.0, 0.26666666666666666], anchor='x5' ) ) ) - fig = tls.get_subplots(rows=3, columns=3, + fig = tls.make_subplots(rows=3, cols=3, specs=[[{}, {}, {'rowspan': 2}], [{'colspan': 2}, None, None], [{'colspan': 3}, None, None]]) - assert fig == expected -def test_subplot_specs_colspan_rowpan(): +def test_specs_colspan_rowpan(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.6444444444444445], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y6' + ), + yaxis1=YAxis( + domain=[0.36666666666666664, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x6' + ) + ) + ) + + fig = tls.make_subplots(rows=3, cols=3, + specs=[[{'colspan': 2, 'rowspan': 2}, None, {}], + [None, None, {}], + [{}, {}, {}]]) + assert fig == expected +def test_specs_colspan_rowpan_bottom_left(): expected = Figure( data=Data(), layout=Layout( @@ -930,21 +1068,22 @@ def test_subplot_specs_colspan_rowpan(): ) ) - fig = tls.get_subplots(rows=3, columns=3, - specs=[[{'colspan': 2, 'rowspan': 2}, None, {}], + fig = tls.make_subplots(rows=3, cols=3, + specs=[[{'colspan': 2, 'rowspan': 2}, None, {}], [None, None, {}], - [{}, {}, {}]]) + [{}, {}, {}]], + start_cell='bottom-left') assert fig == expected -def test_subplot_specs_is_3d(): +def test_specs_is_3d(): expected = Figure( data=Data(), layout=Layout( scene1=Scene( - domain={'y': [0.0, 0.425], 'x': [0.0, 0.45]} + domain={'y': [0.575, 1.0], 'x': [0.0, 0.45]} ), scene2=Scene( - domain={'y': [0.575, 1.0], 'x': [0.0, 0.45]} + domain={'y': [0.0, 0.425], 'x': [0.0, 0.45]} ), xaxis1=XAxis( domain=[0.55, 1.0], @@ -955,23 +1094,67 @@ def test_subplot_specs_is_3d(): anchor='y2' ), yaxis1=YAxis( - domain=[0.0, 0.425], + domain=[0.575, 1.0], anchor='x1' ), yaxis2=YAxis( - domain=[0.575, 1.0], + domain=[0.0, 0.425], anchor='x2' ) ) ) - fig = tls.get_subplots(rows=2, columns=2, + fig = tls.make_subplots(rows=2, cols=2, specs=[[{'is_3d': True}, {}], [{'is_3d': True}, {}]]) + assert fig == expected + +def test_specs_padding(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.1, 0.5], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.5, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.0, 0.5], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.5, 0.9], + anchor='y4' + ), + yaxis1=YAxis( + domain=[0.5, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.7, 1.0], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.3], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.0, 0.5], + anchor='x4' + ) + ) + ) + fig = tls.make_subplots(rows=2, cols=2, + horizontal_spacing=0, vertical_spacing=0, + specs=[[{'l': 0.1}, {'b': 0.2}], + [{'t': 0.2}, {'r': 0.1}]]) assert fig == expected -def test_subplot_specs_padding(): +def test_specs_padding_bottom_left(): expected = Figure( data=Data(), layout=Layout( @@ -1010,14 +1193,16 @@ def test_subplot_specs_padding(): ) ) - fig = tls.get_subplots(rows=2, columns=2, + fig = tls.make_subplots(rows=2, cols=2, horizontal_spacing=0, vertical_spacing=0, specs=[[{'l': 0.1}, {'b': 0.2}], - [{'t': 0.2}, {'r': 0.1}]]) - + [{'t': 0.2}, {'r': 0.1}]], + start_cell='bottom-left') assert fig == expected -def test_subplot_shared_xaxes(): +# def test_shared_xaxes(): + +def test_shared_xaxes_bottom_left(): expected = Figure( data=Data(), layout=Layout( @@ -1062,11 +1247,11 @@ def test_subplot_shared_xaxes(): ) ) - fig = tls.get_subplots(rows=2, columns=3, shared_xaxes=True) - + fig = tls.make_subplots(rows=2, cols=3, + shared_xaxes=True, start_cell='bottom-left') assert fig == expected -def test_subplot_shared_yaxes(): +def test_shared_yaxes(): expected = Figure( data=Data(), layout=Layout( @@ -1076,12 +1261,12 @@ def test_subplot_shared_yaxes(): ), xaxis10=XAxis( domain=[0.55, 1.0], - anchor='free', - position=0.848 + anchor='free' ), xaxis2=XAxis( domain=[0.55, 1.0], - anchor='free' + anchor='free', + position=0.848 ), xaxis3=XAxis( domain=[0.0, 0.45], @@ -1090,7 +1275,7 @@ def test_subplot_shared_yaxes(): xaxis4=XAxis( domain=[0.55, 1.0], anchor='free', - position=0.212 + position=0.636 ), xaxis5=XAxis( domain=[0.0, 0.45], @@ -1108,18 +1293,18 @@ def test_subplot_shared_yaxes(): xaxis8=XAxis( domain=[0.55, 1.0], anchor='free', - position=0.636 + position=0.212 ), xaxis9=XAxis( domain=[0.0, 0.45], anchor='y5' ), yaxis1=YAxis( - domain=[0.0, 0.152], + domain=[0.848, 1.0], anchor='x1' ), yaxis2=YAxis( - domain=[0.212, 0.364], + domain=[0.636, 0.788], anchor='x2' ), yaxis3=YAxis( @@ -1127,21 +1312,22 @@ def test_subplot_shared_yaxes(): anchor='x3' ), yaxis4=YAxis( - domain=[0.636, 0.788], + domain=[0.212, 0.364], anchor='x4' ), yaxis5=YAxis( - domain=[0.848, 1.0], + domain=[0.0, 0.152], anchor='x5' ) ) ) - fig = tls.get_subplots(rows=5, columns=2, shared_yaxes=True) - + fig = tls.make_subplots(rows=5, cols=2, shared_yaxes=True) assert fig == expected -def test_subplot_shared_xaxes_yaxes(): +# def test_shared_xaxes_yaxes(): + +def test_shared_xaxes_yaxes_bottom_left(): expected = Figure( data=Data(), layout=Layout( @@ -1171,12 +1357,15 @@ def test_subplot_shared_xaxes_yaxes(): ) ) ) - fig = tls.get_subplots(rows=3, columns=3, - shared_xaxes=True, shared_yaxes=True) + fig = tls.make_subplots(rows=3, cols=3, + shared_xaxes=True, shared_yaxes=True, + start_cell='bottom-left') assert fig == expected -def test_subplot_shared_axes_list(): +# def test_shared_axes_list(): + +def test_shared_axes_list_bottom_left(): expected = Figure( data=Data(), layout=Layout( @@ -1207,14 +1396,15 @@ def test_subplot_shared_axes_list(): ) ) - fig = tls.get_subplots(rows=2, columns=2, - shared_xaxes=[(1,1), (2,1)], - shared_yaxes=[(1,1), (1,2)]) - + fig = tls.make_subplots(rows=2, cols=2, + shared_xaxes=[(1,1), (2,1)], + shared_yaxes=[(1,1), (1,2)], + start_cell='bottom-left') assert fig == expected +# def test_shared_axes_list_of_lists(): -def test_subplot_shared_axes_list_of_lists(): +def test_shared_axes_list_of_lists_bottom_left(): expected = Figure( data=Data(), layout=Layout( @@ -1262,21 +1452,66 @@ def test_subplot_shared_axes_list_of_lists(): ) ) - fig = tls.get_subplots(rows=2, columns=3, - shared_xaxes=[[(1,1), (2,1)], - [(1,3), (2,3)]]) - + fig = tls.make_subplots(rows=2, cols=3, + shared_xaxes=[[(1,1), (2,1)], + [(1,3), (2,3)]], + start_cell='bottom-left') assert fig == expected -@raises(Exception) -def test_subplot_insets_wrong_type(): - fig = tls.get_subplots(insets="not going to work") +def test_insets(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.45], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.55, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.0, 0.45], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.55, 1.0], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.865, 0.955], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.575, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.0, 0.425], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.0, 0.425], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.085, 0.2975], + anchor='x5' + ) + ) + ) -@raises(Exception) -def test_subplot_insets_wrong_item(): - fig = tls.get_subplots(insets=[{'not': "going to work"}]) + fig = tls.make_subplots(rows=2, cols=2, + insets=[{'cell': (2,2), + 'l': 0.7, 'w': 0.2, + 'b': 0.2, 'h': 0.5}]) + assert fig == expected -def test_subplot_insets(): +def test_insets_bottom_left(): expected = Figure( data=Data(), layout=Layout( @@ -1323,14 +1558,58 @@ def test_subplot_insets(): ) ) - fig = tls.get_subplots(rows=2, columns=2, - insets=[{'cell': (2,2), - 'l': 0.7, 'w': 0.2, - 'b': 0.2, 'h': 0.5}]) + fig = tls.make_subplots(rows=2, cols=2, + insets=[{'cell': (2,2), + 'l': 0.7, 'w': 0.2, + 'b': 0.2, 'h': 0.5}], + start_cell='bottom-left') + assert fig == expected + +def test_insets_multiple(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 1.0], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.0, 1.0], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.8, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.8, 1.0], + anchor='y4' + ), + yaxis1=YAxis( + domain=[0.575, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.425], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.575, 1.0], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.0, 0.425], + anchor='x4' + ) + ) + ) + fig = tls.make_subplots(rows=2, + insets=[{'cell': (1,1), 'l':0.8}, + {'cell': (2,1), 'l':0.8}]) assert fig == expected -def test_subplot_insets_multiple(): +def test_insets_multiple_bottom_left(): expected = Figure( data=Data(), layout=Layout( @@ -1369,13 +1648,8 @@ def test_subplot_insets_multiple(): ) ) - fig = tls.get_subplots(rows=2, - insets=[{'cell': (1,1), 'l':0.8}, - {'cell': (2,1), 'l':0.8}]) - - -@raises(Exception) -def test_start_cell_wrong_values(): - fig = tls.get_subplots(rows=2, columns=2, start_cell='not gonna work') - - + fig = tls.make_subplots(rows=2, + insets=[{'cell': (1,1), 'l':0.8}, + {'cell': (2,1), 'l':0.8}], + start_cell='bottom-left') + assert fig == expected From bfa69cec98d81697d1aa66bd18cf99d589deb162 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 15:16:46 -0500 Subject: [PATCH 079/103] add exception for out of range 'insets.cell' --- plotly/tools.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index cc1e9a5a6a3..e1a222a1e9f 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -955,6 +955,14 @@ def _add_domain_is_3d(layout, s_label, x_domain, y_domain): r = inset['cell'][0] - 1 c = inset['cell'][1] - 1 + # Throw exception if r | c is out of range + if not (0 <= r < rows): + raise Exception("Some 'cell' row value it out of range. " + "Note: the starting cell is (1, 1)") + if not (0 <= c < cols): + raise Exception("Some 'cell' col value it out of range. " + "Note: the starting cell is (1, 1)") + # Get inset x domain using grid x_s = grid[r][c][0] + inset['l'] * width if inset['w'] == 'to_end': From 0f75a2d721c393460d309d6a2920eea21de53697 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 15:17:05 -0500 Subject: [PATCH 080/103] add tests for prev commit --- .../tests/test_core/test_tools/test_make_subplots.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_make_subplots.py b/plotly/tests/test_core/test_tools/test_make_subplots.py index 8cb793f7b70..d8bef702641 100644 --- a/plotly/tests/test_core/test_tools/test_make_subplots.py +++ b/plotly/tests/test_core/test_tools/test_make_subplots.py @@ -71,13 +71,21 @@ def test_specs_rowspan_too_big(): [{'rowspan': 2}]]) @raises(Exception) -def test_subplot_insets_wrong_type(): +def test_insets_wrong_type(): fig = tls.make_subplots(insets="not going to work") @raises(Exception) -def test_subplot_insets_wrong_item(): +def test_insets_wrong_item(): fig = tls.make_subplots(insets=[{'not': "going to work"}]) +@raises(Exception) +def test_insets_wrong_cell_row(): + fig = tls.make_subplots(insets=([{'cell': (0, 1)}])) + +@raises(Exception) +def test_insets_wrong_cell_col(): + fig = tls.make_subplots(insets=([{'cell': (1, 0)}])) + def test_get_single_plot(): expected = Figure( data=Data(), From 9c34d840253ded1acddda803d114270184bae926 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 7 Jan 2015 15:43:56 -0500 Subject: [PATCH 081/103] sync for graph ref (trace 'scene') --- plotly/graph_reference/graph_objs_meta.json | 8 ++++---- submodules/graph_reference | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plotly/graph_reference/graph_objs_meta.json b/plotly/graph_reference/graph_objs_meta.json index 61425e31548..127e4e44862 100644 --- a/plotly/graph_reference/graph_objs_meta.json +++ b/plotly/graph_reference/graph_objs_meta.json @@ -1586,9 +1586,9 @@ }, "scene": { "key_type": "plot_info", - "val_types": "'s1' | 's2' | 's3' | etc.", + "val_types": "'scene1' | 'scene2' | 'scene3' | etc.", "required": false, - "description": "This key determines the scene on which this trace will be plotted in. More info coming soon." + "description": "This key determines the scene on which this trace will be plotted in." }, "stream": { "key_type": "object", @@ -1657,9 +1657,9 @@ }, "scene": { "key_type": "plot_info", - "val_types": "'s1' | 's2' | 's3' | etc.", + "val_types": "'scene1' | 'scene2' | 'scene3' | etc.", "required": false, - "description": "This key determines the scene on which this trace will be plotted in. More info coming soon." + "description": "This key determines the scene on which this trace will be plotted in." }, "stream": { "key_type": "object", diff --git a/submodules/graph_reference b/submodules/graph_reference index 9ce9ade27da..fb337adf934 160000 --- a/submodules/graph_reference +++ b/submodules/graph_reference @@ -1 +1 @@ -Subproject commit 9ce9ade27da2554bde2b98b99c48fdfd2078f0d1 +Subproject commit fb337adf934ea06242fd826ed1d613a492c6873e From d39a0beebc51f67d14cfe8a2c5031c3b5947d2fa Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 11:05:05 -0500 Subject: [PATCH 082/103] make print_grid=True the default for make_subplots --- plotly/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index e1a222a1e9f..00a92624a62 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -493,7 +493,7 @@ def get_subplots(rows=1, columns=1, print_grid=False, **kwargs): def make_subplots(rows=1, cols=1, shared_xaxes=False, shared_yaxes=False, - start_cell='top-left', print_grid=False, + start_cell='top-left', print_grid=True, **kwargs): """Return an instance of plotly.graph_objs.Figure with the subplots domain set in 'layout'. @@ -575,7 +575,7 @@ def make_subplots(rows=1, cols=1, Choose the starting cell in the subplot grid used to set the domains of the subplots. - print_grid (kwarg, boolean, default=False): + print_grid (kwarg, boolean, default=True): If True, prints a tab-delimited string representation of your plot grid. From 87d645aeab3eeb6f18a48d9b4590dcf1d4fa81f9 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 11:05:22 -0500 Subject: [PATCH 083/103] sub '!' -> ':' in print grid msg --- plotly/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index 00a92624a62..8726a0be262 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1015,7 +1015,7 @@ def _add_domain_is_3d(layout, s_label, x_domain, y_domain): empty_str = ' (empty) ' # empty cell string # Init grid_str with intro message - grid_str = "This is the format of your plot grid!\n" + grid_str = "This is the format of your plot grid:\n" # Init tmp list of lists of strings (sorta like 'grid_ref' but w/ strings) _tmp = [['' for c in range(cols)] for r in range(rows)] From 08892a98c40c677486a5fb4f737a7aac9ebb5aa5 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 13:24:50 -0500 Subject: [PATCH 084/103] update shared_{x,y}axes docstring --- plotly/tools.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 8726a0be262..f52d80c8368 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -557,18 +557,20 @@ def make_subplots(rows=1, cols=1, shared_xaxes (kwarg, boolean or list, default=False) Assign shared x axes. - If True, all x axes are shared. + If True, subplots in the same grid column have one common + shared x-axis at the bottom of the gird. To assign shared x axes per subplot grid cell (see 'specs'), - send list (or list of lists, one list per shared axis) + send list (or list of lists, one list per shared x axis) of cell index tuples. shared_yaxes (kwarg, boolean or list, default=False) Assign shared y axes. - If True, all y axes are shared. + If True, subplots in the same grid row have one common + shared y-axis on the left-hand side of the gird. To assign shared y axes per subplot grid cell (see 'specs'), - send list (or list of lists, one list per shared axis) + send list (or list of lists, one list per shared y axis) of cell index tuples. start_cell (kwarg, 'bottom-left' or 'top-left', default='top-left') From e4d4936b1e334653a99db4b911941460465d5142 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 13:25:58 -0500 Subject: [PATCH 085/103] capitalize ROW_DIR --- plotly/tools.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index f52d80c8368..007c1b432e3 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -777,10 +777,10 @@ def _checks(item, defaults): height = (1. - vertical_spacing * (rows - 1)) / rows # Built row/col sequence using 'row_dir' and 'col_dir' - col_dir = START_CELL['col_dir'] - col_seq = range(cols)[::col_dir] - row_dir = START_CELL['row_dir'] - row_seq = range(rows)[::row_dir] + COL_DIR = START_CELL['col_dir'] + ROW_DIR = START_CELL['row_dir'] + col_seq = range(cols)[::COL_DIR] + row_seq = range(rows)[::ROW_DIR] # [grid] Build subplot grid (coord tuple of cell) grid = [[((width + horizontal_spacing) * c, @@ -908,7 +908,7 @@ def _add_domain_is_3d(layout, s_label, x_domain, y_domain): x_domain = [x_s, x_e] # Get y domain (dep. on row_dir) using grid & r_spanned - if row_dir > 0: + if ROW_DIR > 0: y_s = grid[r][c][1] + spec['b'] y_e = grid[r_spanned][c][1] + height - spec['t'] else: From 2de9ad7902198475efd03bfd25d0227209db3262 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 13:26:48 -0500 Subject: [PATCH 086/103] update get_anchors logic: - shared_xaxes=True -> shared x axis at bottom, always --- plotly/tools.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 007c1b432e3..f86d2c8348d 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -819,6 +819,9 @@ def _get_label(x_or_y, r, c, cnt, shared_axes): return label + # Row in grid of anchor row if shared_xaxes=True + ANCHOR_ROW = 0 if ROW_DIR > 0 else rows - 1 + # Function handling logic around 2d axis anchors # Return 'x{}' | 'y{}' | 'free' | False def _get_anchors(r, c, x_cnt, y_cnt, shared_xaxes, shared_yaxes): @@ -828,12 +831,10 @@ def _get_anchors(r, c, x_cnt, y_cnt, shared_xaxes, shared_yaxes): if isinstance(shared_xaxes, bool): if shared_xaxes: - if r == 0: - x_anchor = "y{col_cnt}".format(col_cnt=c+1) - else: + if r != ANCHOR_ROW: x_anchor = False y_anchor = 'free' - if shared_yaxes and c > 0: # TODO covers all cases? + if shared_yaxes and c != 0: # TODO covers all cases? y_anchor = False return x_anchor, y_anchor @@ -847,12 +848,10 @@ def _get_anchors(r, c, x_cnt, y_cnt, shared_xaxes, shared_yaxes): if isinstance(shared_yaxes, bool): if shared_yaxes: - if c == 0: - y_anchor = "x{row_cnt}".format(row_cnt=r+1) - else: + if c != 0: y_anchor = False x_anchor = 'free' - if shared_xaxes and r > 0: # TODO covers all cases? + if shared_xaxes and r != ANCHOR_ROW: # TODO all cases? x_anchor = False return x_anchor, y_anchor From bd1667261fd0d84b7e36e5a113d434df860f026e Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 13:26:59 -0500 Subject: [PATCH 087/103] add shared axes tests --- .../test_tools/test_make_subplots.py | 255 +++++++++++++++++- 1 file changed, 248 insertions(+), 7 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_make_subplots.py b/plotly/tests/test_core/test_tools/test_make_subplots.py index d8bef702641..4f83efba1c6 100644 --- a/plotly/tests/test_core/test_tools/test_make_subplots.py +++ b/plotly/tests/test_core/test_tools/test_make_subplots.py @@ -1208,7 +1208,53 @@ def test_specs_padding_bottom_left(): start_cell='bottom-left') assert fig == expected -# def test_shared_xaxes(): +def test_shared_xaxes(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y4' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y5' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y6' + ), + yaxis1=YAxis( + domain=[0.575, 1.0], + anchor='free' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='free', + position=0.35555555555555557 + ), + yaxis3=YAxis( + domain=[0.575, 1.0], + anchor='free', + position=0.7111111111111111 + ), + yaxis4=YAxis( + domain=[0.0, 0.425], + anchor='x1' + ), + yaxis5=YAxis( + domain=[0.0, 0.425], + anchor='x2' + ), + yaxis6=YAxis( + domain=[0.0, 0.425], + anchor='x3' + ) + ) + ) + + fig = tls.make_subplots(rows=2, cols=3, shared_xaxes=True) + assert fig == expected def test_shared_xaxes_bottom_left(): expected = Figure( @@ -1313,19 +1359,93 @@ def test_shared_yaxes(): ), yaxis2=YAxis( domain=[0.636, 0.788], - anchor='x2' + anchor='x3' ), yaxis3=YAxis( domain=[0.424, 0.576], - anchor='x3' + anchor='x5' ), yaxis4=YAxis( domain=[0.212, 0.364], - anchor='x4' + anchor='x7' ), yaxis5=YAxis( domain=[0.0, 0.152], + anchor='x9' + ) + ) + ) + + fig = tls.make_subplots(rows=5, cols=2, shared_yaxes=True) + assert fig == expected + +def test_shared_yaxes(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.45], + anchor='y1' + ), + xaxis10=XAxis( + domain=[0.55, 1.0], + anchor='free' + ), + xaxis2=XAxis( + domain=[0.55, 1.0], + anchor='free', + position=0.848 + ), + xaxis3=XAxis( + domain=[0.0, 0.45], + anchor='y2' + ), + xaxis4=XAxis( + domain=[0.55, 1.0], + anchor='free', + position=0.636 + ), + xaxis5=XAxis( + domain=[0.0, 0.45], + anchor='y3' + ), + xaxis6=XAxis( + domain=[0.55, 1.0], + anchor='free', + position=0.424 + ), + xaxis7=XAxis( + domain=[0.0, 0.45], + anchor='y4' + ), + xaxis8=XAxis( + domain=[0.55, 1.0], + anchor='free', + position=0.212 + ), + xaxis9=XAxis( + domain=[0.0, 0.45], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.848, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.636, 0.788], + anchor='x3' + ), + yaxis3=YAxis( + domain=[0.424, 0.576], anchor='x5' + ), + yaxis4=YAxis( + domain=[0.212, 0.364], + anchor='x7' + ), + yaxis5=YAxis( + domain=[0.0, 0.152], + anchor='x9' ) ) ) @@ -1333,7 +1453,41 @@ def test_shared_yaxes(): fig = tls.make_subplots(rows=5, cols=2, shared_yaxes=True) assert fig == expected -# def test_shared_xaxes_yaxes(): +def test_shared_xaxes_yaxes(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y3' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='free' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='free' + ), + yaxis1=YAxis( + domain=[0.7333333333333333, 1.0], + anchor='free' + ), + yaxis2=YAxis( + domain=[0.36666666666666664, 0.6333333333333333], + anchor='free' + ), + yaxis3=YAxis( + domain=[0.0, 0.26666666666666666], + anchor='x1' + ) + ) + ) + + fig = tls.make_subplots(rows=3, cols=3, + shared_xaxes=True, shared_yaxes=True) + assert fig == expected + def test_shared_xaxes_yaxes_bottom_left(): expected = Figure( @@ -1371,7 +1525,42 @@ def test_shared_xaxes_yaxes_bottom_left(): start_cell='bottom-left') assert fig == expected -# def test_shared_axes_list(): +def test_shared_axes_list(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.45], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.55, 1.0], + anchor='free', + position=0.575 + ), + xaxis3=XAxis( + domain=[0.55, 1.0], + anchor='y3' + ), + yaxis1=YAxis( + domain=[0.575, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.0, 0.425], + anchor='free' + ), + yaxis3=YAxis( + domain=[0.0, 0.425], + anchor='x3' + ) + ) + ) + + fig = tls.make_subplots(rows=2, cols=2, + shared_xaxes=[(1,1), (2,1)], + shared_yaxes=[(1,1), (1,2)]) + assert fig == expected def test_shared_axes_list_bottom_left(): expected = Figure( @@ -1410,7 +1599,59 @@ def test_shared_axes_list_bottom_left(): start_cell='bottom-left') assert fig == expected -# def test_shared_axes_list_of_lists(): +def test_shared_axes_list_of_lists(): + expected = Figure( + data=Data(), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y5' + ), + yaxis1=YAxis( + domain=[0.575, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.575, 1.0], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.0, 0.425], + anchor='free' + ), + yaxis5=YAxis( + domain=[0.0, 0.425], + anchor='x4' + ), + yaxis6=YAxis( + domain=[0.0, 0.425], + anchor='free', + position=0.7111111111111111 + ) + ) + ) + + fig = tls.make_subplots(rows=2, cols=3, + shared_xaxes=[[(1,1), (2,1)], + [(1,3), (2,3)]]) + assert fig == expected + def test_shared_axes_list_of_lists_bottom_left(): expected = Figure( From 6b2465b7e4839e510a6c0fbb8c028ff5400b7a86 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 14:41:48 -0500 Subject: [PATCH 088/103] paste grid_ref and grid_str to fig in make_subplots --- plotly/tools.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plotly/tools.py b/plotly/tools.py index f86d2c8348d..95a299eac7c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1086,6 +1086,10 @@ def _pad(s, cell_len=cell_len): print(grid_str) fig = graph_objs.Figure(layout=layout) + + fig['_grid_ref'] = grid_ref + fig['_grid_str'] = grid_str + return fig From 0437513608213fdbc78dd7cb2541684f382f2447 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 14:42:17 -0500 Subject: [PATCH 089/103] add print_grid method to Figure --- plotly/graph_objs/graph_objs.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plotly/graph_objs/graph_objs.py b/plotly/graph_objs/graph_objs.py index 75cde188a4b..ee5a734809f 100644 --- a/plotly/graph_objs/graph_objs.py +++ b/plotly/graph_objs/graph_objs.py @@ -919,6 +919,16 @@ def __init__(self, *args, **kwargs): kwargs['layout'] = Layout() super(Figure, self).__init__(*args, **kwargs) Figure.__init__ = __init__ # override method! + + def print_grid(self): + try: + grid_str = self['_grid_str'] + except KeyError: + raise Exception("Use tools.make_subplots " + "to create a subplot grid.") + print(grid_str) + Figure.print_grid = print_grid + return Figure Figure = get_patched_figure_class(Figure) From 9a2767b058f31ab972f658fb18fb384d0419c6fb Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 14:42:29 -0500 Subject: [PATCH 090/103] add append_trace method to Figure --- plotly/graph_objs/graph_objs.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/plotly/graph_objs/graph_objs.py b/plotly/graph_objs/graph_objs.py index ee5a734809f..00070ad98b4 100644 --- a/plotly/graph_objs/graph_objs.py +++ b/plotly/graph_objs/graph_objs.py @@ -929,6 +929,37 @@ def print_grid(self): print(grid_str) Figure.print_grid = print_grid + def append_trace(self, trace, row, col): + try: + grid_ref = self['_grid_ref'] + except KeyError: + raise Exception("In order to use Figure.append_trace, " + "you must first use tools.make_subplots " + "to create a subplot grid.") + try: + ref = grid_ref[row-1][col-1] + except IndexError: + raise Exception("The (row, col) pair sent is out of range. " + "Use Figure.print_grid to view the subplot grid. ") + if 'scene' in ref[0]: + trace['scene'] = ref[0] + if ref[0] not in self['layout']: + raise Exception("Something went wrong. " + "The scene object for ({r},{c}) subplot cell " + "got deleted.".format(r=row, c=col)) + else: + xaxis_key = "xaxis{ref}".format(ref=ref[0][1:]) + yaxis_key = "yaxis{ref}".format(ref=ref[1][1:]) + if (xaxis_key not in self['layout'] + or yaxis_key not in self['layout']): + raise Exception("Something went wrong. " + "An axis object for ({r},{c}) subplot cell " + "got deleted.".format(r=row, c=col)) + trace['xaxis'] = ref[0] + trace['yaxis'] = ref[1] + self['data'] += [trace] + Figure.append_trace = append_trace + return Figure Figure = get_patched_figure_class(Figure) From f13a4082f3dbbad64c4679cd07b01daf5e86d933 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 15:00:39 -0500 Subject: [PATCH 091/103] make _grid_ref and _grid_str attributes --- plotly/graph_objs/graph_objs.py | 4 ++-- plotly/tools.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plotly/graph_objs/graph_objs.py b/plotly/graph_objs/graph_objs.py index 00070ad98b4..c9db7885c0f 100644 --- a/plotly/graph_objs/graph_objs.py +++ b/plotly/graph_objs/graph_objs.py @@ -922,7 +922,7 @@ def __init__(self, *args, **kwargs): def print_grid(self): try: - grid_str = self['_grid_str'] + grid_str = self._grid_str except KeyError: raise Exception("Use tools.make_subplots " "to create a subplot grid.") @@ -931,7 +931,7 @@ def print_grid(self): def append_trace(self, trace, row, col): try: - grid_ref = self['_grid_ref'] + grid_ref = self._grid_ref except KeyError: raise Exception("In order to use Figure.append_trace, " "you must first use tools.make_subplots " diff --git a/plotly/tools.py b/plotly/tools.py index 95a299eac7c..500affadf1f 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1087,8 +1087,8 @@ def _pad(s, cell_len=cell_len): fig = graph_objs.Figure(layout=layout) - fig['_grid_ref'] = grid_ref - fig['_grid_str'] = grid_str + fig._grid_ref = grid_ref + fig._grid_str = grid_str return fig From 857013c191b7858628d9fd31082b1873d4807f6a Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 15:23:57 -0500 Subject: [PATCH 092/103] fix typo in exception msg --- plotly/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 500affadf1f..e4db044d9d5 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -958,10 +958,10 @@ def _add_domain_is_3d(layout, s_label, x_domain, y_domain): # Throw exception if r | c is out of range if not (0 <= r < rows): - raise Exception("Some 'cell' row value it out of range. " + raise Exception("Some 'cell' row value is out of range. " "Note: the starting cell is (1, 1)") if not (0 <= c < cols): - raise Exception("Some 'cell' col value it out of range. " + raise Exception("Some 'cell' col value is out of range. " "Note: the starting cell is (1, 1)") # Get inset x domain using grid From 7e2d3e0e62cd60e74b252cade8c47f6a12b1886c Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 15:24:10 -0500 Subject: [PATCH 093/103] better test name --- plotly/tests/test_core/test_tools/test_make_subplots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tests/test_core/test_tools/test_make_subplots.py b/plotly/tests/test_core/test_tools/test_make_subplots.py index 4f83efba1c6..d3d4ed94746 100644 --- a/plotly/tests/test_core/test_tools/test_make_subplots.py +++ b/plotly/tests/test_core/test_tools/test_make_subplots.py @@ -86,7 +86,7 @@ def test_insets_wrong_cell_row(): def test_insets_wrong_cell_col(): fig = tls.make_subplots(insets=([{'cell': (1, 0)}])) -def test_get_single_plot(): +def test_single_plot(): expected = Figure( data=Data(), layout=Layout( From ff0a112e777c85c0fd0b2bfe01175a14be6659eb Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 15:24:36 -0500 Subject: [PATCH 094/103] add exception for row/col =< 0 --- plotly/graph_objs/graph_objs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plotly/graph_objs/graph_objs.py b/plotly/graph_objs/graph_objs.py index c9db7885c0f..6cc1cb14d43 100644 --- a/plotly/graph_objs/graph_objs.py +++ b/plotly/graph_objs/graph_objs.py @@ -936,6 +936,12 @@ def append_trace(self, trace, row, col): raise Exception("In order to use Figure.append_trace, " "you must first use tools.make_subplots " "to create a subplot grid.") + if row <= 0: + raise Exception("Row value is out of range. " + "Note: the starting cell is (1, 1)") + if col <= 0: + raise Exception("Col value is out of range. " + "Note: the starting cell is (1, 1)") try: ref = grid_ref[row-1][col-1] except IndexError: From 8dc3f8028f3cae7f8bcd236b9dba95ea6a4e622f Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 15:24:59 -0500 Subject: [PATCH 095/103] add tests for append_trace --- .../test_graph_objs/test_append_trace.py | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 plotly/tests/test_core/test_graph_objs/test_append_trace.py diff --git a/plotly/tests/test_core/test_graph_objs/test_append_trace.py b/plotly/tests/test_core/test_graph_objs/test_append_trace.py new file mode 100644 index 00000000000..0ca385532f3 --- /dev/null +++ b/plotly/tests/test_core/test_graph_objs/test_append_trace.py @@ -0,0 +1,176 @@ +import plotly +from plotly.graph_objs import * +import plotly.tools as tls +from nose.tools import raises + + +@raises(Exception) +def test_print_grid_before_make_subplots(): + fig.print_grid() + +@raises(Exception) +def test_append_trace_before_make_subplots(): + trace = Scatter( + x=[1,2,3], + y=[2,3,4] + ) + fig.append_trace(trace, 2, 2) + +@raises(Exception) +def test_append_trace_row_out_of_range(): + trace = Scatter( + x=[1,2,3], + y=[2,3,4] + ) + fig = tls.make_subplots(rows=2, cols=3) + fig.append_trace(trace, 10, 2) + +@raises(Exception) +def test_append_trace_col_out_of_range(): + trace = Scatter( + x=[1,2,3], + y=[2,3,4] + ) + fig = tls.make_subplots(rows=2, cols=3) + fig.append_trace(trace, 2, 0) + +def test_append_scatter(): + expected = Figure( + data=Data([ + Scatter( + x=[1, 2, 3], + y=[2, 3, 4], + xaxis='x5', + yaxis='y5' + ) + ]), + layout=Layout( + xaxis1=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y1' + ), + xaxis2=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y2' + ), + xaxis3=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y3' + ), + xaxis4=XAxis( + domain=[0.0, 0.2888888888888889], + anchor='y4' + ), + xaxis5=XAxis( + domain=[0.35555555555555557, 0.6444444444444445], + anchor='y5' + ), + xaxis6=XAxis( + domain=[0.7111111111111111, 1.0], + anchor='y6' + ), + yaxis1=YAxis( + domain=[0.575, 1.0], + anchor='x1' + ), + yaxis2=YAxis( + domain=[0.575, 1.0], + anchor='x2' + ), + yaxis3=YAxis( + domain=[0.575, 1.0], + anchor='x3' + ), + yaxis4=YAxis( + domain=[0.0, 0.425], + anchor='x4' + ), + yaxis5=YAxis( + domain=[0.0, 0.425], + anchor='x5' + ), + yaxis6=YAxis( + domain=[0.0, 0.425], + anchor='x6' + ) + ) + ) + + trace = Scatter( + x=[1,2,3], + y=[2,3,4] + ) + fig = tls.make_subplots(rows=2, cols=3) + fig.append_trace(trace, 2, 2) + assert fig == expected + +@raises(Exception) +def test_append_scatter_after_deleting_xaxis(): + trace = Scatter( + x=[1,2,3], + y=[2,3,4] + ) + fig = tls.make_subplots(rows=2, cols=3) + fig['layout'].pop('xaxis5', None) + fig.append_trace(trace, 2, 2) + +@raises(Exception) +def test_append_scatter_after_deleting_yaxis(): + trace = Scatter( + x=[1,2,3], + y=[2,3,4] + ) + fig = tls.make_subplots(rows=2, cols=3) + fig['layout'].pop('yaxis5', None) + fig.append_trace(trace, 2, 2) + +def test_append_scatter3d(): + expected = Figure( + data=Data([ + Scatter3d( + x=[1, 2, 3], + y=[2, 3, 4], + z=[1, 2, 3], + scene='scene2' + ), + Scatter3d( + x=[1, 2, 3], + y=[2, 3, 4], + z=[1, 2, 3], + scene='scene2' + ) + ]), + layout=Layout( + scene1=Scene( + domain={'y': [0.575, 1.0], 'x': [0.0, 1.0]} + ), + scene2=Scene( + domain={'y': [0.0, 0.425], 'x': [0.0, 1.0]} + ) + ) + ) + + fig = tls.make_subplots(rows=2, cols=1, + specs=[[{'is_3d': True}], + [{'is_3d': True}]]) + trace = Scatter3d( + x=[1,2,3], + y=[2,3,4], + z=[1,2,3] + ) + fig.append_trace(trace, 1, 1) + fig.append_trace(trace, 2, 1) + assert fig == expected + +@raises(Exception) +def test_append_scatter3d_after_deleting_scene(): + fig = tls.make_subplots(rows=2, cols=1, + specs=[[{'is_3d': True}], + [{'is_3d': True}]]) + trace = Scatter3d( + x=[1,2,3], + y=[2,3,4], + z=[1,2,3] + ) + fig['layout'].pop('scene1', None) + fig.append_trace(trace, 1, 1) From 87501d859810bb0d3a67a5bff1140fc529b9bcfc Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 17:31:30 -0500 Subject: [PATCH 096/103] force set axis.position when anchor == 'free' --- plotly/tools.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index e4db044d9d5..e11278b2cb0 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -872,7 +872,7 @@ def _add_domain(layout, x_or_y, label, domain, anchor, position): axis = getattr(graph_objs, graph_obj)(domain=domain) if anchor: axis['anchor'] = anchor - if position: # N.B. No need to add position == 0 to axis + if isinstance(position, float): axis['position'] = position layout[name] = axis @@ -935,14 +935,20 @@ def _add_domain_is_3d(layout, s_label, x_domain, y_domain): # Add a xaxis to layout (N.B anchor == False -> no axis) if x_anchor: - x_position = y_domain[0] if x_anchor == 'free' else 0 + if x_anchor == 'free': + x_position = y_domain[0] + else: + x_position = False _add_domain(layout, 'x', x_label, x_domain, x_anchor, x_position) x_cnt += 1 # Add a yaxis to layout (N.B anchor == False -> no axis) if y_anchor: - y_position = x_domain[0] if y_anchor == 'free' else 0 + if y_anchor == 'free': + y_position = x_domain[0] + else: + y_position = False _add_domain(layout, 'y', y_label, y_domain, y_anchor, y_position) y_cnt += 1 From cebfae226e352befac436b958cf6d616ee46df0e Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 17:46:33 -0500 Subject: [PATCH 097/103] update tests --- .../test_tools/test_make_subplots.py | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_make_subplots.py b/plotly/tests/test_core/test_tools/test_make_subplots.py index d3d4ed94746..7b98ebc40bf 100644 --- a/plotly/tests/test_core/test_tools/test_make_subplots.py +++ b/plotly/tests/test_core/test_tools/test_make_subplots.py @@ -1226,7 +1226,8 @@ def test_shared_xaxes(): ), yaxis1=YAxis( domain=[0.575, 1.0], - anchor='free' + anchor='free', + position=0.0 ), yaxis2=YAxis( domain=[0.575, 1.0], @@ -1286,7 +1287,8 @@ def test_shared_xaxes_bottom_left(): ), yaxis4=YAxis( domain=[0.575, 1.0], - anchor='free' + anchor='free', + position=0.0 ), yaxis5=YAxis( domain=[0.575, 1.0], @@ -1389,7 +1391,8 @@ def test_shared_yaxes(): ), xaxis10=XAxis( domain=[0.55, 1.0], - anchor='free' + anchor='free', + position=0.0 ), xaxis2=XAxis( domain=[0.55, 1.0], @@ -1463,19 +1466,23 @@ def test_shared_xaxes_yaxes(): ), xaxis2=XAxis( domain=[0.35555555555555557, 0.6444444444444445], - anchor='free' + anchor='free', + position=0.0 ), xaxis3=XAxis( domain=[0.7111111111111111, 1.0], - anchor='free' + anchor='free', + position=0.0 ), yaxis1=YAxis( domain=[0.7333333333333333, 1.0], - anchor='free' + anchor='free', + position=0.0 ), yaxis2=YAxis( domain=[0.36666666666666664, 0.6333333333333333], - anchor='free' + anchor='free', + position=0.0 ), yaxis3=YAxis( domain=[0.0, 0.26666666666666666], @@ -1499,11 +1506,13 @@ def test_shared_xaxes_yaxes_bottom_left(): ), xaxis2=XAxis( domain=[0.35555555555555557, 0.6444444444444445], - anchor='free' + anchor='free', + position=0.0 ), xaxis3=XAxis( domain=[0.7111111111111111, 1.0], - anchor='free' + anchor='free', + position=0.0 ), yaxis1=YAxis( domain=[0.0, 0.26666666666666666], @@ -1511,11 +1520,13 @@ def test_shared_xaxes_yaxes_bottom_left(): ), yaxis2=YAxis( domain=[0.36666666666666664, 0.6333333333333333], - anchor='free' + anchor='free', + position=0.0 ), yaxis3=YAxis( domain=[0.7333333333333333, 1.0], - anchor='free' + anchor='free', + position=0.0 ) ) ) @@ -1548,7 +1559,8 @@ def test_shared_axes_list(): ), yaxis2=YAxis( domain=[0.0, 0.425], - anchor='free' + anchor='free', + position=0.0 ), yaxis3=YAxis( domain=[0.0, 0.425], @@ -1572,7 +1584,8 @@ def test_shared_axes_list_bottom_left(): ), xaxis2=XAxis( domain=[0.55, 1.0], - anchor='free' + anchor='free', + position=0.0 ), xaxis3=XAxis( domain=[0.55, 1.0], @@ -1584,7 +1597,8 @@ def test_shared_axes_list_bottom_left(): ), yaxis2=YAxis( domain=[0.575, 1.0], - anchor='free' + anchor='free', + position=0.0 ), yaxis3=YAxis( domain=[0.575, 1.0], @@ -1633,7 +1647,8 @@ def test_shared_axes_list_of_lists(): ), yaxis4=YAxis( domain=[0.0, 0.425], - anchor='free' + anchor='free', + position=0.0 ), yaxis5=YAxis( domain=[0.0, 0.425], @@ -1687,7 +1702,8 @@ def test_shared_axes_list_of_lists_bottom_left(): ), yaxis4=YAxis( domain=[0.575, 1.0], - anchor='free' + anchor='free', + position=0.0 ), yaxis5=YAxis( domain=[0.575, 1.0], From 68aee3673e0dfcb00c81b2b15dafce076e0f9feb Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 18:13:15 -0500 Subject: [PATCH 098/103] update make_subplot docstring --- plotly/tools.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index e11278b2cb0..d6cb1050441 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -500,43 +500,44 @@ def make_subplots(rows=1, cols=1, Example 1: # stack two subplots vertically - fig = tools.make_subplots(rows=2, print_grid=True) + fig = tools.make_subplots(rows=2) - This is the format of your plot grid! + This is the format of your plot grid: [ (1,1) x1,y1 ] [ (2,1) x2,y2 ] - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2])] fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] Example 2: # subplots with shared x axes - fig = tools.make_subplots(rows=2, shared_xaxes=True, print_grid=True) + fig = tools.make_subplots(rows=2, shared_xaxes=True) + + This is the format of your plot grid: + [ (1,1) x1,y1 ] + [ (2,1) x1,y2 ] - TODO What's the default behavior here?? - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y1')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2])] fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], yaxis='y2')] Example 3: # irregular subplot layout (more examples below under 'specs') fig = tools.make_subplots(rows=2, cols=2, specs=[[{}, {}], - [{'colspan': 2}, None]], - print_grid=True) + [{'colspan': 2}, None]]) This is the format of your plot grid! [ (1,1) x1,y1 ] [ (1,2) x2,y2 ] [ (2,1) x3,y3 - ] - fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x1', yaxis='y1')] + fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2])] fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x3', yaxis='y3')] Example 4: # insets - fig = tools.make_subplots(insets=[{'cell': (1,1), 'l': 0.7, 'b': 0.3}], - print_grid=True) + fig = tools.make_subplots(insets=[{'cell': (1,1), 'l': 0.7, 'b': 0.3}]) This is the format of your plot grid! [ (1,1) x1,y1 ] From 4ee9b59955175a042f33ea2a715395bfe2dd229e Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 9 Jan 2015 18:13:45 -0500 Subject: [PATCH 099/103] add docstring to append_trace and print_grid --- plotly/graph_objs/graph_objs.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/plotly/graph_objs/graph_objs.py b/plotly/graph_objs/graph_objs.py index 6cc1cb14d43..63eb0370e5b 100644 --- a/plotly/graph_objs/graph_objs.py +++ b/plotly/graph_objs/graph_objs.py @@ -921,20 +921,43 @@ def __init__(self, *args, **kwargs): Figure.__init__ = __init__ # override method! def print_grid(self): + """Print a visual layout of the figure's axes arrangement. + + This is only valid for figures that are created + with plotly.tools.make_subplots. + """ try: grid_str = self._grid_str except KeyError: - raise Exception("Use tools.make_subplots " + raise Exception("Use plotly.tools.make_subplots " "to create a subplot grid.") print(grid_str) Figure.print_grid = print_grid def append_trace(self, trace, row, col): + """ Helper function to add a data traces to your figure + that is bound to axes at the row, col index. + + The row, col index is generated from figures created with + plotly.tools.make_subplots and can be viewed with Figure.print_grid. + + Arguments: + + trace (plotly trace object): + The data trace to be bound. + + row (int): + Subplot row index on the subplot grid (see Figure.print_grid) + + col (int): + Subplot column index on the subplot grid (see Figure.print_grid) + + """ try: grid_ref = self._grid_ref except KeyError: raise Exception("In order to use Figure.append_trace, " - "you must first use tools.make_subplots " + "you must first use plotly.tools.make_subplots " "to create a subplot grid.") if row <= 0: raise Exception("Row value is out of range. " From d2d7a92ce151bc3d4400a9568655a7cbd7c850a6 Mon Sep 17 00:00:00 2001 From: etpinard Date: Sat, 10 Jan 2015 11:29:08 -0500 Subject: [PATCH 100/103] add Exception if rows / cols <= 0 --- plotly/tools.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index d6cb1050441..c14e7c71a81 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -401,19 +401,19 @@ def get_subplots(rows=1, columns=1, print_grid=False, **kwargs): Keywords arguments with constant defaults: - rows (int, default=1): + rows (kwarg, int greater than 0, default=1): Number of rows, evenly spaced vertically on the figure. - columns (int, default=1): + columns (kwarg, int greater than 0, default=1): Number of columns, evenly spaced horizontally on the figure. - horizontal_spacing (float in [0,1], default=0.1): + horizontal_spacing (kwarg, float in [0,1], default=0.1): Space between subplot columns. Applied to all columns. - vertical_spacing (float in [0,1], default=0.05): + vertical_spacing (kwarg, float in [0,1], default=0.05): Space between subplot rows. Applied to all rows. - print_grid (True | False, default=False): + print_grid (kwarg, True | False, default=False): If True, prints a tab-delimited string representation of your plot grid. @@ -433,10 +433,12 @@ def get_subplots(rows=1, columns=1, print_grid=False, **kwargs): ) # Throw exception for non-integer rows and columns - if not isinstance(rows, int): - raise Exception("Keyword argument 'rows' must be an int") - if not isinstance(columns, int): - raise Exception("Keyword argument 'columns' must be an int") + if not isinstance(rows, int) or rows <= 0: + raise Exception("Keyword argument 'rows' " + "must be an int greater than 0") + if not isinstance(columns, int) or columns <= 0: + raise Exception("Keyword argument 'columns' " + "must be an int greater than 0") # Throw exception if non-valid kwarg is sent VALID_KWARGS = ['horizontal_spacing', 'vertical_spacing'] @@ -550,10 +552,10 @@ def make_subplots(rows=1, cols=1, Keywords arguments with constant defaults: - rows (kwarg, int, default=1): + rows (kwarg, int greater than 0, default=1): Number of rows in the subplot grid. - cols (kwarg, int, default=1): + cols (kwarg, int greater than 0, default=1): Number of columns in the subplot grid. shared_xaxes (kwarg, boolean or list, default=False) @@ -651,10 +653,12 @@ def make_subplots(rows=1, cols=1, """ # Throw exception for non-integer rows and cols - if not isinstance(rows, int): - raise Exception("Keyword argument 'rows' must be an int") - if not isinstance(cols, int): - raise Exception("Keyword argument 'cols' must be an int") + if not isinstance(rows, int) or rows <= 0: + raise Exception("Keyword argument 'rows' " + "must be an int greater than 0") + if not isinstance(cols, int) or cols <= 0: + raise Exception("Keyword argument 'cols' " + "must be an int greater than 0") # Dictionary of things start_cell START_CELL_all = { From 1f1960d08a969828d4c8181f53482c9b172c1bba Mon Sep 17 00:00:00 2001 From: etpinard Date: Sat, 10 Jan 2015 11:29:31 -0500 Subject: [PATCH 101/103] add tests about last commit --- .../test_core/test_tools/test_get_subplots.py | 8 +++++++ .../test_tools/test_make_subplots.py | 22 +++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_get_subplots.py b/plotly/tests/test_core/test_tools/test_get_subplots.py index b5df85ceadc..026dffebff5 100644 --- a/plotly/tests/test_core/test_tools/test_get_subplots.py +++ b/plotly/tests/test_core/test_tools/test_get_subplots.py @@ -8,10 +8,18 @@ def test_non_integer_rows(): fig = tls.get_subplots(rows=2.1) +@raises(Exception) +def test_less_than_zero_rows(): + fig = tls.make_subplots(rows=-2) + @raises(Exception) def test_non_integer_columns(): fig = tls.get_subplots(columns=2/3) +@raises(Exception) +def test_less_than_zero_cols(): + fig = tls.make_subplots(columns=-10) + @raises(Exception) def test_wrong_kwarg(): fig = tls.get_subplots(stuff='no gonna work') diff --git a/plotly/tests/test_core/test_tools/test_make_subplots.py b/plotly/tests/test_core/test_tools/test_make_subplots.py index 7b98ebc40bf..ff449a77d2b 100644 --- a/plotly/tests/test_core/test_tools/test_make_subplots.py +++ b/plotly/tests/test_core/test_tools/test_make_subplots.py @@ -9,8 +9,16 @@ def test_non_integer_rows(): fig = tls.make_subplots(rows=2.1) @raises(Exception) -def test_non_integer_columns(): - fig = tls.make_subplots(columns=2/3) +def test_less_than_zero_rows(): + fig = tls.make_subplots(rows=-2) + +@raises(Exception) +def test_non_integer_cols(): + fig = tls.make_subplots(cols=2/3) + +@raises(Exception) +def test_less_than_zero_cols(): + fig = tls.make_subplots(cols=-10) @raises(Exception) def test_wrong_kwarg(): @@ -21,8 +29,8 @@ def test_non_integer_rows(): fig = tls.make_subplots(rows=2.1) @raises(Exception) -def test_non_integer_columns(): - fig = tls.make_subplots(columns=2/3) +def test_non_integer_cols(): + fig = tls.make_subplots(cols=2/3) @raises(Exception) def test_wrong_kwarg(): @@ -30,7 +38,7 @@ def test_wrong_kwarg(): @raises(Exception) def test_start_cell_wrong_values(): - fig = tls.make_subplots(rows=2, columns=2, start_cell='not gonna work') + fig = tls.make_subplots(rows=2, cols=2, start_cell='not gonna work') @raises(Exception) def test_specs_wrong_type(): @@ -51,12 +59,12 @@ def test_specs_wrong_item_key(): @raises(Exception) def test_specs_underspecified(): fig = tls.make_subplots(rows=2, specs=[{}]) - fig = tls.make_subplots(rows=2, columns=2, specs=[[{}, {}], [{}]]) + fig = tls.make_subplots(rows=2, cols=2, specs=[[{}, {}], [{}]]) @raises(Exception) def test_specs_overspecified(): fig = tls.make_subplots(rows=2, specs=[[{}], [{}], [{}]]) - fig = tls.make_subplots(columns=2, specs=[{}, {}, {}]) + fig = tls.make_subplots(cols=2, specs=[{}, {}, {}]) @raises(Exception) def test_specs_colspan_too_big(): From e0301ad4a80d7cb6aca78a39e82a287841b4671c Mon Sep 17 00:00:00 2001 From: etpinard Date: Sat, 10 Jan 2015 11:30:09 -0500 Subject: [PATCH 102/103] add example in Figure.append_trace docstring on ref in make_subplots --- plotly/graph_objs/graph_objs.py | 11 +++++++++++ plotly/tools.py | 2 ++ 2 files changed, 13 insertions(+) diff --git a/plotly/graph_objs/graph_objs.py b/plotly/graph_objs/graph_objs.py index 63eb0370e5b..69198576889 100644 --- a/plotly/graph_objs/graph_objs.py +++ b/plotly/graph_objs/graph_objs.py @@ -941,6 +941,17 @@ def append_trace(self, trace, row, col): The row, col index is generated from figures created with plotly.tools.make_subplots and can be viewed with Figure.print_grid. + Example: + # stack two subplots vertically + fig = tools.make_subplots(rows=2) + + This is the format of your plot grid: + [ (1,1) x1,y1 ] + [ (2,1) x2,y2 ] + + fig.append_trace(Scatter(x=[1,2,3], y=[2,1,2]), 1, 1) + fig.append_trace(Scatter(x=[1,2,3], y=[2,1,2]), 2, 1) + Arguments: trace (plotly trace object): diff --git a/plotly/tools.py b/plotly/tools.py index c14e7c71a81..59b9f5fd03c 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -511,6 +511,8 @@ def make_subplots(rows=1, cols=1, fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2])] fig['data'] += [Scatter(x=[1,2,3], y=[2,1,2], xaxis='x2', yaxis='y2')] + # or see Figure.append_trace + Example 2: # subplots with shared x axes fig = tools.make_subplots(rows=2, shared_xaxes=True) From 3ac711f83f8d2286273531e4a49e9994ba2db7ee Mon Sep 17 00:00:00 2001 From: etpinard Date: Sat, 10 Jan 2015 11:30:31 -0500 Subject: [PATCH 103/103] v 1.5.0 --- plotly/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/version.py b/plotly/version.py index 133086d0815..77f1c8e63c9 100644 --- a/plotly/version.py +++ b/plotly/version.py @@ -1 +1 @@ -__version__ = '1.4.13' +__version__ = '1.5.0'