Example #1
0
    def __init__(self, master, parent=None):
        self.selection = []
        self.master: OWBarPlot = master
        self.state: int = SELECT
        self.bar_item: pg.BarGraphItem = None
        super().__init__(
            parent=parent,
            viewBox=BarPlotViewBox(self),
            background="w", enableMenu=False,
            axisItems={"bottom": AxisItem(orientation="bottom",
                                          rotate_ticks=True),
                       "left": AxisItem(orientation="left")}
        )
        self.hideAxis("left")
        self.hideAxis("bottom")
        self.getPlotItem().buttonsHidden = True
        self.getPlotItem().setContentsMargins(10, 0, 0, 10)
        self.getViewBox().setMouseMode(pg.ViewBox.PanMode)

        self.group_axis = AxisItem("bottom")
        self.group_axis.hide()
        self.group_axis.linkToView(self.getViewBox())
        self.getPlotItem().layout.addItem(self.group_axis, 4, 1)

        self.legend = self._create_legend()

        self.tooltip_delegate = HelpEventDelegate(self.help_event)
        self.scene().installEventFilter(self.tooltip_delegate)

        self.parameter_setter = ParameterSetter(self)

        self.showGrid(y=self.parameter_setter.DEFAULT_SHOW_GRID,
                      alpha=self.parameter_setter.DEFAULT_ALPHA_GRID / 255)
Example #2
0
 def axis_view(orientation):
     ax = AxisItem(orientation=orientation, maxTickLength=7)
     ax.mousePressed.connect(self._activate_cut_line)
     ax.mouseMoved.connect(self._activate_cut_line)
     ax.mouseReleased.connect(self._activate_cut_line)
     ax.setRange(1.0, 0.0)
     return ax
    def __init__(self, parent: OWWidget):
        self.__data_bounds: Optional[Tuple[Tuple[float, float],
                                           Tuple[float, float]]] = None
        self.__pos_labels: Optional[List[str]] = None
        self.__neg_labels: Optional[List[str]] = None
        self.__tooltip_data: Optional[Table] = None
        self.__show_tooltips = True
        self.__highlight_feature = True
        self.__mouse_pressed = False

        self.__fill_items: List[pg.FillBetweenItem] = []
        self.__text_items: List[pg.TextItem] = []
        self.__dot_items: List[pg.ScatterPlotItem] = []
        self.__vertical_line_item: Optional[pg.InfiniteLine] = None
        self.__selection: List = []
        self.__selection_rect_items: List[SelectionRect] = []

        view_box = ForcePlotViewBox()
        view_box.sigSelectionChanged.connect(self._update_selection)
        view_box.sigDeselect.connect(self._deselect)
        view_box.sigRangeChangedManually.connect(self.__on_range_changed_man)
        view_box.sigRangeChanged.connect(self.__on_range_changed)

        super().__init__(parent, viewBox=view_box,
                         background="w", enableMenu=False,
                         axisItems={"bottom": AxisItem("bottom", True),
                                    "left": AxisItem("left")})
        self.setAntialiasing(True)
        self.getPlotItem().setContentsMargins(10, 10, 10, 10)
        self.getPlotItem().buttonsHidden = True
        self.getPlotItem().scene().sigMouseMoved.connect(self.__on_mouse_moved)

        self.parameter_setter = ParameterSetter(self)
Example #4
0
 def _clear_axes(self):
     self.setAxisItems({
         "bottom": AxisItem(orientation="bottom"),
         "left": AxisItem(orientation="left")
     })
     Updater.update_axes_titles_font(
         self.parameter_setter.axis_items,
         **self.parameter_setter.titles_settings)
     Updater.update_axes_ticks_font(self.parameter_setter.axis_items,
                                    **self.parameter_setter.ticks_settings)
     self.getAxis("bottom").setRotateTicks(
         self.parameter_setter.is_vertical_setting)
Example #5
0
 def __init__(self, parent):
     self.groups: List[ProfileGroup] = []
     self.bottom_axis = BottomAxisItem(orientation="bottom")
     self.bottom_axis.setLabel("")
     left_axis = AxisItem(orientation="left")
     left_axis.setLabel("")
     super().__init__(parent, viewBox=LinePlotViewBox(),
                      background="w", enableMenu=False,
                      axisItems={"bottom": self.bottom_axis,
                                 "left": left_axis})
     self.view_box = self.getViewBox()
     self.selection = set()
     self.legend = self._create_legend(((1, 0), (1, 0)))
     self.getPlotItem().buttonsHidden = True
     self.setRenderHint(QPainter.Antialiasing, True)
Example #6
0
    def __init__(self, parent: OWWidget, kernel: str, scale: int,
                 orientation: Qt.Orientations, show_box_plot: bool,
                 show_strip_plot: bool, show_rug_plot: bool, show_grid: bool,
                 sort_items: bool):

        # data
        self.__values: Optional[np.ndarray] = None
        self.__value_var: Optional[ContinuousVariable] = None
        self.__group_values: Optional[np.ndarray] = None
        self.__group_var: Optional[DiscreteVariable] = None

        # settings
        self.__kernel = kernel
        self.__scale = scale
        self.__orientation = orientation
        self.__show_box_plot = show_box_plot
        self.__show_strip_plot = show_strip_plot
        self.__show_rug_plot = show_rug_plot
        self.__show_grid = show_grid
        self.__sort_items = sort_items

        # items
        self.__violin_items: List[ViolinItem] = []
        self.__box_items: List[BoxItem] = []
        self.__median_items: List[MedianItem] = []
        self.__strip_items: List[pg.ScatterPlotItem] = []

        # selection
        self.__selection: Set[int] = set()
        self.__selection_rects: List[SelectionRect] = []

        view_box = ViolinPlotViewBox(self)
        super().__init__(parent,
                         viewBox=view_box,
                         enableMenu=False,
                         axisItems={
                             "bottom": AxisItem("bottom"),
                             "left": AxisItem("left")
                         })
        self.setAntialiasing(True)
        self.hideButtons()
        self.getPlotItem().setContentsMargins(10, 10, 10, 10)
        self.setMouseEnabled(False, False)
        view_box.sigSelectionChanged.connect(self._update_selection)
        view_box.sigDeselect.connect(self._deselect)

        self.parameter_setter = ParameterSetter(self)
    def __init__(self):
        super().__init__()
        self.__violin_column_width = self.VIOLIN_COLUMN_WIDTH  # type: int
        self.__range = None  # type: Optional[Tuple[float, float]]
        self.__violin_items = []  # type: List[ViolinItem]
        self.__variable_items = []  # type: List[VariableItem]
        self.__bottom_axis = AxisItem(parent=self, orientation="bottom",
                                      maxTickLength=7, pen=QPen(Qt.black))
        self.__bottom_axis.setLabel("Impact on model output")
        self.__vertical_line = QGraphicsLineItem(self.__bottom_axis)
        self.__vertical_line.setPen(QPen(Qt.gray))
        self.__legend = Legend(self)

        self.__layout = QGraphicsGridLayout()
        self.__layout.addItem(self.__legend, 0, ViolinPlot.LEGEND_COLUMN)
        self.__layout.setVerticalSpacing(0)
        self.setLayout(self.__layout)

        self.parameter_setter = ParameterSetter(self)
Example #8
0
    def __init__(self):
        super().__init__()
        self._item_column_width = self.ITEM_COLUMN_WIDTH
        self._range: Optional[Tuple[float, float]] = None
        self._items: List[FeatureItem] = []
        self._variable_items: List[VariableItem] = []
        self._bottom_axis = AxisItem(parent=self,
                                     orientation="bottom",
                                     maxTickLength=7,
                                     pen=QPen(Qt.black))
        self._bottom_axis.setLabel(self.BOTTOM_AXIS_LABEL)
        self._vertical_line = QGraphicsLineItem(self._bottom_axis)
        self._vertical_line.setPen(QPen(Qt.gray))

        self._layout = QGraphicsGridLayout()
        self._layout.setVerticalSpacing(0)
        self.setLayout(self._layout)

        self.parameter_setter = BaseParameterSetter(self)
Example #9
0
    def __init__(self):
        super().__init__()

        self.results = None
        self.scores = None
        self.classifier_names = []
        self.colors = []
        self.line = None

        self._last_score_value = -1

        box = gui.vBox(self.controlArea, box="Settings")
        self.target_cb = gui.comboBox(box,
                                      self,
                                      "target_index",
                                      label="Target:",
                                      orientation=Qt.Horizontal,
                                      callback=self.target_index_changed,
                                      contentsLength=8,
                                      searchable=True)
        gui.checkBox(box,
                     self,
                     "display_rug",
                     "Show rug",
                     callback=self._on_display_rug_changed)
        gui.checkBox(box,
                     self,
                     "fold_curves",
                     "Curves for individual folds",
                     callback=self._replot)

        self.classifiers_list_box = gui.listBox(
            self.controlArea,
            self,
            "selected_classifiers",
            "classifier_names",
            box="Classifier",
            selectionMode=QListWidget.ExtendedSelection,
            sizePolicy=(QSizePolicy.Preferred, QSizePolicy.Preferred),
            sizeHint=QSize(150, 40),
            callback=self._on_selection_changed)

        box = gui.vBox(self.controlArea, "Metrics")
        combo = gui.comboBox(box,
                             self,
                             "score",
                             items=(metric.name for metric in Metrics),
                             callback=self.score_changed)

        self.explanation = gui.widgetLabel(box,
                                           wordWrap=True,
                                           fixedWidth=combo.sizeHint().width())
        self.explanation.setContentsMargins(8, 8, 0, 0)
        font = self.explanation.font()
        font.setPointSizeF(0.85 * font.pointSizeF())
        self.explanation.setFont(font)

        gui.radioButtons(box,
                         self,
                         value="output_calibration",
                         btnLabels=("Sigmoid calibration",
                                    "Isotonic calibration"),
                         label="Output model calibration",
                         callback=self.apply)

        self.info_box = gui.widgetBox(self.controlArea, "Info")
        self.info_label = gui.widgetLabel(self.info_box)

        gui.auto_apply(self.controlArea,
                       self,
                       "auto_commit",
                       commit=self.apply)

        self.plotview = pg.GraphicsView(background="w")
        axes = {
            "bottom": AxisItem(orientation="bottom"),
            "left": AxisItem(orientation="left")
        }
        self.plot = pg.PlotItem(enableMenu=False, axisItems=axes)
        self.plot.parameter_setter = ParameterSetter(self.plot)
        self.plot.setMouseEnabled(False, False)
        self.plot.hideButtons()

        for axis_name in ("bottom", "left"):
            axis = self.plot.getAxis(axis_name)
            axis.setPen(pg.mkPen(color=0.0))
            # Remove the condition (that is, allow setting this for bottom
            # axis) when pyqtgraph is fixed
            # Issue: https://github.com/pyqtgraph/pyqtgraph/issues/930
            # Pull request: https://github.com/pyqtgraph/pyqtgraph/pull/932
            if axis_name != "bottom":  # remove if when pyqtgraph is fixed
                axis.setStyle(stopAxisAtTick=(True, True))

        self.plot.setRange(xRange=(0.0, 1.0), yRange=(0.0, 1.0), padding=0.05)
        self.plotview.setCentralItem(self.plot)

        self.mainArea.layout().addWidget(self.plotview)
        self._set_explanation()

        VisualSettingsDialog(self, self.plot.parameter_setter.initial_settings)
Example #10
0
class BarPlotGraph(PlotWidget):
    selection_changed = Signal(list)
    bar_width = 0.7

    def __init__(self, master, parent=None):
        self.selection = []
        self.master: OWBarPlot = master
        self.state: int = SELECT
        self.bar_item: pg.BarGraphItem = None
        super().__init__(
            parent=parent,
            viewBox=BarPlotViewBox(self),
            enableMenu=False,
            axisItems={
                "bottom": AxisItem(orientation="bottom", rotate_ticks=True),
                "left": AxisItem(orientation="left"),
            },
        )
        self.hideAxis("left")
        self.hideAxis("bottom")
        self.getPlotItem().buttonsHidden = True
        self.getPlotItem().setContentsMargins(10, 0, 0, 10)
        self.getViewBox().setMouseMode(pg.ViewBox.PanMode)

        self.group_axis = AxisItem("bottom")
        self.group_axis.hide()
        self.group_axis.linkToView(self.getViewBox())
        self.getPlotItem().layout.addItem(self.group_axis, 4, 1)

        self.legend = self._create_legend()

        self.tooltip_delegate = HelpEventDelegate(self.help_event)
        self.scene().installEventFilter(self.tooltip_delegate)

        self.parameter_setter = ParameterSetter(self)

        self.showGrid(
            y=self.parameter_setter.DEFAULT_SHOW_GRID,
            alpha=self.parameter_setter.DEFAULT_ALPHA_GRID / 255,
        )

    def _create_legend(self):
        legend = LegendItem()
        legend.setParentItem(self.getViewBox())
        legend.anchor((1, 0), (1, 0), offset=(-3, 1))
        legend.hide()
        return legend

    def update_legend(self):
        self.legend.clear()
        self.legend.hide()
        for color, text in self.master.get_legend_data():
            dot = pg.ScatterPlotItem(pen=pg.mkPen(color=color),
                                     brush=pg.mkBrush(color=color))
            self.legend.addItem(dot, escape(text))
            self.legend.show()
        Updater.update_legend_font(self.legend.items,
                                   **self.parameter_setter.legend_settings)

    def reset_graph(self):
        self.clear()
        self.update_bars()
        self.update_axes()
        self.update_group_lines()
        self.update_legend()
        self.reset_view()

    def update_bars(self):
        if self.bar_item is not None:
            self.removeItem(self.bar_item)
            self.bar_item = None

        values = self.master.get_values()
        if values is None:
            return

        self.bar_item = pg.BarGraphItem(
            x=np.arange(len(values)),
            height=values,
            width=self.bar_width,
            pen=pg.mkPen(QColor(Qt.white)),
            labels=self.master.get_labels(),
            brushes=self.master.get_colors(),
        )
        self.addItem(self.bar_item)
        self.__select_bars()

    def update_axes(self):
        if self.bar_item is not None:
            self.showAxis("left")
            self.showAxis("bottom")
            self.group_axis.show()

            vals_label, group_label, annot_label = self.master.get_axes()
            self.setLabel(axis="left", text=vals_label)
            self.setLabel(axis="bottom", text=annot_label)
            self.group_axis.setLabel(group_label)

            ticks = [list(enumerate(self.master.get_labels()))]
            self.getAxis("bottom").setTicks(ticks)

            labels = np.array(self.master.get_group_labels())
            _, indices, counts = np.unique(labels,
                                           return_index=True,
                                           return_counts=True)
            ticks = [[(i + (c - 1) / 2, labels[i])
                      for i, c in zip(indices, counts)]]
            self.group_axis.setTicks(ticks)

            if not group_label:
                self.group_axis.hide()
            elif not annot_label:
                self.hideAxis("bottom")
        else:
            self.hideAxis("left")
            self.hideAxis("bottom")
            self.group_axis.hide()

    def reset_view(self):
        if self.bar_item is None:
            return
        values = np.append(self.bar_item.opts["height"], 0)
        min_ = np.nanmin(values)
        max_ = -min_ + np.nanmax(values)
        rect = QRectF(-0.5, min_, len(values) - 1, max_)
        self.getViewBox().setRange(rect)

    def zoom_button_clicked(self):
        self.state = ZOOMING
        self.getViewBox().setMouseMode(pg.ViewBox.RectMode)

    def pan_button_clicked(self):
        self.state = PANNING
        self.getViewBox().setMouseMode(pg.ViewBox.PanMode)

    def select_button_clicked(self):
        self.state = SELECT
        self.getViewBox().setMouseMode(pg.ViewBox.RectMode)

    def reset_button_clicked(self):
        self.reset_view()

    def update_group_lines(self):
        if self.bar_item is None:
            return

        labels = np.array(self.master.get_group_labels())
        if labels is None or len(labels) == 0:
            return

        _, indices = np.unique(labels, return_index=True)
        offset = self.bar_width / 2 + (1 - self.bar_width) / 2
        for index in sorted(indices)[1:]:
            line = pg.InfiniteLine(pos=index - offset, angle=90)
            self.addItem(line)

    def select_by_rectangle(self, rect: QRectF):
        if self.bar_item is None:
            return

        x0, x1 = sorted((rect.topLeft().x(), rect.bottomRight().x()))
        y0, y1 = sorted((rect.topLeft().y(), rect.bottomRight().y()))
        x = self.bar_item.opts["x"]
        height = self.bar_item.opts["height"]
        d = self.bar_width / 2
        # positive bars
        mask = (x0 <= x + d) & (x1 >= x - d) & (y0 <= height) & (y1 > 0)
        # negative bars
        mask |= (x0 <= x + d) & (x1 >= x - d) & (y0 <= 0) & (y1 > height)
        self.select_by_indices(list(np.flatnonzero(mask)))

    def select_by_click(self, p: QPointF):
        if self.bar_item is None:
            return

        index = self.__get_index_at(p)
        self.select_by_indices([index] if index is not None else [])

    def __get_index_at(self, p: QPointF):
        x = p.x()
        index = round(x)
        heights = self.bar_item.opts["height"]
        if 0 <= index < len(heights) and abs(x - index) <= self.bar_width / 2:
            height = heights[index]  # pylint: disable=unsubscriptable-object
            if 0 <= p.y() <= height or height <= p.y() <= 0:
                return index
        return None

    def select_by_indices(self, indices: List):
        keys = QApplication.keyboardModifiers()
        if keys & Qt.ControlModifier:
            self.selection = list(set(self.selection) ^ set(indices))
        elif keys & Qt.AltModifier:
            self.selection = list(set(self.selection) - set(indices))
        elif keys & Qt.ShiftModifier:
            self.selection = list(set(self.selection) | set(indices))
        else:
            self.selection = list(set(indices))
        self.__select_bars()
        self.selection_changed.emit(self.selection)

    def __select_bars(self):
        if self.bar_item is None:
            return

        n = len(self.bar_item.opts["height"])
        pens = np.full(n, pg.mkPen(QColor(Qt.white)))
        pen = pg.mkPen(QColor(Qt.black))
        pen.setStyle(Qt.DashLine)
        pens[self.selection] = pen
        self.bar_item.setOpts(pens=pens)

    def help_event(self, ev: QGraphicsSceneHelpEvent):
        if self.bar_item is None:
            return False

        index = self.__get_index_at(self.bar_item.mapFromScene(ev.scenePos()))
        text = ""
        if index is not None:
            text = self.master.get_tooltip(index)
        if text:
            QToolTip.showText(ev.screenPos(), text, widget=self)
            return True
        else:
            return False
Example #11
0
    def __init__(self):
        super().__init__()

        self.results = None
        self.classifier_names = []
        self.colors = []
        self._points_hull: Dict[Tuple[int, int], PointsAndHull] = {}

        box = gui.vBox(self.controlArea, box="Curve")
        self.target_cb = gui.comboBox(box,
                                      self,
                                      "target_index",
                                      label="Target: ",
                                      orientation=Qt.Horizontal,
                                      callback=self._on_target_changed,
                                      contentsLength=8,
                                      searchable=True)
        gui.radioButtons(box,
                         self,
                         "curve_type", ("Lift Curve", "Cumulative Gains"),
                         callback=self._on_curve_type_changed)

        self.classifiers_list_box = gui.listBox(
            self.controlArea,
            self,
            "selected_classifiers",
            "classifier_names",
            box="Models",
            selectionMode=QListView.MultiSelection,
            callback=self._on_classifiers_changed)
        self.classifiers_list_box.setMaximumHeight(100)

        gui.checkBox(self.controlArea,
                     self,
                     "display_convex_hull",
                     "Show convex hull",
                     box="Settings",
                     callback=self._replot)

        gui.rubber(self.controlArea)

        self.plotview = pg.GraphicsView(background="w")
        self.plotview.setFrameStyle(QFrame.StyledPanel)

        axes = {
            "bottom": AxisItem(orientation="bottom"),
            "left": AxisItem(orientation="left")
        }
        self.plot = pg.PlotItem(enableMenu=False, axisItems=axes)
        self.plot.parameter_setter = ParameterSetter(self.plot)
        self.plot.curve_items = []
        self.plot.hull_items = []
        self.plot.default_line_item = None
        self.plot.display_convex_hull = self.display_convex_hull
        self.plot.setMouseEnabled(False, False)
        self.plot.hideButtons()

        pen = QPen(self.palette().color(QPalette.Text))

        tickfont = QFont(self.font())
        tickfont.setPixelSize(max(int(tickfont.pixelSize() * 2 // 3), 11))

        for pos, label in (("bottom", "P Rate"), ("left", "")):
            axis = self.plot.getAxis(pos)
            axis.setTickFont(tickfont)
            axis.setPen(pen)
            axis.setLabel(label)
        self._set_left_label()

        self.plot.showGrid(True, True, alpha=0.1)

        self.plotview.setCentralItem(self.plot)
        self.mainArea.layout().addWidget(self.plotview)

        VisualSettingsDialog(self, self.plot.parameter_setter.initial_settings)
class ViolinPlot(QGraphicsWidget):
    LABEL_COLUMN, VIOLIN_COLUMN, LEGEND_COLUMN = range(3)
    VIOLIN_COLUMN_WIDTH, OFFSET = 300, 80
    MAX_N_ITEMS = 100
    selection_cleared = Signal()
    selection_changed = Signal(float, float, str)
    resized = Signal()

    def __init__(self):
        super().__init__()
        self.__violin_column_width = self.VIOLIN_COLUMN_WIDTH  # type: int
        self.__range = None  # type: Optional[Tuple[float, float]]
        self.__violin_items = []  # type: List[ViolinItem]
        self.__variable_items = []  # type: List[VariableItem]
        self.__bottom_axis = AxisItem(parent=self, orientation="bottom",
                                      maxTickLength=7, pen=QPen(Qt.black))
        self.__bottom_axis.setLabel("Impact on model output")
        self.__vertical_line = QGraphicsLineItem(self.__bottom_axis)
        self.__vertical_line.setPen(QPen(Qt.gray))
        self.__legend = Legend(self)

        self.__layout = QGraphicsGridLayout()
        self.__layout.addItem(self.__legend, 0, ViolinPlot.LEGEND_COLUMN)
        self.__layout.setVerticalSpacing(0)
        self.setLayout(self.__layout)

        self.parameter_setter = ParameterSetter(self)

    @property
    def violin_column_width(self):
        return self.__violin_column_width

    @violin_column_width.setter
    def violin_column_width(self, view_width: int):
        j = ViolinPlot.LABEL_COLUMN
        w = max([self.__layout.itemAt(i, j).item.boundingRect().width()
                 for i in range(len(self.__violin_items))] + [0])
        width = view_width - self.legend.sizeHint().width() - self.OFFSET - w
        self.__violin_column_width = max(self.VIOLIN_COLUMN_WIDTH, width)

    @property
    def bottom_axis(self):
        return self.__bottom_axis

    @property
    def labels(self):
        return self.__variable_items

    @property
    def legend(self):
        return self.__legend

    def set_data(self, x: np.ndarray, colors: np.ndarray,
                 names: List[str], n_attrs: float, view_width: int):
        self.violin_column_width = view_width
        abs_max = np.max(np.abs(x)) * 1.05
        self.__range = (-abs_max, abs_max)
        self._set_violin_items(x, colors, names)
        self._set_labels(names)
        self._set_bottom_axis()
        self.set_n_visible(n_attrs)

    def set_n_visible(self, n: int):
        for i in range(len(self.__violin_items)):
            violin_item = self.__layout.itemAt(i, ViolinPlot.VIOLIN_COLUMN)
            violin_item.setVisible(i < n)
            text_item = self.__layout.itemAt(i, ViolinPlot.LABEL_COLUMN).item
            text_item.setVisible(i < n)
        self.set_vertical_line()

    def rescale(self, view_width: int):
        self.violin_column_width = view_width
        with temp_seed(0):
            for item in self.__violin_items:
                item.rescale(self.violin_column_width)

        self.__bottom_axis.setWidth(self.violin_column_width)
        x = self.violin_column_width / 2
        self.__vertical_line.setLine(x, 0, x, self.__vertical_line.line().y2())

    def show_legend(self, show: bool):
        self.__legend.setVisible(show)
        self.__bottom_axis.setWidth(self.violin_column_width)
        x = self.violin_column_width / 2
        self.__vertical_line.setLine(x, 0, x, self.__vertical_line.line().y2())

    def _set_violin_items(self, x: np.ndarray, colors: np.ndarray,
                          labels: List[str]):
        with temp_seed(0):
            for i in range(x.shape[1]):
                item = ViolinItem(self, labels[i], self.__range,
                                  self.violin_column_width)
                item.set_data(x[:, i], colors[:, i])
                item.selection_changed.connect(self.select)
                self.__violin_items.append(item)
                self.__layout.addItem(item, i, ViolinPlot.VIOLIN_COLUMN)
                if i == self.MAX_N_ITEMS:
                    break

    def _set_labels(self, labels: List[str]):
        for i, (label, _) in enumerate(zip(labels, self.__violin_items)):
            text = VariableItem(self, label)
            item = SimpleLayoutItem(text)
            item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
            self.__layout.addItem(item, i, ViolinPlot.LABEL_COLUMN,
                                  Qt.AlignRight | Qt.AlignVCenter)
            self.__variable_items.append(item)

    def _set_bottom_axis(self):
        self.__bottom_axis.setRange(*self.__range)
        self.__layout.addItem(self.__bottom_axis,
                              len(self.__violin_items),
                              ViolinPlot.VIOLIN_COLUMN)

    def set_vertical_line(self):
        x = self.violin_column_width / 2
        height = 0
        for i in range(len(self.__violin_items)):
            violin_item = self.__layout.itemAt(i, ViolinPlot.VIOLIN_COLUMN)
            text_item = self.__layout.itemAt(i, ViolinPlot.LABEL_COLUMN).item
            if violin_item.isVisible():
                height += max(text_item.boundingRect().height(),
                              violin_item.preferredSize().height())
        self.__vertical_line.setLine(x, 0, x, -height)

    def deselect(self):
        self.selection_cleared.emit()

    def select(self, *args):
        self.selection_changed.emit(*args)

    def select_from_settings(self, x1: float, x2: float, attr_name: str):
        point_r_diff = 2 * self.__range[1] / (self.violin_column_width / 2)
        for item in self.__violin_items:
            if item.attr_name == attr_name:
                item.add_selection_rect(x1 - point_r_diff, x2 + point_r_diff)
                break
        self.select(x1, x2, attr_name)

    def apply_visual_settings(self, settings: Dict):
        for key, value in settings.items():
            self.parameter_setter.set_parameter(key, value)
Example #13
0
    def __setup(self):
        # Setup the subwidgets/groups/layout
        smax = max(
            (np.nanmax(g.scores) for g in self.__groups if g.scores.size),
            default=1)
        smax = 1 if np.isnan(smax) else smax

        smin = min(
            (np.nanmin(g.scores) for g in self.__groups if g.scores.size),
            default=-1)
        smin = -1 if np.isnan(smin) else smin
        smin = min(smin, 0)

        font = self.font()
        font.setPixelSize(self.__barHeight)
        foreground = self.palette().brush(QPalette.WindowText)
        ax = AxisItem(parent=self, orientation="top", maxTickLength=7)
        ax.setRange(smin, smax)
        self.__topScale = ax
        layout = self.__layout
        assert layout is self.layout()
        layout.addItem(ax, 0, 2)

        for i, group in enumerate(self.__groups):
            silhouettegroup = BarPlotItem(parent=self)
            silhouettegroup.setBrush(QBrush(QColor(*group.color)))
            silhouettegroup.setPen(self.__pen)
            silhouettegroup.setDataRange(smin, smax)
            silhouettegroup.setPlotData(group.scores)
            silhouettegroup.setPreferredBarSize(self.__barHeight)
            silhouettegroup.setData(0, group.indices)
            layout.addItem(silhouettegroup, i + 1, 2)

            if group.label:
                layout.addItem(Line(orientation=Qt.Vertical), i + 1, 1)
                text = group.label
                if group.scores.size:
                    text += f" ({np.mean(group.scores):.3f})"
                label = QGraphicsSimpleTextItem(text, self)
                label.setBrush(foreground)
                label.setPen(QPen(Qt.NoPen))
                label.setRotation(-90)
                item = SimpleLayoutItem(
                    label,
                    anchor=(0., 1.0),
                    anchorItem=(0., 0.),
                )
                item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
                layout.addItem(item, i + 1, 0, Qt.AlignCenter)

            textlist = _SilhouettePlotTextListWidget(self,
                                                     font=font,
                                                     elideMode=Qt.ElideRight,
                                                     alignment=Qt.AlignLeft
                                                     | Qt.AlignVCenter)
            textlist.setMaximumWidth(750)
            textlist.setFlag(TextListWidget.ItemClipsChildrenToShape, False)
            sp = textlist.sizePolicy()
            sp.setVerticalPolicy(QSizePolicy.Ignored)
            textlist.setSizePolicy(sp)
            if group.rownames is not None:
                textlist.setItems(group.items)
                textlist.setVisible(self.__rowNamesVisible)
            else:
                textlist.setVisible(False)

            layout.addItem(textlist, i + 1, 3)

        ax = AxisItem(parent=self, orientation="bottom", maxTickLength=7)
        ax.setRange(smin, smax)
        self.__bottomScale = ax
        layout.addItem(ax, len(self.__groups) + 1, 2)
Example #14
0
class FeaturesPlot(QGraphicsWidget):
    BOTTOM_AXIS_LABEL = "Feature Importance"
    LABEL_COLUMN, ITEM_COLUMN = range(2)
    ITEM_COLUMN_WIDTH, OFFSET = 300, 80
    selection_cleared = Signal()
    selection_changed = Signal(object)
    resized = Signal()

    def __init__(self):
        super().__init__()
        self._item_column_width = self.ITEM_COLUMN_WIDTH
        self._range: Optional[Tuple[float, float]] = None
        self._items: List[FeatureItem] = []
        self._variable_items: List[VariableItem] = []
        self._bottom_axis = AxisItem(parent=self,
                                     orientation="bottom",
                                     maxTickLength=7,
                                     pen=QPen(Qt.black))
        self._bottom_axis.setLabel(self.BOTTOM_AXIS_LABEL)
        self._vertical_line = QGraphicsLineItem(self._bottom_axis)
        self._vertical_line.setPen(QPen(Qt.gray))

        self._layout = QGraphicsGridLayout()
        self._layout.setVerticalSpacing(0)
        self.setLayout(self._layout)

        self.parameter_setter = BaseParameterSetter(self)

    @property
    def item_column_width(self) -> int:
        return self._item_column_width

    @item_column_width.setter
    def item_column_width(self, view_width: int):
        j = FeaturesPlot.LABEL_COLUMN
        w = max([
            self._layout.itemAt(i, j).item.boundingRect().width()
            for i in range(len(self._items))
        ] + [0])
        width = view_width - self.OFFSET - w
        self._item_column_width = max(self.ITEM_COLUMN_WIDTH, width)

    @property
    def x0_scaled(self) -> float:
        min_max = self._range[1] - self._range[0]
        return -self._range[0] * self.item_column_width / min_max

    @property
    def bottom_axis(self) -> AxisItem:
        return self._bottom_axis

    @property
    def labels(self) -> List[VariableItem]:
        return self._variable_items

    def set_data(self, x: np.ndarray, names: List[str], n_attrs: int,
                 view_width: int, *plot_args):
        self.item_column_width = view_width
        self._set_range(x, *plot_args)
        self._set_items(x, names, *plot_args)
        self._set_labels(names)
        self._set_bottom_axis()
        self.set_n_visible(n_attrs)

    def _set_range(self, *_):
        raise NotImplementedError

    def _set_items(self, *_):
        raise NotImplementedError

    def set_n_visible(self, n: int):
        for i in range(len(self._items)):
            item = self._layout.itemAt(i, FeaturesPlot.ITEM_COLUMN)
            item.setVisible(i < n)
            text_item = self._layout.itemAt(i, FeaturesPlot.LABEL_COLUMN).item
            text_item.setVisible(i < n)
        self.set_vertical_line()

    def rescale(self, view_width: int):
        self.item_column_width = view_width
        for item in self._items:
            item.rescale(self.item_column_width)

        self._bottom_axis.setWidth(self.item_column_width)
        x = self.x0_scaled
        self._vertical_line.setLine(x, 0, x, self._vertical_line.line().y2())
        self.updateGeometry()

    def set_height(self, height: float):
        for i in range(len(self._items)):
            item = self._layout.itemAt(i, FeaturesPlot.ITEM_COLUMN)
            item.set_height(height)
        self.set_vertical_line()
        self.updateGeometry()

    def _set_labels(self, labels: List[str]):
        for i, (label, _) in enumerate(zip(labels, self._items)):
            text = VariableItem(self, label)
            item = SimpleLayoutItem(text)
            item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
            self._layout.addItem(item, i, FeaturesPlot.LABEL_COLUMN,
                                 Qt.AlignRight | Qt.AlignVCenter)
            self._variable_items.append(item)

    def _set_bottom_axis(self):
        self._bottom_axis.setRange(*self._range)
        self._layout.addItem(self._bottom_axis, len(self._items),
                             FeaturesPlot.ITEM_COLUMN)

    def set_vertical_line(self):
        height = 0
        for i in range(len(self._items)):
            item = self._layout.itemAt(i, FeaturesPlot.ITEM_COLUMN)
            text_item = self._layout.itemAt(i, FeaturesPlot.LABEL_COLUMN).item
            if item.isVisible():
                height += max(text_item.boundingRect().height(),
                              item.preferredSize().height())
        self._vertical_line.setLine(self.x0_scaled, 0, self.x0_scaled, -height)

    def deselect(self):
        self.selection_cleared.emit()

    def select(self, *args):
        self.selection_changed.emit(*args)

    def select_from_settings(self, *_):
        raise NotImplementedError

    def apply_visual_settings(self, settings: Dict):
        for key, value in settings.items():
            self.parameter_setter.set_parameter(key, value)