(self)
Update the graph.
Update the graph.
Update the graph .
def update_graph(self):
        # Function uses weird names like r, g, b, but it does it with utmost
        # caution, hence
        # pylint: disable=invalid-name
        """Update the graph."""

        def text(txt, *args, **kwargs):
            text = html_text = None
            if "max_width" in kwargs:
                text = txt
            else:
                html_text = to_html(txt)
            return CanvasText(self.canvas, text, html_text=html_text,
                              *args, **kwargs)

        def width(txt):
            return text(txt, 0, 0, show=False).boundingRect().width()

        def height(txt):
            return text(txt, 0, 0, show=False).boundingRect().height()

        def fmt(val):
            return str(int(val)) if val % 1 == 0 else "{:.2f}".format(val)

        def show_pearson(rect, pearson, pen_width):
            """
            Color the given rectangle according to its corresponding
            standardized Pearson residual.

            Args:
                rect (QRect): the rectangle being drawn
                pearson (float): signed standardized pearson residual
                pen_width (int): pen width (bolder pen is used for selection)
            """
            r = rect.rect()
            x, y, w, h = r.x(), r.y(), r.width(), r.height()
            if w == 0 or h == 0:
                return

            r = b = 255
            if pearson > 0:
                r = g = max(255 - 20 * pearson, 55)
            elif pearson < 0:
                b = g = max(255 + 20 * pearson, 55)
            else:
                r = g = b = 224
            rect.setBrush(QBrush(QColor(r, g, b)))
            pen_color = QColor(255 * (r == 255), 255 * (g == 255),
                               255 * (b == 255))
            pen = QPen(pen_color, pen_width)
            rect.setPen(pen)
            if pearson > 0:
                pearson = min(pearson, 10)
                dist = 20 - 1.6 * pearson
            else:
                pearson = max(pearson, -10)
                dist = 20 - 8 * pearson
            pen.setWidth(1)

            def _offseted_line(ax, ay):
                r = QGraphicsLineItem(x + ax, y + ay, x + (ax or w),
                                      y + (ay or h))
                self.canvas.addItem(r)
                r.setPen(pen)

            ax = dist
            while ax < w:
                _offseted_line(ax, 0)
                ax += dist

            ay = dist
            while ay < h:
                _offseted_line(0, ay)
                ay += dist

        def make_tooltip():
            """Create the tooltip. The function uses local variables from
            the enclosing scope."""
            # pylint: disable=undefined-loop-variable
            def _oper(attr, txt):
                if self.data.domain[attr.name] == ddomain[attr.name]:
                    return " = "
                return " " if txt[0] in "<≥" else " in "

            xt, yt = ["<b>{attr}{eq}{val_name}</b>: {obs}/{n} ({p:.0f} %)".format(
                attr=to_html(attr.name),
                eq=_oper(attr, val_name),
                val_name=to_html(val_name),
                obs=fmt(prob * n),
                n=int(n),
                p=100 * prob)
                      for attr, val_name, prob in [(attr_x, xval_name, chi.probs_x[x]),
                                                   (attr_y, yval_name, chi.probs_y[y])]]

            ct = """<b>combination of values: </b><br/>
                   &nbsp;&nbsp;&nbsp;expected {exp} ({p_exp:.0f} %)<br/>
                   &nbsp;&nbsp;&nbsp;observed {obs} ({p_obs:.0f} %)""".format(
                       exp=fmt(chi.expected[y, x]),
                       p_exp=100 * chi.expected[y, x] / n,
                       obs=fmt(chi.observed[y, x]),
                       p_obs=100 * chi.observed[y, x] / n)

            return f"{xt}<br/>{yt}<hr/>{ct}"


        for item in self.canvas.items():
            self.canvas.removeItem(item)
        if self.data is None or len(self.data) == 0 or \
                self.attr_x is None or self.attr_y is None:
            return

        ddomain = self.discrete_data.domain
        attr_x, attr_y = self.attr_x, self.attr_y
        disc_x, disc_y = ddomain[attr_x.name], ddomain[attr_y.name]
        view = self.canvasView

        chi = ChiSqStats(self.discrete_data, disc_x, disc_y)
        max_ylabel_w = max((width(val) for val in disc_y.values), default=0)
        max_ylabel_w = min(max_ylabel_w, 200)
        x_off = height(attr_y.name) + max_ylabel_w
        y_off = 15
        square_size = min(view.width() - x_off - 35, view.height() - y_off - 80)
        square_size = max(square_size, 10)
        self.canvasView.setSceneRect(0, 0, view.width(), view.height())
        if not disc_x.values or not disc_y.values:
            text_ = "Features {} and {} have no values".format(disc_x, disc_y) \
                if not disc_x.values and \
                   not disc_y.values and \
                          disc_x != disc_y \
                else \
                    "Feature {} has no values".format(
                        disc_x if not disc_x.values else disc_y)
            text(text_, view.width() / 2 + 70, view.height() / 2,
                 Qt.AlignRight | Qt.AlignVCenter)
            return
        n = chi.n
        curr_x = x_off
        max_xlabel_h = 0
        self.areas = []
        for x, (px, xval_name) in enumerate(zip(chi.probs_x, disc_x.values)):
            if px == 0:
                continue
            width = square_size * px

            curr_y = y_off
            for y in range(len(chi.probs_y) - 1, -1, -1):  # bottom-up order
                py = chi.probs_y[y]
                yval_name = disc_y.values[y]
                if py == 0:
                    continue
                height = square_size * py

                selected = len(self.areas) in self.selection
                rect = CanvasRectangle(
                    self.canvas, curr_x + 2, curr_y + 2, width - 4, height - 4,
                    z=-10, onclick=self.select_area)
                rect.value_pair = x, y
                self.areas.append(rect)
                show_pearson(rect, chi.residuals[y, x], 3 * selected)
                rect.setToolTip(make_tooltip())

                if x == 0:
                    text(yval_name, x_off, curr_y + height / 2,
                         Qt.AlignRight | Qt.AlignVCenter)
                curr_y += height

            xl = text(xval_name, curr_x + width / 2, y_off + square_size,
                      Qt.AlignHCenter | Qt.AlignTop, max_width=width)
            max_xlabel_h = max(int(xl.boundingRect().height()), max_xlabel_h)
            curr_x += width

        bottom = y_off + square_size + max_xlabel_h
        text(attr_y.name, 0, y_off + square_size / 2,
             Qt.AlignLeft | Qt.AlignVCenter, bold=True, vertical=True)
        text(attr_x.name, x_off + square_size / 2, bottom,
             Qt.AlignHCenter | Qt.AlignTop, bold=True)
        bottom += 30
        xl = text("χ²={:.2f}, p={:.3f}".format(chi.chisq, chi.p),
                  0, bottom)
        # Assume similar height for both lines
        text("N = " + fmt(chi.n), 0, bottom - xl.boundingRect().height())
def update_graph ( self ) : # Function uses weird names like r, g, b, but it does it with utmost # caution, hence # pylint: disable=invalid-name def text ( txt , * args , * * kwargs ) : text = html_text = None if "max_width" in kwargs : text = txt else : html_text = to_html ( txt ) return CanvasText ( self . canvas , text , html_text = html_text , * args , * * kwargs ) def width ( txt ) : return text ( txt , 0 , 0 , show = False ) . boundingRect ( ) . width ( ) def height ( txt ) : return text ( txt , 0 , 0 , show = False ) . boundingRect ( ) . height ( ) def fmt ( val ) : return str ( int ( val ) ) if val % 1 == 0 else "{:.2f}" . format ( val ) def show_pearson ( rect , pearson , pen_width ) : """
            Color the given rectangle according to its corresponding
            standardized Pearson residual.

            Args:
                rect (QRect): the rectangle being drawn
                pearson (float): signed standardized pearson residual
                pen_width (int): pen width (bolder pen is used for selection)
            """ r = rect . rect ( ) x , y , w , h = r . x ( ) , r . y ( ) , r . width ( ) , r . height ( ) if w == 0 or h == 0 : return r = b = 255 if pearson > 0 : r = g = max ( 255 - 20 * pearson , 55 ) elif pearson < 0 : b = g = max ( 255 + 20 * pearson , 55 ) else : r = g = b = 224 rect . setBrush ( QBrush ( QColor ( r , g , b ) ) ) pen_color = QColor ( 255 * ( r == 255 ) , 255 * ( g == 255 ) , 255 * ( b == 255 ) ) pen = QPen ( pen_color , pen_width ) rect . setPen ( pen ) if pearson > 0 : pearson = min ( pearson , 10 ) dist = 20 - 1.6 * pearson else : pearson = max ( pearson , - 10 ) dist = 20 - 8 * pearson pen . setWidth ( 1 ) def _offseted_line ( ax , ay ) : r = QGraphicsLineItem ( x + ax , y + ay , x + ( ax or w ) , y + ( ay or h ) ) self . canvas . addItem ( r ) r . setPen ( pen ) ax = dist while ax < w : _offseted_line ( ax , 0 ) ax += dist ay = dist while ay < h : _offseted_line ( 0 , ay ) ay += dist def make_tooltip ( ) : """Create the tooltip. The function uses local variables from
            the enclosing scope.""" # pylint: disable=undefined-loop-variable def _oper ( attr , txt ) : if self . data . domain [ attr . name ] == ddomain [ attr . name ] : return " = " return " " if txt [ 0 ] in "<≥" e se " in " xt , yt = [ "<b>{attr}{eq}{val_name}</b>: {obs}/{n} ({p:.0f} %)" . format ( attr = to_html ( attr . name ) , eq = _oper ( attr , val_name ) , val_name = to_html ( val_name ) , obs = fmt ( prob * n ) , n = int ( n ) , p = 100 * prob ) for attr , val_name , prob in [ ( attr_x , xval_name , chi . probs_x [ x ] ) , ( attr_y , yval_name , chi . probs_y [ y ] ) ] ] ct = """<b>combination of values: </b><br/>
                   &nbsp;&nbsp;&nbsp;expected {exp} ({p_exp:.0f} %)<br/>
                   &nbsp;&nbsp;&nbsp;observed {obs} ({p_obs:.0f} %)""" . format ( exp = fmt ( chi . expected [ y , x ] ) , p_exp = 100 * chi . expected [ y , x ] / n , obs = fmt ( chi . observed [ y , x ] ) , p_obs = 100 * chi . observed [ y , x ] / n ) return f"{xt}<br/>{yt}<hr/>{ct}" for item in self . canvas . items ( ) : self . canvas . removeItem ( item ) if self . data is None or len ( self . data ) == 0 or self . attr_x is None or self . attr_y is None : return ddomain = self . discrete_data . domain attr_x , attr_y = self . attr_x , self . attr_y disc_x , disc_y = ddomain [ attr_x . name ] , ddomain [ attr_y . name ] view = self . canvasView chi = ChiSqStats ( self . discrete_data , disc_x , disc_y ) max_ylabel_w = max ( ( width ( val ) for val in disc_y . values ) , default = 0 ) max_ylabel_w = min ( max_ylabel_w , 200 ) x_off = height ( attr_y . name ) + max_ylabel_w y_off = 15 square_size = min ( view . width ( ) - x_off - 35 , view . height ( ) - y_off - 80 ) square_size = max ( square_size , 10 ) self . canvasView . setSceneRect ( 0 , 0 , view . width ( ) , view . height ( ) ) if not disc_x . values or not disc_y . values : text_ = "Features {} and {} have no values" . format ( disc_x , disc_y ) if not disc_x . values and not disc_y . values and disc_x != disc_y else "Feature {} has no values" . format ( disc_x if not disc_x . values else disc_y ) text ( text_ , view . width ( ) / 2 + 70 , view . height ( ) / 2 , Qt . AlignRight | Qt . AlignVCenter ) return n = chi . n curr_x = x_off max_xlabel_h = 0 self . areas = [ ] for x , ( px , xval_name ) in enumerate ( zip ( chi . probs_x , disc_x . values ) ) : if px == 0 : continue width = square_size * px curr_y = y_off for y in range ( len ( chi . probs_y ) - 1 , - 1 , - 1 ) : # bottom-up order py = chi . probs_y [ y ] yval_name = disc_y . values [ y ] if py == 0 : continue height = square_size * py selected = len ( self . areas ) in self . selection rect = CanvasRectangle ( self . canvas , curr_x + 2 , curr_y + 2 , width - 4 , height - 4 , z = - 10 , onclick = self . select_area ) rect . value_pair = x , y self . areas . append ( rect ) show_pearson ( rect , chi . residuals [ y , x ] , 3 * selected ) rect . setToolTip ( make_tooltip ( ) ) if x == 0 : text ( yval_name , x_off , curr_y + height / 2 , Qt . AlignRight | Qt . AlignVCenter ) curr_y += height xl = text ( xval_name , curr_x + width / 2 , y_off + square_size , Qt . AlignHCenter | Qt . AlignTop , max_width = width ) max_xlabel_h = max ( int ( xl . boundingRect ( ) . height ( ) ) , max_xlabel_h ) curr_x += width bottom = y_off + square_size + max_xlabel_h text ( attr_y . name , 0 , y_off + square_size / 2 , Qt . AlignLeft | Qt . AlignVCenter , bold = True , vertical = True ) text ( attr_x . name , x_off + square_size / 2 , bottom , Qt . AlignHCenter | Qt . AlignTop , bold = True ) bottom += 30 xl = text ( "χ²={:.2f}, p={:.3f}".f o rmat(c h i.c h isq,  c i.p ) , 0 , bottom ) # Assume similar height for both lines text ( "N = " + fmt ( chi . n ) , 0 , bottom - xl . boundingRect ( ) . height ( ) )