From bf3829166dd375c79d514841d3d5e51830d985a6 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Mon, 9 Sep 2019 09:31:44 -0400 Subject: [PATCH 01/19] doc for custom data --- packages/python/plotly/plotly/express/_doc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/python/plotly/plotly/express/_doc.py b/packages/python/plotly/plotly/express/_doc.py index 8c54f89a026..80f1b545cd1 100644 --- a/packages/python/plotly/plotly/express/_doc.py +++ b/packages/python/plotly/plotly/express/_doc.py @@ -111,6 +111,10 @@ colref_list, "Values from these columns appear as extra data in the hover tooltip.", ], + custom_data=[ + colref_list, + "Values from these columns are extra data, to be used in widgets or Dash callbacks for example.", + ], text=[colref, "Values from this column appear in the figure as text labels."], locationmode=[ "(string, one of 'ISO-3', 'USA-states', 'country names')", From 3a8f33e7816d89b43e21ccf72c1be095ae4e8d1e Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Mon, 9 Sep 2019 10:46:33 -0400 Subject: [PATCH 02/19] add some comments --- packages/python/plotly/plotly/express/_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index 69b3c9ac382..38d6486539e 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -916,6 +916,7 @@ def make_figure(args, constructor, trace_patch={}, layout_patch={}): if constructor_to_use == go.Scatter else go.Scatterpolargl ) + # Create the trace trace = constructor_to_use(name=trace_name) if trace_spec.constructor not in [ go.Parcats, From 16b0753319352a77f3a85debc858392219413a42 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 09:48:42 -0400 Subject: [PATCH 03/19] add customdata to px functions --- .../plotly/plotly/express/_chart_types.py | 20 +++++++++ .../python/plotly/plotly/express/_core.py | 44 ++++++++++++++++--- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/packages/python/plotly/plotly/express/_chart_types.py b/packages/python/plotly/plotly/express/_chart_types.py index c6f2fc099a0..7a08d2e6ac4 100644 --- a/packages/python/plotly/plotly/express/_chart_types.py +++ b/packages/python/plotly/plotly/express/_chart_types.py @@ -12,6 +12,7 @@ def scatter( size=None, hover_name=None, hover_data=None, + custom_data=None, text=None, facet_row=None, facet_col=None, @@ -174,6 +175,7 @@ def line( line_dash=None, hover_name=None, hover_data=None, + custom_data=None, text=None, facet_row=None, facet_col=None, @@ -217,6 +219,7 @@ def area( color=None, hover_name=None, hover_data=None, + custom_data=None, text=None, facet_row=None, facet_col=None, @@ -262,6 +265,7 @@ def bar( facet_col=None, hover_name=None, hover_data=None, + custom_data=None, text=None, error_x=None, error_x_minus=None, @@ -368,6 +372,7 @@ def violin( facet_col=None, hover_name=None, hover_data=None, + custom_data=None, animation_frame=None, animation_group=None, category_orders={}, @@ -418,6 +423,7 @@ def box( facet_col=None, hover_name=None, hover_data=None, + custom_data=None, animation_frame=None, animation_group=None, category_orders={}, @@ -463,6 +469,7 @@ def strip( facet_col=None, hover_name=None, hover_data=None, + custom_data=None, animation_frame=None, animation_group=None, category_orders={}, @@ -514,6 +521,7 @@ def scatter_3d( text=None, hover_name=None, hover_data=None, + custom_data=None, error_x=None, error_x_minus=None, error_y=None, @@ -564,6 +572,7 @@ def line_3d( line_group=None, hover_name=None, hover_data=None, + custom_data=None, error_x=None, error_x_minus=None, error_y=None, @@ -609,6 +618,7 @@ def scatter_ternary( text=None, hover_name=None, hover_data=None, + custom_data=None, animation_frame=None, animation_group=None, category_orders={}, @@ -646,6 +656,7 @@ def line_ternary( line_group=None, hover_name=None, hover_data=None, + custom_data=None, text=None, animation_frame=None, animation_group=None, @@ -679,6 +690,7 @@ def scatter_polar( size=None, hover_name=None, hover_data=None, + custom_data=None, text=None, animation_frame=None, animation_group=None, @@ -721,6 +733,7 @@ def line_polar( line_dash=None, hover_name=None, hover_data=None, + custom_data=None, line_group=None, text=None, animation_frame=None, @@ -759,6 +772,7 @@ def bar_polar( color=None, hover_name=None, hover_data=None, + custom_data=None, animation_frame=None, animation_group=None, category_orders={}, @@ -798,6 +812,7 @@ def choropleth( color=None, hover_name=None, hover_data=None, + custom_data=None, size=None, animation_frame=None, animation_group=None, @@ -838,6 +853,7 @@ def scatter_geo( text=None, hover_name=None, hover_data=None, + custom_data=None, size=None, animation_frame=None, animation_group=None, @@ -882,6 +898,7 @@ def line_geo( text=None, hover_name=None, hover_data=None, + custom_data=None, line_group=None, animation_frame=None, animation_group=None, @@ -920,6 +937,7 @@ def scatter_mapbox( text=None, hover_name=None, hover_data=None, + custom_data=None, size=None, animation_frame=None, animation_group=None, @@ -955,6 +973,7 @@ def line_mapbox( text=None, hover_name=None, hover_data=None, + custom_data=None, line_group=None, animation_frame=None, animation_group=None, @@ -985,6 +1004,7 @@ def scatter_matrix( size=None, hover_name=None, hover_data=None, + custom_data=None, category_orders={}, labels={}, color_discrete_sequence=None, diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index 38d6486539e..6db35bd5d98 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -6,6 +6,7 @@ from .colors import qualitative, sequential import math import pandas +import numpy as np from plotly.subplots import ( make_subplots, @@ -137,12 +138,38 @@ def make_mapping(args, variable): def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): - + """Populates a dict with arguments to update trace + + Parameters + ---------- + args : dict + args to be used for the trace + trace_spec : dict + which kind of trace to be used (has constructor, marginal etc. + attributes) + g : pandas DataFrame + data + mapping_labels : dict + to be used for hovertemplate + sizeref : float + marker sizeref + + Returns + ------- + result : dict + dict to be used to update trace + fit_results : dict + fit information to be used for trendlines + """ if "line_close" in args and args["line_close"]: g = g.append(g.iloc[0]) result = trace_spec.trace_patch.copy() or {} fit_results = None hover_header = "" + custom_data_len = 1 + # parcats does not have customdata + if trace_spec.constructor != go.Parcats: + result["customdata"] = g.index.values[:, None] for k in trace_spec.attrs: v = args[k] v_label = get_decorated_label(args, v, k) @@ -194,7 +221,6 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): elif k == "trendline": if v in ["ols", "lowess"] and args["x"] and args["y"] and len(g) > 1: import statsmodels.api as sm - import numpy as np # sorting is bad but trace_specs with "trendline" have no other attrs g2 = g.sort_values(by=args["x"]) @@ -231,6 +257,10 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): if error_xy not in result: result[error_xy] = {} result[error_xy][arr] = g[v] + elif k == "custom_data": + result["customdata"] = np.hstack( + (result["customdata"], g[v].values)) + custom_data_len += len(v) # number of custom data columns elif k == "hover_name": if trace_spec.constructor not in [ go.Histogram, @@ -246,10 +276,12 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): go.Histogram2d, go.Histogram2dContour, ]: - result["customdata"] = g[v].values + result["customdata"] = np.hstack( + (result["customdata"], g[v].values)) for i, col in enumerate(v): v_label_col = get_decorated_label(args, col, None) - mapping_labels[v_label_col] = "%%{customdata[%d]}" % i + mapping_labels[v_label_col] = ( + "%%{customdata[%d]}" % (i + custom_data_len)) elif k == "color": if trace_spec.constructor == go.Choropleth: result["z"] = g[v] @@ -722,11 +754,11 @@ def infer_config(args, constructor, trace_patch): # Declare all supported attributes, across all plot types attrables = ( ["x", "y", "z", "a", "b", "c", "r", "theta", "size"] - + ["dimensions", "hover_name", "hover_data", "text", "error_x", "error_x_minus"] + + ["dimensions", "custom_data", "hover_name", "hover_data", "text", "error_x", "error_x_minus"] + ["error_y", "error_y_minus", "error_z", "error_z_minus"] + ["lat", "lon", "locations", "animation_group"] ) - array_attrables = ["dimensions", "hover_data"] + array_attrables = ["dimensions", "custom_data", "hover_data"] group_attrables = ["animation_frame", "facet_row", "facet_col", "line_group"] # Validate that the strings provided as attribute values reference columns From b39e460a493d94e6076a5898e40fbe608795c1f3 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 09:48:58 -0400 Subject: [PATCH 04/19] try to fix tests --- .../tests/test_plot_ly/test_stream/test_stream.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py index 01f8a5b143a..b20e1ee129b 100644 --- a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py +++ b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py @@ -36,7 +36,7 @@ def test_initialize_stream_plot(self): [Scatter(x=[], y=[], mode="markers", stream=stream)], auto_open=False, world_readable=True, - filename="stream-test2", + filename="stream-test3", ) self.assertTrue(url.startswith("https://plot.ly/~PythonAPI/")) time.sleep(0.5) @@ -49,7 +49,7 @@ def test_stream_single_points(self): [Scatter(x=[], y=[], mode="markers", stream=stream)], auto_open=False, world_readable=True, - filename="stream-test2", + filename="stream-test3", ) time.sleep(0.5) my_stream = py.Stream(tk) @@ -66,7 +66,7 @@ def test_stream_multiple_points(self): [Scatter(x=[], y=[], mode="markers", stream=stream)], auto_open=False, world_readable=True, - filename="stream-test2", + filename="stream-test3", ) time.sleep(0.5) my_stream = py.Stream(tk) @@ -83,7 +83,7 @@ def test_stream_layout(self): [Scatter(x=[], y=[], mode="markers", stream=stream)], auto_open=False, world_readable=True, - filename="stream-test2", + filename="stream-test3", ) time.sleep(0.5) title_0 = "some title i picked first" From a761ee4ba8887de519fd3c6302578acfb8797261 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 10:08:45 -0400 Subject: [PATCH 05/19] make black happy --- packages/python/plotly/plotly/express/_core.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index 6db35bd5d98..8ea757eb6fc 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -258,9 +258,8 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): result[error_xy] = {} result[error_xy][arr] = g[v] elif k == "custom_data": - result["customdata"] = np.hstack( - (result["customdata"], g[v].values)) - custom_data_len += len(v) # number of custom data columns + result["customdata"] = np.hstack((result["customdata"], g[v].values)) + custom_data_len += len(v) # number of custom data columns elif k == "hover_name": if trace_spec.constructor not in [ go.Histogram, @@ -277,11 +276,13 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): go.Histogram2dContour, ]: result["customdata"] = np.hstack( - (result["customdata"], g[v].values)) + (result["customdata"], g[v].values) + ) for i, col in enumerate(v): v_label_col = get_decorated_label(args, col, None) - mapping_labels[v_label_col] = ( - "%%{customdata[%d]}" % (i + custom_data_len)) + mapping_labels[v_label_col] = "%%{customdata[%d]}" % ( + i + custom_data_len + ) elif k == "color": if trace_spec.constructor == go.Choropleth: result["z"] = g[v] @@ -753,8 +754,9 @@ def apply_default_cascade(args): def infer_config(args, constructor, trace_patch): # Declare all supported attributes, across all plot types attrables = ( - ["x", "y", "z", "a", "b", "c", "r", "theta", "size"] - + ["dimensions", "custom_data", "hover_name", "hover_data", "text", "error_x", "error_x_minus"] + ["x", "y", "z", "a", "b", "c", "r", "theta", "size", "dimensions"] + + ["custom_data", "hover_name", "hover_data", "text"] + + ["error_x", "error_x_minus"] + ["error_y", "error_y_minus", "error_z", "error_z_minus"] + ["lat", "lon", "locations", "animation_group"] ) From 466abf79440fc62880bc2f7efee3dfc38c3385c5 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 10:23:16 -0400 Subject: [PATCH 06/19] debug test --- .../chart_studio/tests/test_plot_ly/test_stream/test_stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py index b20e1ee129b..619e0ad3889 100644 --- a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py +++ b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py @@ -85,7 +85,7 @@ def test_stream_layout(self): world_readable=True, filename="stream-test3", ) - time.sleep(0.5) + time.sleep(2) title_0 = "some title i picked first" title_1 = "this other title i picked second" my_stream = py.Stream(tk) From 0fbc1fd3ba771e0cfb480e99f2499d4f3b29a815 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 10:40:05 -0400 Subject: [PATCH 07/19] more debugging --- .../chart_studio/tests/test_plot_ly/test_stream/test_stream.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py index 619e0ad3889..8a88d763e5e 100644 --- a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py +++ b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py @@ -75,6 +75,7 @@ def test_stream_multiple_points(self): time.sleep(0.5) my_stream.close() + @nottest @attr("slow") def test_stream_layout(self): py.sign_in(un, ak) From ab421c225490913250d75801110c95b56fa046e9 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 10:49:20 -0400 Subject: [PATCH 08/19] typo --- .../chart_studio/tests/test_plot_ly/test_stream/test_stream.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py index 8a88d763e5e..bef891f3f14 100644 --- a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py +++ b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py @@ -7,6 +7,7 @@ import time from nose.plugins.attrib import attr +from nose.tools import nottest from chart_studio import plotly as py from plotly.graph_objs import Layout, Scatter, Stream From 8b1317f25f87ed933cdc524e02c221b3795c89d7 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 13:13:00 -0400 Subject: [PATCH 09/19] still debugging --- .../chart_studio/tests/test_plot_ly/test_stream/test_stream.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py index bef891f3f14..1607af13a45 100644 --- a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py +++ b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py @@ -60,6 +60,7 @@ def test_stream_single_points(self): my_stream.close() @attr("slow") + @nottest def test_stream_multiple_points(self): py.sign_in(un, ak) stream = Stream(token=tk, maxpoints=50) From 8ef1187e7128ce6fb1f82893b97e92ac5f49a1f9 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 14:05:09 -0400 Subject: [PATCH 10/19] Revert "debug test" This reverts commit 466abf79440fc62880bc2f7efee3dfc38c3385c5. --- .../tests/test_plot_ly/test_stream/test_stream.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py index 1607af13a45..b20e1ee129b 100644 --- a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py +++ b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py @@ -7,7 +7,6 @@ import time from nose.plugins.attrib import attr -from nose.tools import nottest from chart_studio import plotly as py from plotly.graph_objs import Layout, Scatter, Stream @@ -60,7 +59,6 @@ def test_stream_single_points(self): my_stream.close() @attr("slow") - @nottest def test_stream_multiple_points(self): py.sign_in(un, ak) stream = Stream(token=tk, maxpoints=50) @@ -77,7 +75,6 @@ def test_stream_multiple_points(self): time.sleep(0.5) my_stream.close() - @nottest @attr("slow") def test_stream_layout(self): py.sign_in(un, ak) @@ -88,7 +85,7 @@ def test_stream_layout(self): world_readable=True, filename="stream-test3", ) - time.sleep(2) + time.sleep(0.5) title_0 = "some title i picked first" title_1 = "this other title i picked second" my_stream = py.Stream(tk) From 1e2d7d902a800857e7f4df3674bac1f7be61a96b Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 14:06:10 -0400 Subject: [PATCH 11/19] Revert "try to fix tests" This reverts commit b39e460a493d94e6076a5898e40fbe608795c1f3. --- .../tests/test_plot_ly/test_stream/test_stream.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py index b20e1ee129b..01f8a5b143a 100644 --- a/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py +++ b/packages/python/chart-studio/chart_studio/tests/test_plot_ly/test_stream/test_stream.py @@ -36,7 +36,7 @@ def test_initialize_stream_plot(self): [Scatter(x=[], y=[], mode="markers", stream=stream)], auto_open=False, world_readable=True, - filename="stream-test3", + filename="stream-test2", ) self.assertTrue(url.startswith("https://plot.ly/~PythonAPI/")) time.sleep(0.5) @@ -49,7 +49,7 @@ def test_stream_single_points(self): [Scatter(x=[], y=[], mode="markers", stream=stream)], auto_open=False, world_readable=True, - filename="stream-test3", + filename="stream-test2", ) time.sleep(0.5) my_stream = py.Stream(tk) @@ -66,7 +66,7 @@ def test_stream_multiple_points(self): [Scatter(x=[], y=[], mode="markers", stream=stream)], auto_open=False, world_readable=True, - filename="stream-test3", + filename="stream-test2", ) time.sleep(0.5) my_stream = py.Stream(tk) @@ -83,7 +83,7 @@ def test_stream_layout(self): [Scatter(x=[], y=[], mode="markers", stream=stream)], auto_open=False, world_readable=True, - filename="stream-test3", + filename="stream-test2", ) time.sleep(0.5) title_0 = "some title i picked first" From edea629e52aa965a1ee2435763f0e58b36cc0960 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 17:19:25 -0400 Subject: [PATCH 12/19] implemented deduplication --- packages/python/plotly/plotly/express/_core.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index 8ea757eb6fc..d7befec6d00 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -275,13 +275,19 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): go.Histogram2d, go.Histogram2dContour, ]: - result["customdata"] = np.hstack( - (result["customdata"], g[v].values) + for col in v: + try: + position = args["custom_data"].index(col) + 1 + except ValueError: + position = custom_data_len + custom_data_len += 1 + result["customdata"] = np.hstack( + (result["customdata"], + g[col].values[:, None]) ) - for i, col in enumerate(v): v_label_col = get_decorated_label(args, col, None) mapping_labels[v_label_col] = "%%{customdata[%d]}" % ( - i + custom_data_len + position ) elif k == "color": if trace_spec.constructor == go.Choropleth: From 8c5eedd3d3c9ec2bcb4757e1b79fa83648319068 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 10 Sep 2019 20:32:51 -0400 Subject: [PATCH 13/19] make black happy --- packages/python/plotly/plotly/express/_core.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index d7befec6d00..8d66c20291a 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -282,13 +282,10 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): position = custom_data_len custom_data_len += 1 result["customdata"] = np.hstack( - (result["customdata"], - g[col].values[:, None]) - ) + (result["customdata"], g[col].values[:, None]) + ) v_label_col = get_decorated_label(args, col, None) - mapping_labels[v_label_col] = "%%{customdata[%d]}" % ( - position - ) + mapping_labels[v_label_col] = "%%{customdata[%d]}" % (position) elif k == "color": if trace_spec.constructor == go.Choropleth: result["z"] = g[v] From 336cae6f75328adddfbebb84fabddaa700389eb9 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Wed, 11 Sep 2019 09:39:27 -0400 Subject: [PATCH 14/19] removed index from customdata --- .../python/plotly/plotly/express/_core.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index 8d66c20291a..f858173e25d 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -166,10 +166,7 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): result = trace_spec.trace_patch.copy() or {} fit_results = None hover_header = "" - custom_data_len = 1 - # parcats does not have customdata - if trace_spec.constructor != go.Parcats: - result["customdata"] = g.index.values[:, None] + custom_data_len = 0 for k in trace_spec.attrs: v = args[k] v_label = get_decorated_label(args, v, k) @@ -258,8 +255,8 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): result[error_xy] = {} result[error_xy][arr] = g[v] elif k == "custom_data": - result["customdata"] = np.hstack((result["customdata"], g[v].values)) - custom_data_len += len(v) # number of custom data columns + result["customdata"] = g[v].values + custom_data_len = len(v) # number of custom data columns elif k == "hover_name": if trace_spec.constructor not in [ go.Histogram, @@ -277,13 +274,16 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): ]: for col in v: try: - position = args["custom_data"].index(col) + 1 - except ValueError: + position = args["custom_data"].index(col) + except (ValueError, AttributeError): position = custom_data_len custom_data_len += 1 - result["customdata"] = np.hstack( - (result["customdata"], g[col].values[:, None]) - ) + if "customdata" in result: + result["customdata"] = np.hstack( + (result["customdata"], g[col].values[:, None]) + ) + else: + result["customdata"] = g[col].values[:, None] v_label_col = get_decorated_label(args, col, None) mapping_labels[v_label_col] = "%%{customdata[%d]}" % (position) elif k == "color": From 4f0f3ae137dd9b21f584e0d826a7736a09ab183a Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Wed, 11 Sep 2019 17:11:15 -0400 Subject: [PATCH 15/19] corrected bug --- packages/python/plotly/plotly/express/_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index f858173e25d..bc27f8d1cdd 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -275,7 +275,7 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): for col in v: try: position = args["custom_data"].index(col) - except (ValueError, AttributeError): + except (ValueError, AttributeError, KeyError): position = custom_data_len custom_data_len += 1 if "customdata" in result: From 46b8aad9c0df766be37b721ae32f7eb2289bba8f Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Thu, 12 Sep 2019 13:17:11 -0400 Subject: [PATCH 16/19] added test for customdata --- .../plotly/tests/test_core/test_px/test_px.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py b/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py index 001bdb6997a..86809d5efc2 100644 --- a/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py +++ b/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py @@ -10,3 +10,27 @@ def test_scatter(): assert np.all(fig.data[0].y == iris.sepal_length) # test defaults assert fig.data[0].mode == "markers" + + +def test_custom_data_scatter(): + iris = px.data.iris() + # No hover, no custom data + fig = px.scatter(iris, x="sepal_width", y="sepal_length", color="species") + assert fig.data[0].customdata is None + # Hover, no custom data + fig = px.scatter(iris, x="sepal_width", y="sepal_length", color="species", + hover_data=["petal_length", "petal_width"]) + for data in fig.data: + assert np.all(np.in1d(data.customdata[:, 1], iris.petal_width)) + # Hover and custom data, no repeated arguments + fig = px.scatter(iris, x="sepal_width", y="sepal_length", + hover_data=["petal_length", "petal_width"], + custom_data=['species_id', 'species']) + assert np.all(fig.data[0].customdata[:, 0] == iris.species_id) + assert fig.data[0].customdata.shape[1] == 4 + # Hover and custom data, with repeated arguments + fig = px.scatter(iris, x="sepal_width", y="sepal_length", + hover_data=["petal_length", "petal_width", 'species_id'], + custom_data=['species_id', 'species']) + assert np.all(fig.data[0].customdata[:, 0] == iris.species_id) + assert fig.data[0].customdata.shape[1] == 4 From 61e892768eba4baa65dbc9aa1a1733db7b2e80d3 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Thu, 12 Sep 2019 14:03:14 -0400 Subject: [PATCH 17/19] blackify --- .../plotly/tests/test_core/test_px/test_px.py | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py b/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py index 86809d5efc2..322e3ce2844 100644 --- a/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py +++ b/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py @@ -18,19 +18,32 @@ def test_custom_data_scatter(): fig = px.scatter(iris, x="sepal_width", y="sepal_length", color="species") assert fig.data[0].customdata is None # Hover, no custom data - fig = px.scatter(iris, x="sepal_width", y="sepal_length", color="species", - hover_data=["petal_length", "petal_width"]) + fig = px.scatter( + iris, + x="sepal_width", + y="sepal_length", + color="species", + hover_data=["petal_length", "petal_width"], + ) for data in fig.data: assert np.all(np.in1d(data.customdata[:, 1], iris.petal_width)) # Hover and custom data, no repeated arguments - fig = px.scatter(iris, x="sepal_width", y="sepal_length", - hover_data=["petal_length", "petal_width"], - custom_data=['species_id', 'species']) + fig = px.scatter( + iris, + x="sepal_width", + y="sepal_length", + hover_data=["petal_length", "petal_width"], + custom_data=["species_id", "species"], + ) assert np.all(fig.data[0].customdata[:, 0] == iris.species_id) assert fig.data[0].customdata.shape[1] == 4 # Hover and custom data, with repeated arguments - fig = px.scatter(iris, x="sepal_width", y="sepal_length", - hover_data=["petal_length", "petal_width", 'species_id'], - custom_data=['species_id', 'species']) + fig = px.scatter( + iris, + x="sepal_width", + y="sepal_length", + hover_data=["petal_length", "petal_width", "species_id"], + custom_data=["species_id", "species"], + ) assert np.all(fig.data[0].customdata[:, 0] == iris.species_id) assert fig.data[0].customdata.shape[1] == 4 From cc3c6ba986d07c0d224fe2c7452cc5e64a9bc12f Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Thu, 12 Sep 2019 15:09:23 -0400 Subject: [PATCH 18/19] addressed Nicolas's comments --- packages/python/plotly/plotly/express/_core.py | 2 +- packages/python/plotly/plotly/express/_doc.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index bc27f8d1cdd..3d55d3004da 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -144,7 +144,7 @@ def make_trace_kwargs(args, trace_spec, g, mapping_labels, sizeref): ---------- args : dict args to be used for the trace - trace_spec : dict + trace_spec : NamedTuple which kind of trace to be used (has constructor, marginal etc. attributes) g : pandas DataFrame diff --git a/packages/python/plotly/plotly/express/_doc.py b/packages/python/plotly/plotly/express/_doc.py index 80f1b545cd1..fbefe4e3860 100644 --- a/packages/python/plotly/plotly/express/_doc.py +++ b/packages/python/plotly/plotly/express/_doc.py @@ -113,7 +113,7 @@ ], custom_data=[ colref_list, - "Values from these columns are extra data, to be used in widgets or Dash callbacks for example.", + "Values from these columns are extra data, to be used in widgets or Dash callbacks for example. This data is not user-visible but is included in events emitted by the figure (lasso selection etc.)", ], text=[colref, "Values from this column appear in the figure as text labels."], locationmode=[ From 79bcd12ed30ee41d1b5a9474c213213e6f0e8315 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Thu, 12 Sep 2019 15:24:31 -0400 Subject: [PATCH 19/19] added test --- .../python/plotly/plotly/tests/test_core/test_px/test_px.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py b/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py index 322e3ce2844..588bfa3d18a 100644 --- a/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py +++ b/packages/python/plotly/plotly/tests/test_core/test_px/test_px.py @@ -47,3 +47,7 @@ def test_custom_data_scatter(): ) assert np.all(fig.data[0].customdata[:, 0] == iris.species_id) assert fig.data[0].customdata.shape[1] == 4 + assert ( + fig.data[0].hovertemplate + == "sepal_width=%{x}
sepal_length=%{y}
petal_length=%{customdata[2]}
petal_width=%{customdata[3]}
species_id=%{customdata[0]}" + )