Source code for emat.analysis.explore_2.twoway
import numpy
import pandas
import plotly.graph_objs as go
from plotly.callbacks import BoxSelector, LassoSelector
from ipywidgets import HBox, VBox, Dropdown, Label, Text, Output
from ...viz import colors
from ...util.naming import clean_name, multiindex_to_strings
from .explore_visualizer import Visualizer
from .menu import Menu
import logging
_logger = logging.getLogger('EMAT.widget')
# def _debugprint(s):
# print(s.replace("rgb(255, 127, 14)", "<ORANGE>").replace("rgb(255, 46, 241)","<PINK>"))
class BaseTwoWayFigure():
def __init__(
self,
viz,
):
assert isinstance(viz, Visualizer)
self._dfv = viz
self._alt_selections = {}
@property
def scope(self):
return self._dfv.scope
@property
def selection(self):
return self._dfv.active_selection()
def _get_sectional_names(self, columns):
scope = self.scope
uncs = scope.get_uncertainty_names()
levs = scope.get_lever_names()
cons = scope.get_constant_names()
meas = scope.get_measure_names()
c_uncs = []
c_levs = []
c_cons = []
c_meas = []
c_other = []
for c in columns:
if c in uncs:
c_uncs.append(c)
elif c in levs:
c_levs.append(c)
elif c in cons:
c_cons.append(c)
elif c in meas:
c_meas.append(c)
else:
c_other.append(c)
result = []
if len(c_uncs):
result.append("-- (X) Uncertainties --")
result.extend(c_uncs)
if len(c_levs):
result.append("-- (L) Levers --")
result.extend(c_levs)
if len(c_cons):
result.append("-- Constants --")
result.extend(c_cons)
if len(c_meas):
result.append("-- (M) Measures --")
result.extend(c_meas)
if len(c_other):
result.append("-- Other --")
result.extend(c_other)
return result
def _get_shortname(self, name):
if self.scope is None:
return name
return self.scope.shortname(name)
def _manage_categorical(self, x, perturb=True):
try:
valid_scales = ['linear']
x_categories = None
x_ticktext = None
x_tickvals = None
if not isinstance(x, pandas.Series):
x = pandas.Series(x)
try:
if numpy.issubdtype(x.dtype, numpy.bool_):
x = x.astype('category')
except TypeError:
pass
if isinstance(x.dtype, pandas.CategoricalDtype):
x_categories = x.cat.categories
codes = x.cat.codes
x = codes.astype(float)
s_ = x.size * 0.01
s_ = s_ / (1 + s_)
epsilon = 0.05 + 0.20 * s_
if perturb:
x = x + numpy.random.uniform(-epsilon, epsilon, size=x.shape)
x_tickmode = 'array'
x_ticktext = list(x_categories)
x_tickvals = list(range(len(x_ticktext)))
else:
if x.min() >= 0:
valid_scales.append('log')
return x, x_ticktext, x_tickvals, valid_scales
except:
_logger.exception('ERROR IN _manage_categorical')
raise
[docs]class TwoWayFigure(HBox, BaseTwoWayFigure):
"""
The two-way visualizer widget.
This class encapsulates a TMIP-EMAT interactive widget
that displays a configurable two dimensional scatter
plot of Visualizer data. Dropdown menus allow the user
to change the data dimensions displayed on the x and
y axes, as well as create and manipulate new selection
sets directly from the interactive widget.
Args:
viz (emat.Visualizer):
The visualizer object to link for this two-way.
target_marker_opacity (numeric, default 1000):
The number of scatter point markers to display
fully opaque. If the number of markers displayed
exceeds this value, each marker is rendered
partially transparent, such that the total marker
weight (opacity * number of markers) is loosely
approximate to this total value.
minimum_marker_opacity (float, default 0.25):
This is the minimum marker opacity used,
notwithstanding any transparency level implied
by `target_marker_opacity`.
use_gl (bool, default True):
Use Plotly's `Scattergl` instead of `Scatter`, which
may provide some performance benefit for large data
sets.
"""
def __init__(
self,
viz,
target_marker_opacity=1000,
minimum_marker_opacity=0.25,
use_gl=True,
):
BaseTwoWayFigure.__init__(self, viz)
axis_choices = self._get_sectional_names(self._dfv.data.columns)
self.x_axis_choose = Dropdown(
options=axis_choices,
# description='X Axis',
value=self._dfv.data.columns[0],
)
self.x_axis_scale = Dropdown(
options=['linear' ,],
value='linear',
)
self.y_axis_choose = Dropdown(
options=axis_choices,
# description='Y Axis',
value=self._dfv.data.columns[-1],
)
self.y_axis_scale = Dropdown(
options=['linear' ,],
value='linear',
)
self.selection_choose = Dropdown(
options=list(self._dfv.selection_names()) + ['Expr'] + list(self._alt_selections.keys()),
value='None',
)
self.selection_edit_menu = Menu(
"Edit Selection...",
{
"Use Manual Selection":self._use_manual_selection,
}
)
self.selection_expr = Text(
value='True',
disabled=True,
)
self.selection_name = Text(
value='',
disabled=True,
)
self.axis_choose = VBox(
[
Label("X Axis"),
self.x_axis_choose,
self.x_axis_scale,
Label("Y Axis"),
self.y_axis_choose,
self.y_axis_scale,
Label("Selection"),
self._dfv._active_selection_chooser,
self.selection_edit_menu,
self.selection_name,
],
layout=dict(
overflow='hidden',
)
)
self.minimum_marker_opacity = minimum_marker_opacity
self.target_marker_opacity = target_marker_opacity
marker_opacity = self._compute_marker_opacity()
self._x_data_range = [0 ,1]
self._y_data_range = [0 ,1]
Scatter = go.Scattergl if use_gl else go.Scatter
self.scattergraph = Scatter(
x=None,
y=None,
mode = 'markers',
marker=dict(
opacity=marker_opacity[0],
color=None,
colorscale=[[0, colors.DEFAULT_BASE_COLOR], [1, colors.DEFAULT_HIGHLIGHT_COLOR]],
cmin=0,
cmax=1,
),
name='Cases',
)
self.x_hist = go.Histogram(
x=None,
# name='x density',
marker=dict(
color=colors.DEFAULT_BASE_COLOR,
# opacity=0.7,
),
yaxis='y2',
bingroup='xxx',
)
self.y_hist = go.Histogram(
y=None,
# name='y density',
marker=dict(
color=colors.DEFAULT_BASE_COLOR,
# opacity=0.7,
),
xaxis='x2',
bingroup='yyy',
)
self.x_hist_s = go.Histogram(
x=None,
marker=dict(
color=colors.DEFAULT_HIGHLIGHT_COLOR,
# opacity=0.7,
),
yaxis='y2',
bingroup='xxx',
)
self.y_hist_s = go.Histogram(
y=None,
marker=dict(
color=colors.DEFAULT_HIGHLIGHT_COLOR,
# opacity=0.7,
),
xaxis='x2',
bingroup='yyy',
)
self.graph = go.FigureWidget()
self.scattergraph = self.graph.add_trace(self.scattergraph).data[-1]
self.x_hist = self.graph.add_trace(self.x_hist).data[-1]
self.y_hist = self.graph.add_trace(self.y_hist).data[-1]
self.x_hist_s = self.graph.add_trace(self.x_hist_s).data[-1]
self.y_hist_s = self.graph.add_trace(self.y_hist_s).data[-1]
self.graph.layout =dict(
xaxis=dict(
domain=[0, 0.85],
showgrid=True,
# title=self._df.data.columns[0],
),
yaxis=dict(
domain=[0, 0.85],
showgrid=True,
# title=self._df.data.columns[-1],
),
xaxis2=dict(
domain=[0.85, 1],
showgrid=True,
zeroline=True,
zerolinecolor='#FFF',
zerolinewidth=4,
),
yaxis2=dict(
domain=[0.85, 1],
showgrid=True,
zeroline=True,
zerolinecolor='#FFF',
zerolinewidth=4,
),
barmode="overlay",
showlegend=False,
margin=dict(l=10, r=10, t=10, b=10),
dragmode="lasso",
)
self.x_axis_choose.observe(self._observe_change_column_x, names='value')
self.y_axis_choose.observe(self._observe_change_column_y, names='value')
self.x_axis_scale.observe(self._observe_change_scale_x, names='value')
self.y_axis_scale.observe(self._observe_change_scale_y, names='value')
self.selection_choose.observe(self._on_change_selection_choose, names='value')
self.selection_expr.observe(self._on_change_selection_expr, names='value')
self.set_x(self._dfv.data.columns[0], drawbox=False)
self.set_y(self._dfv.data.columns[-1])
self.scattergraph.meta = multiindex_to_strings(self._dfv.data.index)
if self._dfv.data.index.name:
hover_name = self._dfv.data.index.name
else:
hover_name = "Experiment"
self.scattergraph.hovertemplate = f"%{{xaxis.title.text}}: %{{x}}<br>%{{yaxis.title.text}}: %{{y}}<extra>{hover_name} %{{meta}}</extra>"
self.draw_box()
super().__init__(
[
self.graph,
self.axis_choose,
],
layout=dict(
align_items='center',
)
)
self.scattergraph.on_selection(self._on_select_points)
self.out_logger = Output()
self._update_state_ = set()
def _compute_marker_opacity(self):
# if self.selection is None:
# marker_opacity = 1.0
# if len(self._df.data) > self.target_marker_opacity:
# marker_opacity = self.target_marker_opacity / len(self._dfv.data)
# if marker_opacity < self.minimum_marker_opacity:
# marker_opacity = self.minimum_marker_opacity
# return marker_opacity, 1.0
# else:
marker_opacity = [1.0, 1.0]
n_selected = int(self._dfv.active_selection().sum())
n_unselect = len(self._dfv.data) - n_selected
if n_unselect > self.target_marker_opacity:
marker_opacity[0] = self.target_marker_opacity / n_unselect
if marker_opacity[0] < self.minimum_marker_opacity:
marker_opacity[0] = self.minimum_marker_opacity
if n_selected > self.target_marker_opacity:
marker_opacity[1] = self.target_marker_opacity / n_selected
if marker_opacity[1] < self.minimum_marker_opacity:
marker_opacity[1] = self.minimum_marker_opacity
return marker_opacity
@property
def _x_data_width(self):
w = self._x_data_range[1] - self._x_data_range[0]
if w > 0:
return w
return 1
@property
def _y_data_width(self):
w = self._y_data_range[1] - self._y_data_range[0]
if w > 0:
return w
return 1
def _observe_change_column_x(self, payload):
if payload['new'][:3] == '-- ' and payload['new'][-3:] == ' --':
# Just a heading, not a real option
payload['owner'].value = payload['old']
return
self.set_x(payload['new'])
def _observe_change_column_y(self, payload):
if payload['new'][:3] == '-- ' and payload['new'][-3:] == ' --':
# Just a heading, not a real option
payload['owner'].value = payload['old']
return
self.set_y(payload['new'])
def _observe_change_scale_x(self, payload):
self.set_x(self.x_axis_choose.value)
def _observe_change_scale_y(self, payload):
self.set_y(self.y_axis_choose.value)
def set_x(self, col, drawbox=True):
"""
Set the new X axis data.
Args:
col (str or array-like):
The name of the new `x` column in `df`, or a
computed array or pandas.Series of values.
"""
try:
with self.graph.batch_update():
this_label = None
if isinstance(col, str):
x = self._dfv.data[col]
this_label = self._get_shortname(col)
else:
x = col
try:
this_label = self._get_shortname(col.name)
except:
pass
x, x_ticktext, x_tickvals, x_scales = self._manage_categorical(x)
self.x_axis_scale.options = x_scales
self._x = x
self._x_ticktext = x_ticktext or []
self._x_tickvals = x_tickvals or []
is_linear = (self.x_axis_scale.value == 'linear')
is_log = (self.x_axis_scale.value == 'log')
if this_label is not None:
self.graph.layout.xaxis.title = this_label
self.x_hist.name = this_label +' Unselected Density'
self.x_hist_s.name = this_label +' Selected Density'
else:
self.graph.layout.xaxis.title = 'x axis'
self.x_hist.name = 'Unselected Density'
self.x_hist_s.name = 'Selected Density'
# if self.selection is None:
# self.scattergraph.x = x
# self.x_hist.x = x if is_linear else None
# self.x_hist_s.x = None
# else:
self.scattergraph.x = x
self.x_hist.x = x if is_linear else None
self.x_hist_s.x = x[self.selection] if is_linear else None
if x_ticktext is not None:
self._x_data_range = [x.min(), x.max()]
self.graph.layout.xaxis.range = (
self._x_data_range[0] - 0.3,
self._x_data_range[1] + 0.3,
)
self.graph.layout.xaxis.tickmode = 'array'
self.graph.layout.xaxis.ticktext = x_ticktext
self.graph.layout.xaxis.tickvals = x_tickvals
self.x_hist.xbins = dict(
start=-0.25, end=self._x_data_range[1] + 0.25, size=0.5,
)
self.x_hist.autobinx = False
else:
self._x_data_range = [x.min(), x.max()]
self.graph.layout.xaxis.range = (
self._x_data_range[0] - self._x_data_width * 0.07,
self._x_data_range[1] + self._x_data_width * 0.07,
)
self.graph.layout.xaxis.type = self.x_axis_scale.value
self.graph.layout.xaxis.tickmode = None
self.graph.layout.xaxis.ticktext = None
self.graph.layout.xaxis.tickvals = None
self.x_hist.xbins = None
self.x_hist.autobinx = True
if drawbox:
self.draw_box()
except:
_logger.exception('ERROR IN DataFrameViewer.set_x')
raise
def set_y(self, col):
"""
Set the new Y axis data.
Args:
col (str or array-like):
The name of the new `y` column in `df`, or a
computed array or pandas.Series of values.
"""
with self.graph.batch_update():
this_label = None
if isinstance(col, str):
y = self._dfv.data[col]
this_label = self._get_shortname(col)
else:
y = col
try:
this_label = self._get_shortname(col.name)
except:
pass
y, y_ticktext, y_tickvals, y_scales = self._manage_categorical(y)
self.y_axis_scale.options = y_scales
self._y = y
self._y_ticktext = y_ticktext or []
self._y_tickvals = y_tickvals or []
is_linear = (self.y_axis_scale.value == 'linear')
is_log = (self.y_axis_scale.value == 'log')
if this_label is not None:
self.graph.layout.yaxis.title = this_label
self.y_hist.name = this_label +' Unselected Density'
self.y_hist_s.name = this_label +' Selected Density'
else:
self.graph.layout.yaxis.title = 'y axis'
self.y_hist.name = 'Unselected Density'
self.y_hist_s.name = 'Selected Density'
if self.selection is None:
self.scattergraph.y = y
self.y_hist.y = y if is_linear else None
self.y_hist_s.y = None
else:
self.scattergraph.y = y
self.y_hist.y = y if is_linear else None
self.y_hist_s.y = y[self.selection] if is_linear else None
if y_ticktext is not None:
self._y_data_range = [y.min(), y.max()]
self.graph.layout.yaxis.range = (
self._y_data_range[0] - 0.3,
self._y_data_range[1] + 0.3,
)
self.graph.layout.yaxis.tickmode = 'array'
self.graph.layout.yaxis.ticktext = y_ticktext
self.graph.layout.yaxis.tickvals = y_tickvals
self.y_hist.ybins = dict(
start=-0.25, end=self._y_data_range[1] + 0.25, size=0.5,
)
self.y_hist.autobiny = False
else:
self._y_data_range = [y.min(), y.max()]
self.graph.layout.yaxis.range = (
self._y_data_range[0] - self._y_data_width * 0.07,
self._y_data_range[1] + self._y_data_width * 0.07,
)
self.graph.layout.yaxis.type = self.y_axis_scale.value
self.graph.layout.yaxis.tickmode = None
self.graph.layout.yaxis.ticktext = None
self.graph.layout.yaxis.tickvals = None
self.y_hist.ybins = None
self.y_hist.autobiny = True
self.draw_box()
def change_selection_color(self, new_color=None):
if new_color is None:
new_color = colors.DEFAULT_HIGHLIGHT_COLOR
with self.graph.batch_update():
self.scattergraph.marker.colorscale = [[0, colors.DEFAULT_BASE_COLOR], [1, new_color]]
self.x_hist_s.marker.color = new_color
self.y_hist_s.marker.color = new_color
def change_selection(self, new_selection):
if new_selection is None:
self.selection = None
# Update Selected Portion of Scatters
x = self._x
y = self._y
with self.graph.batch_update():
self.scattergraph.x = x
self.scattergraph.y = y
self.scattergraph.marker.color = numpy.zeros(x.shape, numpy.int8)
marker_opacity = self._compute_marker_opacity()
self.scattergraph.marker.opacity = marker_opacity[0]
# Update Selected Portion of Histograms
self.x_hist_s.x = None
self.y_hist_s.y = None
self.draw_box()
return
if new_selection.size != len(self._dfv.data):
raise ValueError(f"new selection size ({new_selection.size}) "
f"does not match length of data ({len(self._dfv.data)})")
#self.selection = new_selection
with self.graph.batch_update():
# Update Selected Portion of Scatters
x = self._x
y = self._y
self.scattergraph.x = x # [~self.selection]
self.scattergraph.y = y # [~self.selection]
selection_as_int = self.selection.astype(int)
self.scattergraph.marker.color = selection_as_int
marker_opacity = self._compute_marker_opacity()
self.scattergraph.marker.opacity = [marker_opacity[j] for j in selection_as_int]
# Update Selected Portion of Histograms
self.x_hist_s.x = x[self.selection]
self.y_hist_s.y = y[self.selection]
self.draw_box()
def draw_box(self):
from ...scope.box import Bounds
x_label = self.x_axis_choose.value
y_label = self.y_axis_choose.value
background_shapes, foreground_shapes = [], []
box = None
if self._dfv.active_selection_deftype() == 'box':
box = self._dfv._selection_defs[self._dfv.active_selection_name()]
elif self._dfv.active_selection_deftype() == 'primbox':
box = self._dfv._selection_defs[self._dfv.active_selection_name()].to_emat_box()
if box is not None:
if x_label in box.thresholds or y_label in box.thresholds:
x_lo, x_hi = None, None
thresh = box.thresholds.get(x_label)
if isinstance(thresh, Bounds):
x_lo, x_hi = thresh
if isinstance(thresh, set):
x_lo, x_hi = [], []
for tickval, ticktext in zip(self._x_tickvals, self._x_ticktext):
if ticktext in thresh:
x_lo.append(tickval -0.3)
x_hi.append(tickval +0.3)
if x_lo is None:
x_lo = self._x.min( ) -self._x_data_width * 0.02
if x_hi is None:
x_hi = self._x.max( ) +self._x_data_width * 0.02
if not isinstance(x_lo, list):
x_lo = [x_lo]
if not isinstance(x_hi, list):
x_hi = [x_hi]
y_lo, y_hi = None, None
thresh = box.thresholds.get(y_label)
if isinstance(thresh, Bounds):
y_lo, y_hi = thresh
if isinstance(thresh, set):
y_lo, y_hi = [], []
for tickval, ticktext in zip(self._y_tickvals, self._y_ticktext):
if ticktext in thresh:
y_lo.append(tickval -0.3)
y_hi.append(tickval +0.3)
if y_lo is None:
y_lo = self._y.min( ) -self._y_data_width * 0.02
if y_hi is None:
y_hi = self._y.max( ) +self._y_data_width * 0.02
if not isinstance(y_lo, list):
y_lo = [y_lo]
if not isinstance(y_hi, list):
y_hi = [y_hi]
x_pairs = list(zip(x_lo, x_hi))
y_pairs = list(zip(y_lo, y_hi))
background_shapes += [
# Rectangle background color
go.layout.Shape(
type="rect",
xref="x1",
yref="y1",
x0=x_pair[0],
y0=y_pair[0],
x1=x_pair[1],
y1=y_pair[1],
line=dict(
width=0,
),
fillcolor=colors.DEFAULT_BOX_BG_COLOR,
opacity=0.2,
layer="below",
)
for x_pair in x_pairs
for y_pair in y_pairs
]
foreground_shapes += [
# Rectangle reference to the axes
go.layout.Shape(
type="rect",
xref="x1",
yref="y1",
x0=x_pair[0],
y0=y_pair[0],
x1=x_pair[1],
y1=y_pair[1],
line=dict(
width=1,
color=colors.DEFAULT_BOX_LINE_COLOR,
),
fillcolor='rgba(0,0,0,0)',
opacity=1.0,
)
for x_pair in x_pairs
for y_pair in y_pairs
]
x_refpoint = self._dfv.reference_point(x_label)
y_refpoint = self._dfv.reference_point(y_label)
if x_refpoint is not None:
_y_lo = self._y.min( ) -self._y_data_width * 0.02
_y_hi = self._y.max( ) +self._y_data_width * 0.02
foreground_shapes.append(
go.layout.Shape(
type="line",
xref="x1",
yref="y1",
y0=_y_lo,
x0=x_refpoint,
y1=_y_hi,
x1=x_refpoint,
**colors.DEFAULT_REF_LINE_STYLE,
)
)
if y_refpoint is not None:
_x_lo = self._x.min( ) -self._x_data_width * 0.02
_x_hi = self._x.max( ) +self._x_data_width * 0.02
foreground_shapes.append(
go.layout.Shape(
type="line",
xref="x1",
yref="y1",
x0=_x_lo,
y0=y_refpoint,
x1=_x_hi,
y1=y_refpoint,
**colors.DEFAULT_REF_LINE_STYLE,
)
)
self.graph.layout.shapes = background_shapes +foreground_shapes
def _selection_eval(self, txt):
df = self._dfv.data.rename(columns={i: clean_name(i) for i in self._dfv.data.columns})
return df.eval(txt).astype(bool)
def _use_manual_selection(self):
if 'Use Manual Target' not in self._alt_selections:
return
lasso_target = self._alt_selections['Use Manual Target']
del self._alt_selections['Use Manual Target']
lasso_select_name = self.selection_name.value
self._dfv.new_selection(lasso_target, lasso_select_name, color=colors.DEFAULT_LASSO_COLOR, activate=True)
self.refresh_selection_names()
self.selection_name.disabled = True
self.selection_name.value = ""
self._clear_selection()
def _on_change_selection_choose(self, payload):
if "on_change_selection_choose" in self._update_state_: return
if "refresh_selection_names" in self._update_state_: return
self._update_state_.add("on_change_selection_choose")
try:
with self.graph.batch_update():
if payload['new'] != 'Expr':
self.selection_expr.disabled = True
if payload['new'] == 'Expr':
self.selection_expr.disabled = False
self._on_change_selection_expr({'new' :self.selection_expr.value})
elif payload['new'] in (self._dfv.selection_names()):
self._dfv.set_active_selection_name(payload['new'])
self.change_selection(self._dfv.active_selection())
elif payload['new'] == 'Use Manual Target':
lasso_target = self._alt_selections['Use Manual Target']
del self._alt_selections['Use Manual Target']
lasso_select_name = self.selection_name.value
self._dfv.new_selection(lasso_target, lasso_select_name, color=colors.DEFAULT_LASSO_COLOR)
self.add_alt_selection(lasso_select_name, None)
self.selection_choose.value = lasso_select_name
self.selection_name.disabled = True
self.selection_name.value = ""
self._clear_selection()
self._dfv.set_active_selection_name(lasso_select_name)
self.change_selection(self._dfv.active_selection())
else:
self.change_selection(self._alt_selections[payload['new']])
finally:
self._update_state_.discard("on_change_selection_choose")
def _clear_selection(self):
self.scattergraph.selectedpoints = None
self.x_hist.selectedpoints = None
self.x_hist_s.selectedpoints = None
self.y_hist.selectedpoints = None
self.y_hist_s.selectedpoints = None
def _on_change_selection_expr(self, payload):
expression = payload['new']
try:
sel = self._selection_eval(expression)
except Exception as err:
# print("FAILED ON EVAL\n",expression, err)
pass
else:
try:
self.change_selection(sel)
except Exception as err:
# print("FAILED ON SETTING\n", expression, err)
pass
else:
# print("PASSED", expression)
pass
def add_alt_selection(self, key, value):
"""
Add a new possible alternative selection vector.
Parameters
----------
key : str
An identifier for the new selection vector.
value : array-like of bool
A vector of boolean values giving the selection.
"""
if value is not None:
if value.size != len(self._dfv.data):
raise ValueError(f"value size ({value.size}) "
f"does not match length of data ({len(self._dfv.data)})")
self._alt_selections[key] = value
self.refresh_selection_names()
def lasso_selected_indexes(self):
"""
Get the index values of points selected interactively in the figure.
Returns
-------
pandas.Index
"""
return self._dfv.data.index[list(self.scattergraph.selectedpoints)]
def lasso_selected_data(self):
"""
Get the full data of points selected interactively in the figure.
Returns
-------
pandas.DataFrame
"""
return self._dfv.data.loc[self.lasso_selected_indexes()]
def lasso_selected(self):
"""
Get a boolean array indicating points selected interactively in the figure.
Returns
-------
pandas.Series
"""
s = pandas.Series(data=False, index=self._dfv.data.index)
s.iloc[list(self.scattergraph.selectedpoints)] = True
return s
def _on_select_points(self, trace, points, selector):
# callback function for selection
s = pandas.Series(data=False, index=self._dfv.data.index)
for i in points.point_inds:
s.iloc[i] = True
# with self.out_logger:
# print(points)
# print(selector)
self.add_alt_selection("Use Manual Target", s)
self.selection_name.disabled = False
if isinstance(selector, LassoSelector):
default_name = "Lasso Target"
else:
default_name = "Rectangular Target"
if self.selection_name.value == "":
self.selection_name.value = default_name
tentative_name = self.selection_name.value
n=2
while tentative_name in self._dfv.selection_names():
tentative_name = f"{default_name} {n}"
n += 1
self.selection_name.value = tentative_name
def refresh_selection_names(self):
if "refresh_selection_names" in self._update_state_: return
self._update_state_.add("refresh_selection_names")
try:
cache = self.selection_choose.value
self.selection_choose.options = list(self._dfv.selection_names()) + ['Expr'] + list(self._alt_selections.keys())
self.selection_choose.value = self._dfv.active_selection_name()
try:
self.selection_choose.value = cache
except:
pass
finally:
self._update_state_.discard("refresh_selection_names")