Source code for pandas_visual_analysis.widgets.histogram
import ipywidgets as widgets
import plotly.graph_objs as go
from pandas_visual_analysis import DataSource
from pandas_visual_analysis.utils.config import Config
from pandas_visual_analysis.widgets import BaseWidget, register_widget
[docs]@register_widget
class HistogramWidget(BaseWidget):
"""
The HistogramWidget displays a single dimension of the data as a histogram where the brush selection
is overlaid to see the distribution of both the underlying data and the selection.
"""
def __init__(
self,
data_source: DataSource,
row: int,
index: int,
relative_size: float,
max_height: int,
):
"""
:param data_source: :class:`pandas_visual_analysis.data_source.DataSource` for the widget.
:param row: The row the widget is in.
:param index: Index of the row the widget is in.
:param relative_size: The space the widget has in a row which is then converted to the width. (e.g. 0.33 => 33%)
:param max_height: height in pixels the plot has to have
"""
super().__init__(data_source, row, index, relative_size, max_height)
self.columns = (
data_source.numerical_columns
+ data_source.time_columns
+ data_source.categorical_columns
)
self.column_select = widgets.Dropdown(
options=self.columns,
value=self.data_source.column_store.next_prefer_numerical(),
description="Column:",
)
self.normalize = widgets.Checkbox(
value=False, description="Normalize", indent=False
)
self.data = self.data_source.data
self.brushed_data = self.data_source.brushed_data
self.figure_widget = self._get_figure_widget()
self.set_observers()
self.column_select.observe(handler=self._on_column_change, names="value")
self.normalize.observe(handler=self._on_normalize_change, names="value")
self.figure_widget.data[0].on_deselect(callback=self.on_deselection)
[docs] def build(self) -> widgets.Widget:
root = widgets.VBox(
[widgets.HBox([self.column_select, self.normalize]), self.figure_widget]
)
return self.apply_size_constraints(root)
[docs] def observe_brush_indices_change(self, sender):
self.brushed_data = self.data_source.brushed_data
if len(self.brushed_data) == len(self.data):
self.figure_widget.data[0].visible = False
else:
self.figure_widget.data[0].visible = True
# empty selection for histogram does not work
if len(self.brushed_data) == 0:
self.figure_widget.data[1].visible = False
else:
self.figure_widget.data[1].visible = True
self.figure_widget.data[
1
].selectedpoints = (
self.data_source.brushed_indices
) # set selected points so that double click works
self._redraw_plot()
# issue: selection does not work for histogram: https://github.com/plotly/plotly.py/issues/2698
def _on_column_change(self, change):
self._redraw_plot(only_brushed=False)
def _on_normalize_change(self, change):
use_norm = self.normalize.value
hist_norm = "probability" if use_norm else ""
with self.figure_widget.batch_update():
self.figure_widget.data[0].histnorm = hist_norm
self.figure_widget.data[1].histnorm = hist_norm
def _get_figure_widget(self):
return go.FigureWidget(self._get_histograms())
def _get_histograms(self):
col = self.column_select.value
config = Config()
fig = go.Figure(layout=go.Layout(margin=dict(l=5, r=5, b=5, t=5, pad=2)))
fig.add_trace(
go.Histogram(
x=self.data[col],
opacity=max(config.alpha, 0.75),
marker={"color": "rgb(%d,%d,%d)" % config.deselect_color},
selected={"marker": {"color": "rgb(%d,%d,%d)" % config.deselect_color}},
unselected={"marker": {"opacity": 0.4}},
hoverinfo="skip",
histnorm="",
bingroup=1,
)
)
fig.add_trace(
go.Histogram(
x=self.brushed_data[col],
opacity=1.0,
# mode='markers',
marker={"color": "rgb(%d,%d,%d)" % config.select_color},
selected={"marker": {"color": "rgb(%d,%d,%d)" % config.select_color}},
unselected={"marker": {"opacity": 1.0}},
hoverinfo="skip",
histnorm="",
bingroup=1,
)
)
fig.update_layout(barmode="overlay", showlegend=False, dragmode="select")
return fig
def _redraw_plot(self, only_brushed=True):
col = self.column_select.value
with self.figure_widget.batch_update():
self.figure_widget.data[1].x = self.brushed_data[col]
if not only_brushed:
self.figure_widget.data[0].x = self.data[col]