1- from collections import Counter
1+ from collections import Counter , defaultdict
22import sys
33import itertools
44from xml .sax .saxutils import escape
5- from math import log10 , floor , ceil
5+ from math import log2 , log10 , floor , ceil
66
77import numpy as np
88from scipy .stats import linregress
99
1010from AnyQt .QtCore import Qt , QObject , QEvent , QRectF , QPointF , QSize
1111from AnyQt .QtGui import (
12- QStaticText , QColor , QPen , QBrush , QPainterPath , QTransform , QPainter , QKeySequence )
12+ QStaticText , QColor , QPen , QBrush , QPainterPath , QTransform , QPainter , QKeySequence , QConicalGradient )
1313from AnyQt .QtWidgets import QApplication , QToolTip , QPinchGesture , \
1414 QGraphicsTextItem , QGraphicsRectItem , QAction
1515
@@ -691,13 +691,13 @@ def update_data(self, attr_x, attr_y, reset_view=True):
691691 self .shown_x .name , self .shown_y .name )
692692 return
693693
694- x_data , y_data = self .get_xy_data_positions (
694+ self . x_data , self . y_data = self .get_xy_data_positions (
695695 attr_x , attr_y , self .valid_data )
696- self .n_points = len (x_data )
696+ self .n_points = len (self . x_data )
697697
698698 if reset_view :
699- min_x , max_x = np .nanmin (x_data ), np .nanmax (x_data )
700- min_y , max_y = np .nanmin (y_data ), np .nanmax (y_data )
699+ min_x , max_x = np .nanmin (self . x_data ), np .nanmax (self . x_data )
700+ min_y , max_y = np .nanmin (self . y_data ), np .nanmax (self . y_data )
701701 self .view_box .setRange (
702702 QRectF (min_x , min_y , max_x - min_x , max_y - min_y ),
703703 padding = 0.025 )
@@ -712,6 +712,14 @@ def update_data(self, attr_x, attr_y, reset_view=True):
712712 else :
713713 self .set_labels (axis , None )
714714
715+ # compute overlaps of points for use in compute_colors and compute_sizes
716+ self .overlaps = []
717+ points = defaultdict (list )
718+ for i , xy in enumerate (zip (self .x_data , self .y_data )):
719+ points [xy ].append (i )
720+ self .overlaps = [len (points [xy ]) for i , xy in enumerate (zip (self .x_data , self .y_data ))]
721+ self .overlap_factor = [1 + log2 (o ) for o in self .overlaps ]
722+
715723 color_data , brush_data = self .compute_colors ()
716724 color_data_sel , brush_data_sel = self .compute_colors_sel ()
717725 size_data = self .compute_sizes ()
@@ -721,7 +729,7 @@ def update_data(self, attr_x, attr_y, reset_view=True):
721729 rgb_data = [pen .color ().getRgb ()[:3 ] for pen in color_data ]
722730 self .density_img = classdensity .class_density_image (
723731 min_x , max_x , min_y , max_y , self .resolution ,
724- x_data , y_data , rgb_data )
732+ self . x_data , self . y_data , rgb_data )
725733 self .plot_widget .addItem (self .density_img )
726734
727735 self .data_indices = np .flatnonzero (self .valid_data )
@@ -730,11 +738,11 @@ def update_data(self, attr_x, attr_y, reset_view=True):
730738 self .shown_x .name , self .shown_y .name )
731739
732740 self .scatterplot_item = ScatterPlotItem (
733- x = x_data , y = y_data , data = self .data_indices ,
741+ x = self . x_data , y = self . y_data , data = self .data_indices ,
734742 symbol = shape_data , size = size_data , pen = color_data , brush = brush_data
735743 )
736744 self .scatterplot_item_sel = ScatterPlotItem (
737- x = x_data , y = y_data , data = self .data_indices ,
745+ x = self . x_data , y = self . y_data , data = self .data_indices ,
738746 symbol = shape_data , size = size_data + SELECTION_WIDTH ,
739747 pen = color_data_sel , brush = brush_data_sel
740748 )
@@ -815,6 +823,10 @@ def compute_sizes(self):
815823 if np .any (nans ):
816824 size_data [nans ] = self .MinShapeSize - 2
817825 self .master .Information .missing_size (self .attr_size )
826+
827+ # scale sizes because of overlaps
828+ size_data = np .multiply (size_data , self .overlap_factor )
829+
818830 return size_data
819831
820832 def update_sizes (self ):
@@ -957,6 +969,15 @@ def compute_colors(self, keep_colors=False):
957969 QBrush (QColor (col [0 ], col [1 ], col [2 ], alpha ))]
958970 for col in colors ])
959971 self .brush_colors = self .brush_colors [c_data ]
972+
973+ # gray out overlapping points
974+ for i , xy in enumerate (zip (self .x_data , self .y_data )):
975+ if self .overlaps [i ] > 1 :
976+ self .brush_colors [i ] = [
977+ QBrush (QColor (0 , 0 , 0 , 0 )),
978+ QBrush (QColor (128 , 128 , 128 , alpha ))]
979+ self .pen_colors [i ] = _make_pen (QColor (128 , 128 , 128 ).darker (self .DarkerValue ), 1.5 )
980+
960981 if subset is not None :
961982 brush = np .where (
962983 subset ,
0 commit comments