From a32ef7efbbea6ff49600b810d85a0c931ca74b5e Mon Sep 17 00:00:00 2001 From: dabana Date: Mon, 24 Sep 2018 07:11:03 -0400 Subject: [PATCH 1/9] add returnAsFigureWidget bool in options and install a dummy callback. --- plotly/figure_factory/_quiver.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plotly/figure_factory/_quiver.py b/plotly/figure_factory/_quiver.py index 8d0de352ba..1525767c8c 100644 --- a/plotly/figure_factory/_quiver.py +++ b/plotly/figure_factory/_quiver.py @@ -8,7 +8,7 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, - angle=math.pi / 9, **kwargs): + angle=math.pi / 9, returnAsFigureWidget = False, **kwargs): """ Returns data for a quiver plot. @@ -98,8 +98,15 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, data = [quiver] layout = graph_objs.Layout(hovermode='closest') - return graph_objs.Figure(data=data, layout=layout) + if returnAsFigureWidget: + return graph_objs.Figure(data=data, layout=layout) + else: + f = graph_objs.FigureWidget(data=data, layout=layout) + f.layout.on_change(update_quiver, 'xaxis.range', 'yaxis.range', 'width', 'height') + return f +def update_quiver(layout): + print("Inside callback function") class _Quiver(object): """ From 3d1f30e0f6a2af5137247df2dbd2942f1c3a9ab7 Mon Sep 17 00:00:00 2001 From: dabana Date: Mon, 24 Sep 2018 07:53:40 -0400 Subject: [PATCH 2/9] add parameter to options, scale u and fix in layout. --- plotly/figure_factory/_quiver.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/plotly/figure_factory/_quiver.py b/plotly/figure_factory/_quiver.py index 8d0de352ba..97ef0331c7 100644 --- a/plotly/figure_factory/_quiver.py +++ b/plotly/figure_factory/_quiver.py @@ -8,7 +8,7 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, - angle=math.pi / 9, **kwargs): + angle=math.pi / 9, scaleratio = 1, **kwargs): """ Returns data for a quiver plot. @@ -88,15 +88,21 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, utils.validate_positive_scalars(arrow_scale=arrow_scale, scale=scale) barb_x, barb_y = _Quiver(x, y, u, v, scale, - arrow_scale, angle).get_barbs() + arrow_scale, angle, scaleratio).get_barbs() arrow_x, arrow_y = _Quiver(x, y, u, v, scale, - arrow_scale, angle).get_quiver_arrows() + arrow_scale, angle, scaleratio).get_quiver_arrows() quiver = graph_objs.Scatter(x=barb_x + arrow_x, y=barb_y + arrow_y, mode='lines', **kwargs) data = [quiver] - layout = graph_objs.Layout(hovermode='closest') + layout = graph_objs.Layout( + hovermode='closest', + yaxis=dict( + scaleratio = scaleratio, + scaleanchor = "x" + ) + ) return graph_objs.Figure(data=data, layout=layout) @@ -106,7 +112,7 @@ class _Quiver(object): Refer to FigureFactory.create_quiver() for docstring """ def __init__(self, x, y, u, v, - scale, arrow_scale, angle, **kwargs): + scale, arrow_scale, angle, scaleratio = 1, **kwargs): try: x = utils.flatten(x) except exceptions.PlotlyError: @@ -132,6 +138,7 @@ def __init__(self, x, y, u, v, self.u = u self.v = v self.scale = scale + self.scaleratio = scaleratio self.arrow_scale = arrow_scale self.angle = angle self.end_x = [] @@ -148,7 +155,7 @@ def scale_uv(self): endpoints of the arrows so a smaller scale value will result in less overlap of arrows. """ - self.u = [i * self.scale for i in self.u] + self.u = [i * self.scale * self.scaleratio for i in self.u] self.v = [i * self.scale for i in self.v] def get_barbs(self): @@ -188,7 +195,7 @@ def get_quiver_arrows(self): point1, endpoint, point2 y_values separated by a None to create the barb of the arrow. """ - dif_x = [i - j for i, j in zip(self.end_x, self.x)] + dif_x = [i - j for i, j in zip(self.end_x, self.x)] dif_y = [i - j for i, j in zip(self.end_y, self.y)] # Get barb lengths(default arrow length = 30% barb length) From 9711089c9b5c7a79534a11a316253e17918dce6c Mon Sep 17 00:00:00 2001 From: dabana Date: Mon, 24 Sep 2018 08:45:50 -0400 Subject: [PATCH 3/9] Default value of scaleratio to None in . If None, scaleratio is not fixed in layout and is =1 in the Quiver object. --- plotly/figure_factory/_quiver.py | 37 +++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/plotly/figure_factory/_quiver.py b/plotly/figure_factory/_quiver.py index 93de917f3c..f8d17bdd13 100644 --- a/plotly/figure_factory/_quiver.py +++ b/plotly/figure_factory/_quiver.py @@ -8,7 +8,7 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, - angle=math.pi / 9, scaleratio = 1, + angle=math.pi / 9, scaleratio = None, returnAsFigureWidget = False, **kwargs): """ Returns data for a quiver plot. @@ -88,29 +88,40 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, utils.validate_equal_length(x, y, u, v) utils.validate_positive_scalars(arrow_scale=arrow_scale, scale=scale) - barb_x, barb_y = _Quiver(x, y, u, v, scale, - arrow_scale, angle, scaleratio).get_barbs() - arrow_x, arrow_y = _Quiver(x, y, u, v, scale, - arrow_scale, angle, scaleratio).get_quiver_arrows() + if scaleratio is None: + barb_x, barb_y = _Quiver(x, y, u, v, scale, + arrow_scale, angle).get_barbs() + arrow_x, arrow_y = _Quiver(x, y, u, v, scale, + arrow_scale, angle).get_quiver_arrows() + else: + barb_x, barb_y = _Quiver(x, y, u, v, scale, + arrow_scale, angle, scaleratio).get_barbs() + arrow_x, arrow_y = _Quiver(x, y, u, v, scale, + arrow_scale, angle, scaleratio).get_quiver_arrows() + quiver = graph_objs.Scatter(x=barb_x + arrow_x, y=barb_y + arrow_y, mode='lines', **kwargs) data = [quiver] - layout = graph_objs.Layout( - hovermode='closest', - yaxis=dict( - scaleratio = scaleratio, - scaleanchor = "x" + + if scaleratio is None: + layout = graph_objs.Layout(hovermode='closest') + else: + layout = graph_objs.Layout( + hovermode='closest', + yaxis=dict( + scaleratio = scaleratio, + scaleanchor = "x" + ) ) - ) if returnAsFigureWidget: - return graph_objs.Figure(data=data, layout=layout) - else: f = graph_objs.FigureWidget(data=data, layout=layout) f.layout.on_change(update_quiver, 'xaxis.range', 'yaxis.range', 'width', 'height') return f + else: + return graph_objs.Figure(data=data, layout=layout) def update_quiver(layout): print("Inside callback function") From 4b57de21e72664f8821394a27677105a098cc749 Mon Sep 17 00:00:00 2001 From: dabana Date: Mon, 24 Sep 2018 18:12:56 -0400 Subject: [PATCH 4/9] scaled the arrow tips correctly with scaleratio. --- plotly/figure_factory/_quiver.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plotly/figure_factory/_quiver.py b/plotly/figure_factory/_quiver.py index f8d17bdd13..d682c6e848 100644 --- a/plotly/figure_factory/_quiver.py +++ b/plotly/figure_factory/_quiver.py @@ -220,7 +220,7 @@ def get_quiver_arrows(self): # Get barb lengths(default arrow length = 30% barb length) barb_len = [None] * len(self.x) for index in range(len(barb_len)): - barb_len[index] = math.hypot(dif_x[index], dif_y[index]) + barb_len[index] = math.hypot(dif_x[index] / self.scaleratio, dif_y[index]) # Make arrow lengths arrow_len = [None] * len(self.x) @@ -229,7 +229,7 @@ def get_quiver_arrows(self): # Get barb angles barb_ang = [None] * len(self.x) for index in range(len(barb_ang)): - barb_ang[index] = math.atan2(dif_y[index], dif_x[index]) + barb_ang[index] = math.atan2(dif_y[index], dif_x[index] / self.scaleratio) # Set angles to create arrow ang1 = [i + self.angle for i in barb_ang] @@ -257,9 +257,9 @@ def get_quiver_arrows(self): # Set coordinates to create arrow for index in range(len(self.end_x)): - point1_x = [i - j for i, j in zip(self.end_x, seg1_x)] + point1_x = [i - j * self.scaleratio for i, j in zip(self.end_x, seg1_x)] point1_y = [i - j for i, j in zip(self.end_y, seg1_y)] - point2_x = [i - j for i, j in zip(self.end_x, seg2_x)] + point2_x = [i - j * self.scaleratio for i, j in zip(self.end_x, seg2_x)] point2_y = [i - j for i, j in zip(self.end_y, seg2_y)] # Combine lists to create arrow From 11cd9562117d9118c3a60ea1206a02b8b4bbd45f Mon Sep 17 00:00:00 2001 From: dabana Date: Mon, 24 Sep 2018 18:39:11 -0400 Subject: [PATCH 5/9] remove extra instance of _Quiver object. --- plotly/figure_factory/_quiver.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/plotly/figure_factory/_quiver.py b/plotly/figure_factory/_quiver.py index d682c6e848..f57095ac8e 100644 --- a/plotly/figure_factory/_quiver.py +++ b/plotly/figure_factory/_quiver.py @@ -89,21 +89,18 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, utils.validate_positive_scalars(arrow_scale=arrow_scale, scale=scale) if scaleratio is None: - barb_x, barb_y = _Quiver(x, y, u, v, scale, - arrow_scale, angle).get_barbs() - arrow_x, arrow_y = _Quiver(x, y, u, v, scale, - arrow_scale, angle).get_quiver_arrows() + quiver_obj = _Quiver(x, y, u, v, scale, arrow_scale, angle) else: - barb_x, barb_y = _Quiver(x, y, u, v, scale, - arrow_scale, angle, scaleratio).get_barbs() - arrow_x, arrow_y = _Quiver(x, y, u, v, scale, - arrow_scale, angle, scaleratio).get_quiver_arrows() + quiver_obj = _Quiver(x, y, u, v, scale, arrow_scale, angle, scaleratio) + + barb_x, barb_y = quiver_obj.get_barbs() + arrow_x, arrow_y = quiver_obj.get_quiver_arrows() - quiver = graph_objs.Scatter(x=barb_x + arrow_x, + quiver_plot = graph_objs.Scatter(x=barb_x + arrow_x, y=barb_y + arrow_y, mode='lines', **kwargs) - data = [quiver] + data = [quiver_plot] if scaleratio is None: layout = graph_objs.Layout(hovermode='closest') From 9d57f56bf3a43e2701195ed094b381cffa4882b3 Mon Sep 17 00:00:00 2001 From: dabana Date: Mon, 24 Sep 2018 18:43:31 -0400 Subject: [PATCH 6/9] remove references to FigureWidget for now. --- plotly/figure_factory/_quiver.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/plotly/figure_factory/_quiver.py b/plotly/figure_factory/_quiver.py index f57095ac8e..b8805ee13b 100644 --- a/plotly/figure_factory/_quiver.py +++ b/plotly/figure_factory/_quiver.py @@ -8,8 +8,7 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, - angle=math.pi / 9, scaleratio = None, - returnAsFigureWidget = False, **kwargs): + angle=math.pi / 9, scaleratio = None, **kwargs): """ Returns data for a quiver plot. @@ -113,15 +112,7 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, ) ) - if returnAsFigureWidget: - f = graph_objs.FigureWidget(data=data, layout=layout) - f.layout.on_change(update_quiver, 'xaxis.range', 'yaxis.range', 'width', 'height') - return f - else: - return graph_objs.Figure(data=data, layout=layout) - -def update_quiver(layout): - print("Inside callback function") + return graph_objs.Figure(data=data, layout=layout) class _Quiver(object): """ From c065497cb11f269df10a82ad393a096002c1222a Mon Sep 17 00:00:00 2001 From: dabana Date: Mon, 24 Sep 2018 18:54:17 -0400 Subject: [PATCH 7/9] add parameter description for in doc string. --- plotly/figure_factory/_quiver.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plotly/figure_factory/_quiver.py b/plotly/figure_factory/_quiver.py index b8805ee13b..bc8f460b6f 100644 --- a/plotly/figure_factory/_quiver.py +++ b/plotly/figure_factory/_quiver.py @@ -21,6 +21,9 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, :param (float in [0,1]) arrow_scale: value multiplied to length of barb to get length of arrowhead. Default = .3 :param (angle in radians) angle: angle of arrowhead. Default = pi/9 + :param (positive float) angle: the ratio between the scale of the y-axis + and the scale of the x-axis (scale_y / scale_x). Default = None, the + scale ratio is not fixed. :param kwargs: kwargs passed through plotly.graph_objs.Scatter for more information on valid kwargs call help(plotly.graph_objs.Scatter) From ee40b826dc3067bde5ce3c3ee3284f09d7c7ca00 Mon Sep 17 00:00:00 2001 From: dabana Date: Mon, 24 Sep 2018 19:25:11 -0400 Subject: [PATCH 8/9] add a 4th example to create_quiver to showcase the new param. --- plotly/figure_factory/_quiver.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/plotly/figure_factory/_quiver.py b/plotly/figure_factory/_quiver.py index bc8f460b6f..0dd74b9d8f 100644 --- a/plotly/figure_factory/_quiver.py +++ b/plotly/figure_factory/_quiver.py @@ -83,6 +83,29 @@ def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, # Add title to layout fig['layout'].update(title='Quiver Plot') + # Plot + py.plot(fig, filename='quiver') + ``` + + Example 4: Forcing a fix scale ratio to maintain the arrow length + ``` + import plotly.plotly as py + from plotly.figure_factory import create_quiver + + import numpy as np + + # Add data + x,y = np.meshgrid(np.arange(0.5, 3.5, .5), np.arange(0.5, 4.5, .5)) + u = x + v = y + angle = np.arctan(v / u) + norm = 0.25 + u = norm * np.cos(angle) + v = norm * np.sin(angle) + + # Create quiver with a fix scale ratio + fig = create_quiver(x, y, u, v, scale = 1, scaleratio = 0.5) + # Plot py.plot(fig, filename='quiver') ``` From 04dc529db158768718c9f1bfb4639a18f6dee894 Mon Sep 17 00:00:00 2001 From: dabana Date: Wed, 26 Sep 2018 10:42:05 -0400 Subject: [PATCH 9/9] add a test class for the quiver plot. add asimple test for the 'scaleratio' param. --- .../test_figure_factory.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/plotly/tests/test_optional/test_figure_factory/test_figure_factory.py b/plotly/tests/test_optional/test_figure_factory/test_figure_factory.py index 32c2511385..8a14be6242 100644 --- a/plotly/tests/test_optional/test_figure_factory/test_figure_factory.py +++ b/plotly/tests/test_optional/test_figure_factory/test_figure_factory.py @@ -2848,3 +2848,43 @@ def test_full_choropleth(self): ] self.assertEqual(fig['data'][2]['x'][:50], exp_fig_head) + +class TestQuiver(TestCase): + + def test_scaleratio_param(self): + x,y = np.meshgrid(np.arange(0.5, 3.5, .5), np.arange(0.5, 4.5, .5)) + u = x + v = y + angle = np.arctan(v / u) + norm = 0.25 + u = norm * np.cos(angle) + v = norm * np.sin(angle) + fig = ff.create_quiver(x, y, u, v, scale = 1, scaleratio = 0.5) + + exp_fig_head = [( + 0.5, + 0.5883883476483185, + None, + 1.0, + 1.1118033988749896, + None, + 1.5, + 1.6185854122563141, + None, + 2.0), + (0.5, + 0.6767766952966369, + None, + 0.5, + 0.6118033988749895, + None, + 0.5, + 0.5790569415042095, + None, + 0.5)] + + fig_head = [fig['data'][0]['x'][:10], fig['data'][0]['y'][:10]] + + self.assertEqual(fig_head, exp_fig_head) + +