Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5753e13

Browse filesBrowse files
authored
Assorted performance improvements (plotly#1061)
* Remove custom fullmatch function (it was pretty slow) Change regular expressions to match full strings and just use .match. * Add _str_to_dict_path fastpath for common case * Use dict rather than graph_objs when build up dendrogram * Store trace index in the trace object. This lets us avoid the _index_is calls that are expensive for large trace arrays. * Fix fig error when setting nested property on Frame hierarchy * Additional optimizations to use trace._trace_ind rather than _index_is
1 parent 43105ce commit 5753e13
Copy full SHA for 5753e13

File tree

Expand file treeCollapse file tree

3 files changed

+48
-47
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+48
-47
lines changed

‎plotly/basedatatypes.py

Copy file name to clipboardExpand all lines: plotly/basedatatypes.py
+43-43Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,11 @@
3131
Undefined = object()
3232

3333

34-
# back-port of fullmatch from Py3.4+
35-
def fullmatch(regex, string, flags=0):
36-
"""Emulate python-3.4 re.fullmatch()."""
37-
if 'pattern' in dir(regex):
38-
regex_string = regex.pattern
39-
else:
40-
regex_string = regex
41-
return re.match("(?:" + regex_string + r")\Z", string, flags=flags)
42-
43-
4434
class BaseFigure(object):
4535
"""
4636
Base class for all figure types (both widget and non-widget)
4737
"""
38+
_bracket_re = re.compile('^(.*)\[(\d+)\]$')
4839

4940
# Constructor
5041
# -----------
@@ -143,7 +134,7 @@ class is a subclass of both BaseFigure and widgets.DOMWidget.
143134
self._data_defaults = [{} for _ in data]
144135

145136
# ### Reparent trace objects ###
146-
for trace in data:
137+
for trace_ind, trace in enumerate(data):
147138
# By setting the trace's parent to be this figure, we tell the
148139
# trace object to use the figure's _data and _data_defaults
149140
# dicts to get/set it's properties, rather than using the trace
@@ -153,6 +144,9 @@ class is a subclass of both BaseFigure and widgets.DOMWidget.
153144
# We clear the orphan props since the trace no longer needs then
154145
trace._orphan_props.clear()
155146

147+
# Set trace index
148+
trace._trace_ind = trace_ind
149+
156150
# Layout
157151
# ------
158152
# ### Construct layout validator ###
@@ -463,6 +457,7 @@ def data(self, new_data):
463457
old_trace = self.data[i]
464458
old_trace._orphan_props.update(deepcopy(old_trace._props))
465459
old_trace._parent = None
460+
old_trace._trace_ind = None
466461

467462
# ### Compute trace props / defaults after removal ###
468463
traces_props_post_removal = [t for t in self._data]
@@ -527,6 +522,10 @@ def data(self, new_data):
527522
# Update trace objects tuple
528523
self._data_objs = list(new_data)
529524

525+
# Update trace indexes
526+
for trace_ind, trace in enumerate(self._data_objs):
527+
trace._trace_ind = trace_ind
528+
530529
# Restyle
531530
# -------
532531
def plotly_restyle(self, restyle_data, trace_indexes=None, **kwargs):
@@ -690,7 +689,7 @@ def _restyle_child(self, child, key_path_str, val):
690689

691690
# Compute trace index
692691
# -------------------
693-
trace_index = BaseFigure._index_is(self.data, child)
692+
trace_index = child._trace_ind
694693

695694
# Not in batch mode
696695
# -----------------
@@ -743,7 +742,12 @@ def _str_to_dict_path(key_path_str):
743742
-------
744743
tuple[str | int]
745744
"""
746-
if isinstance(key_path_str, tuple):
745+
if isinstance(key_path_str, string_types) and \
746+
'.' not in key_path_str and \
747+
'[' not in key_path_str:
748+
# Fast path for common case that avoids regular expressions
749+
return (key_path_str,)
750+
elif isinstance(key_path_str, tuple):
747751
# Nothing to do
748752
return key_path_str
749753
else:
@@ -752,11 +756,9 @@ def _str_to_dict_path(key_path_str):
752756

753757
# Split out bracket indexes.
754758
# e.g. ['foo', 'bar[1]'] -> ['foo', 'bar', '1']
755-
bracket_re = re.compile('(.*)\[(\d+)\]')
756759
key_path2 = []
757760
for key in key_path:
758-
match = fullmatch(bracket_re, key)
759-
#match = bracket_re.fullmatch(key)
761+
match = BaseFigure._bracket_re.match(key)
760762
if match:
761763
key_path2.extend(match.groups())
762764
else:
@@ -1065,6 +1067,10 @@ def add_traces(self, data, rows=None, cols=None):
10651067
# Validate traces
10661068
data = self._data_validator.validate_coerce(data)
10671069

1070+
# Set trace indexes
1071+
for ind, new_trace in enumerate(data):
1072+
new_trace._trace_ind = ind + len(self.data)
1073+
10681074
# Validate rows / cols
10691075
n = len(data)
10701076
BaseFigure._validate_rows_cols('rows', n, rows)
@@ -1212,14 +1218,9 @@ def _get_child_props(self, child):
12121218
"""
12131219
# Try to find index of child as a trace
12141220
# -------------------------------------
1215-
try:
1216-
trace_index = BaseFigure._index_is(self.data, child)
1217-
except ValueError as _:
1218-
trace_index = None
1219-
1220-
# Child is a trace
1221-
# ----------------
1222-
if trace_index is not None:
1221+
if isinstance(child, BaseTraceType):
1222+
# ### Child is a trace ###
1223+
trace_index = child._trace_ind
12231224
return self._data[trace_index]
12241225

12251226
# Child is the layout
@@ -1247,16 +1248,10 @@ def _get_child_prop_defaults(self, child):
12471248
-------
12481249
dict
12491250
"""
1250-
# Try to find index of child as a trace
1251-
# -------------------------------------
1252-
try:
1253-
trace_index = BaseFigure._index_is(self.data, child)
1254-
except ValueError as _:
1255-
trace_index = None
1256-
12571251
# Child is a trace
12581252
# ----------------
1259-
if trace_index is not None:
1253+
if isinstance(child, BaseTraceType):
1254+
trace_index = child._trace_ind
12601255
return self._data_defaults[trace_index]
12611256

12621257
# Child is the layout
@@ -3365,7 +3360,7 @@ class BaseLayoutType(BaseLayoutHierarchyType):
33653360
'polar']
33663361

33673362
_subplotid_prop_re = re.compile(
3368-
'(' + '|'.join(_subplotid_prop_names) + ')(\d+)')
3363+
'^(' + '|'.join(_subplotid_prop_names) + ')(\d+)$')
33693364

33703365
@property
33713366
def _subplotid_validators(self):
@@ -3429,16 +3424,14 @@ def _process_kwargs(self, **kwargs):
34293424
unknown_kwargs = {
34303425
k: v
34313426
for k, v in kwargs.items()
3432-
if not fullmatch(self._subplotid_prop_re, k)
3433-
# if not self._subplotid_prop_re.fullmatch(k)
3427+
if not self._subplotid_prop_re.match(k)
34343428
}
34353429
super(BaseLayoutHierarchyType, self)._process_kwargs(**unknown_kwargs)
34363430

34373431
subplot_kwargs = {
34383432
k: v
34393433
for k, v in kwargs.items()
3440-
if fullmatch(self._subplotid_prop_re, k)
3441-
#if self._subplotid_prop_re.fullmatch(k)
3434+
if self._subplotid_prop_re.match(k)
34423435
}
34433436

34443437
for prop, value in subplot_kwargs.items():
@@ -3458,8 +3451,7 @@ def _set_subplotid_prop(self, prop, value):
34583451
# Get regular expression match
34593452
# ----------------------------
34603453
# Note: we already tested that match exists in the constructor
3461-
# match = self._subplotid_prop_re.fullmatch(prop)
3462-
match = fullmatch(self._subplotid_prop_re, prop)
3454+
match = self._subplotid_prop_re.match(prop)
34633455
subplot_prop = match.group(1)
34643456
suffix_digit = int(match.group(2))
34653457

@@ -3520,7 +3512,7 @@ def _strip_subplot_suffix_of_1(self, prop):
35203512
# Handle subplot suffix digit of 1
35213513
# --------------------------------
35223514
# Remove digit of 1 from subplot id (e.g.. xaxis1 -> xaxis)
3523-
match = fullmatch(self._subplotid_prop_re, prop)
3515+
match = self._subplotid_prop_re.match(prop)
35243516

35253517
if match:
35263518
subplot_prop = match.group(1)
@@ -3580,7 +3572,7 @@ def __setitem__(self, prop, value):
35803572

35813573
# Check for subplot assignment
35823574
# ----------------------------
3583-
match = fullmatch(self._subplotid_prop_re, prop)
3575+
match = self._subplotid_prop_re.match(prop)
35843576
if match is None:
35853577
# Set as ordinary property
35863578
super(BaseLayoutHierarchyType, self).__setitem__(prop, value)
@@ -3594,8 +3586,7 @@ def __setattr__(self, prop, value):
35943586
"""
35953587
# Check for subplot assignment
35963588
# ----------------------------
3597-
# match = self._subplotid_prop_re.fullmatch(prop)
3598-
match = fullmatch(self._subplotid_prop_re, prop)
3589+
match = self._subplotid_prop_re.match(prop)
35993590
if match is None:
36003591
# Set as ordinary property
36013592
super(BaseLayoutHierarchyType, self).__setattr__(prop, value)
@@ -3649,6 +3640,7 @@ class BaseTraceHierarchyType(BasePlotlyType):
36493640

36503641
def __init__(self, plotly_name, **kwargs):
36513642
super(BaseTraceHierarchyType, self).__init__(plotly_name, **kwargs)
3643+
36523644
def _send_prop_set(self, prop_path_str, val):
36533645
if self.parent:
36543646
# ### Inform parent of restyle operation ###
@@ -3680,6 +3672,9 @@ def __init__(self, plotly_name, **kwargs):
36803672
# ### Callbacks to be called on selection ###
36813673
self._select_callbacks = []
36823674

3675+
# ### Trace index in figure ###
3676+
self._trace_ind = None
3677+
36833678
# uid
36843679
# ---
36853680
# All trace types must have a top-level UID
@@ -3951,6 +3946,11 @@ def _send_prop_set(self, prop_path_str, val):
39513946
# propagated to parents
39523947
pass
39533948

3949+
def _restyle_child(self, child, key_path_str, val):
3950+
# Note: Frames are not supported by FigureWidget, and updates are not
3951+
# propagated to parents
3952+
pass
3953+
39543954
def on_change(self, callback, *args):
39553955
raise NotImplementedError(
39563956
'Change callbacks are not supported on Frames')

‎plotly/basewidget.py

Copy file name to clipboardExpand all lines: plotly/basewidget.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import ipywidgets as widgets
1111
from traitlets import List, Unicode, Dict, observe, Integer
12-
from .basedatatypes import BaseFigure, BasePlotlyType, fullmatch
12+
from .basedatatypes import BaseFigure, BasePlotlyType
1313
from .callbacks import (BoxSelector, LassoSelector,
1414
InputDeviceState, Points)
1515
from .serializers import custom_serializers
@@ -550,7 +550,7 @@ def _handler_js2py_layoutDelta(self, change):
550550
# may include axes that weren't explicitly defined by the user.
551551
for proppath in delta_transform:
552552
prop = proppath[0]
553-
match = fullmatch(self.layout._subplotid_prop_re, prop)
553+
match = self.layout._subplotid_prop_re.match(prop)
554554
if match and prop not in self.layout:
555555
# We need to create a subplotid object
556556
self.layout[prop] = {}

‎plotly/figure_factory/_dendrogram.py

Copy file name to clipboardExpand all lines: plotly/figure_factory/_dendrogram.py
+3-2Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,12 @@ def get_dendrogram_traces(self, X, colorscale, distfun, linkagefun, hovertext):
288288
hovertext_label = None
289289
if hovertext:
290290
hovertext_label = hovertext[i]
291-
trace = graph_objs.Scatter(
291+
trace = dict(
292+
type='scatter',
292293
x=np.multiply(self.sign[self.xaxis], xs),
293294
y=np.multiply(self.sign[self.yaxis], ys),
294295
mode='lines',
295-
marker=graph_objs.scatter.Marker(color=colors[color_key]),
296+
marker=dict(color=colors[color_key]),
296297
text=hovertext_label,
297298
hoverinfo='text'
298299
)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.