Loading autocnet/graph/edge.py +4 −11 Original line number Diff line number Diff line Loading @@ -8,7 +8,6 @@ from scipy.spatial.distance import cdist import autocnet from autocnet.utils import utils from autocnet.matcher import health from autocnet.matcher import outlier_detector as od from autocnet.matcher import suppression_funcs as spf from autocnet.matcher import subpixel as sp Loading Loading @@ -57,6 +56,8 @@ class Edge(dict, MutableMapping): o = other.__dict__ for k, v in d.items(): if isinstance(v, pd.DataFrame): if not k in o.keys(): print(o) if not v.equals(o[k]): eq = False elif isinstance(v, np.ndarray): Loading Loading @@ -326,17 +327,9 @@ class Edge(dict, MutableMapping): merged = matches.merge(coords, left_on=['source_idx'], right_index=True) merged['strength'] = merged.apply(suppression_func, axis=1, args=([self])) if not hasattr(self, 'suppression'): # Instantiate the suppression object and suppress matches self.suppression = od.SpatialSuppression(merged, domain, **kwargs) self.suppression.suppress() else: for k, v in kwargs.items(): if hasattr(self.suppression, k): setattr(self.suppression, k, v) self.suppression.suppress() smask, k = od.spatial_suppression(merged, domain, **kwargs) mask[mask] = self.suppression.mask mask[mask] = smask self.masks = ('suppression', mask) def plot_source(self, ax=None, clean_keys=[], **kwargs): # pragma: no cover Loading autocnet/matcher/outlier_detector.py +33 −61 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ import warnings import numpy as np import pandas as pd from autocnet.utils.observable import Observable def distance_ratio(matches, ratio=0.8, single=False): """ Loading Loading @@ -46,7 +45,7 @@ def distance_ratio(matches, ratio=0.8, single=False): return mask class SpatialSuppression(Observable): def spatial_suppression(df, domain, min_radius=1.5, k=250, error_k=0.1): """ Spatial suppression using disc based method. Loading Loading @@ -76,57 +75,35 @@ class SpatialSuppression(Observable): domain : tuple The (x,y) extent of the input domain Returns ------- mask : pd.Series Boolean suppression mask k : int The number of unsuppressed observations References ---------- [Gauglitz2011]_ """ def __init__(self, df, domain, min_radius=1.5, k=250, error_k=0.1): columns = df.columns for i in ['x', 'y', 'strength']: if i not in columns: raise ValueError('The dataframe is missing a {} column.'.format(i)) self.df = df.sort_values(by=['strength'], ascending=False).copy() self.max_radius = max(domain) self.min_radius = min_radius self.domain = domain self.mask = pd.Series(False, index=self.df.index) self.k = k self._error_k = error_k self.attrs = ['mask', 'k', 'error_k'] df = df.sort_values(by=['strength'], ascending=False).copy() max_radius = max(domain) mask = pd.Series(False, index=df.index) self._action_stack = deque(maxlen=10) self._current_action_stack = 0 self._observers = set() @property def nvalid(self): return self.mask.sum() @property def error_k(self): return self._error_k @error_k.setter def error_k(self, v): self._error_k = v def suppress(self): """ Suppress subpixel registered points so that k +- k * error_k points, with good spatial distribution, remain """ process = True if self.k > len(self.df): warnings.warn('Only {} valid points, but {} points requested'.format(len(self.df), self.k)) self.k = len(self.df) result = self.df.index if k > len(df): warnings.warn('Only {} valid points, but {} points requested'.format(len(df), k)) k = len(df) result = df.index process = False nsteps = max(self.domain) * 0.95 search_space = np.linspace(self.min_radius, self.max_radius, nsteps) nsteps = max(domain) * 0.95 search_space = np.linspace(min_radius, max_radius, nsteps) cell_sizes = search_space / math.sqrt(2) min_idx = 0 max_idx = len(search_space) - 1 Loading @@ -145,21 +122,21 @@ class SpatialSuppression(Observable): process = False cell_size = cell_sizes[mid_idx] n_x_cells = int(self.domain[0] / cell_size) n_y_cells = int(self.domain[1] / cell_size) n_x_cells = int(domain[0] / cell_size) n_y_cells = int(domain[1] / cell_size) grid = np.zeros((n_x_cells, n_y_cells), dtype=np.bool) # Assign all points to bins x_edges = np.linspace(0, self.domain[0], n_x_cells) y_edges = np.linspace(0, self.domain[1], n_y_cells) xbins = np.digitize(self.df['x'], bins=x_edges) ybins = np.digitize(self.df['y'], bins=y_edges) x_edges = np.linspace(0, domain[0], n_x_cells) y_edges = np.linspace(0, domain[1], n_y_cells) xbins = np.digitize(df['x'], bins=x_edges) ybins = np.digitize(df['y'], bins=y_edges) # Convert bins to cells xbins -= 1 ybins -= 1 pts = [] for i, (idx, p) in enumerate(self.df.iterrows()): for i, (idx, p) in enumerate(df.iterrows()): x_center = xbins[i] y_center = ybins[i] cell = grid[y_center, x_center] Loading @@ -167,7 +144,7 @@ class SpatialSuppression(Observable): if cell == False: result.append(idx) pts.append((p[['x', 'y']])) if len(result) > self.k + self.k * self.error_k: if len(result) > k + k * error_k: # Too many points, break min_idx = mid_idx break Loading @@ -193,26 +170,21 @@ class SpatialSuppression(Observable): x_min: x_max] = True # Check break conditions if self.k - self.k * self.error_k <= len(result) <= self.k + self.k * self.error_k: if k - k * error_k <= len(result) <= k + k * error_k: process = False elif len(result) < self.k - self.k * self.error_k: elif len(result) < k - k * error_k: # The radius is too large max_idx = mid_idx if max_idx == 0: warnings.warn('Unable to retrieve {} points. Consider reducing the amount of points you request(k)' .format(self.k)) .format(k)) process = False if min_idx == max_idx: process = False self.mask = pd.Series(False, self.df.index) self.mask.loc[list(result)] = True state_package = {'mask': self.mask, 'k': self.k, 'error_k': self.error_k} self._action_stack.append(state_package) self._notify_subscribers(self) self._current_action_stack = len(self._action_stack) - 1 # 0 based vs. 1 based mask = pd.Series(False, df.index) mask.loc[list(result)] = True return mask, k def self_neighbors(matches): Loading autocnet/matcher/subpixel.py +0 −130 Original line number Diff line number Diff line Loading @@ -87,133 +87,3 @@ def subpixel_offset(template, search, **kwargs): x_offset, y_offset, strength = functions[method](template, search, **kwargs) return x_offset, y_offset, strength ''' Stub for an observable subpixel class class PatternMatch(Observable): """ Attributes ---------- df : dataframe A dataframe of point to be subpixel registered img1 : object or ndarray A file handle object or ndarray to use to subpixel register img2 : object or ndarray A file handle object or ndarray to use to subpixel register destination : object Destination node threshold_mask : series A pandas series masking values < threshold shift_mask : series A pandas series masking values with shifts larger than the allowed x, y shifts subpixel_mask : series A composite mask, threshold_mask & shift_mask """ def __init__(self, img1, img2, df, min_x_shift=-1.0, max_x_shift=1.0, min_y_shift=-1.0, max_y_shift=1.0, threshold=0.8): self.img1 = img1 self.img2 = img2 self.df = df self._min_x_shift = min_x_shift self._min_y_shift = min_y_shift self._max_x_shift = max_x_shift self._max_y_shift = max_y_shift self._threshold = threshold self.threshold_mask = pd.Series(True, index=self.df.index) self.shift_mask = pd.Series(True, index=self.df.index) self.subpixel_mask = self.threshold_mask & self.subpixel_mask self._action_stack = deque(maxlen=20) self._current_action_stack = 0 self._observers = set() self.attrs = ['threshold', 'min_x_shift', 'max_x_shift', 'min_y_shift', 'm_y_shift', 'threshold_mask', 'shift_mask', 'subpixel_mask'] def clip_roi(self, img, center): @property def threshold(self): return self._threshold @threshold.setter def threshold(self, v): if 0 <= v <= 1: self._threshold = v # Update the mask here self.threshold_mask = self.d current_state = self._action_stack[self._current_action_stack] current_state['threshold'] = self.threshold self._update_stack(current_state) @property def min_x_shift(self): return self._min_x_shift @min_x_shift.setter def min_x_shift(self, v): self._min_x_shift = v # Update mask here current_state = self._action_stack[self._current_action_stack] current_state['min_x_shift'] = self.min_x_shift self._update_stack() @property def min_y_shift(self): return self._min_y_shift @min_x_shift.setter def min_y_shift(self, v): self._min_y_shift = v # Update mask here current_state = self._action_stack[self._current_action_stack] current_state['min_y_shift'] = self.min_y_shift self._update_stack() @property def max_x_shift(self): return self._max_x_shift @max_x_shift.setter def max_x_shift(self, v): self._max_x_shift = v # Update mask here current_state = self._action_stack[self._current_action_stack] current_state['max_x_shift'] = self.max_x_shift self._update_stack() @property def max_y_shift(self): return self._max_y_shift @max_y_shift.setter def max_y_shift(self, v): self._max_y_shift = v # Update mask here current_state = self._action_stack[self._current_action_stack] current_state['max_y_shift'] = self.max_y_shift self._update_stack() ''' autocnet/matcher/tests/test_outlier_detector.py +15 −34 Original line number Diff line number Diff line Loading @@ -7,7 +7,6 @@ import numpy as np import pandas as pd from .. import outlier_detector from autocnet.matcher.outlier_detector import SpatialSuppression sys.path.append(os.path.abspath('..')) Loading Loading @@ -53,37 +52,23 @@ class TestSpatialSuppression(unittest.TestCase): y = seed.randint(0, 100, 100).astype(np.float32) strength = seed.rand(100) data = np.vstack((x, y, strength)).T df = pd.DataFrame(data, columns=['x', 'y', 'strength']) self.suppression_obj = outlier_detector.SpatialSuppression(df, (100, 100), k=25) def test_properties(self): self.assertEqual(self.suppression_obj.k, 25) self.suppression_obj.k = 26 self.assertTrue(self.suppression_obj.k, 26) self.assertEqual(self.suppression_obj.error_k, 0.1) self.suppression_obj.error_k = 0.05 self.assertEqual(self.suppression_obj.error_k, 0.05) self.assertEqual(self.suppression_obj.nvalid, 0) self.assertIsInstance(self.suppression_obj.df, pd.DataFrame) self.df = pd.DataFrame(data, columns=['x', 'y', 'strength']) self.domain = (100,100) def test_suppress_non_optimal(self): with warnings.catch_warnings(record=True) as w: self.suppression_obj.suppress() mask, k = outlier_detector.spatial_suppression(self.df, self.domain, k=25) self.assertEqual(len(w), 1) self.assertEqual(w[0].category, UserWarning) self.assertEqual(self.suppression_obj.mask.sum(), 28) self.assertEqual(mask.sum(), 28) def test_suppress(self): self.suppression_obj.k = 30 self.suppression_obj.suppress() self.assertIn(self.suppression_obj.mask.sum(), list(range(27, 35))) mask, k = outlier_detector.spatial_suppression(self.df, self.domain, k=30) self.assertIn(mask.sum(), list(range(27, 35))) with warnings.catch_warnings(record=True) as w: self.suppression_obj.k = 101 self.suppression_obj.suppress() mask, k = outlier_detector.spatial_suppression(self.df, self.domain, k=101) self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[0].category, UserWarning)) Loading @@ -95,24 +80,20 @@ class testSuppressionRanges(unittest.TestCase): def test_min_max(self): df = pd.DataFrame(self.r.uniform(0,2,(500, 3)), columns=['x', 'y', 'strength']) sup = SpatialSuppression(df, (1.5,1.5), k = 1) sup.suppress() self.assertEqual(len(df[sup.mask]), 1) mask, k = outlier_detector.spatial_suppression(df, (1.5,1.5), k = 1) self.assertEqual(len(df[mask]), 1) def test_point_overload(self): df = pd.DataFrame(self.r.uniform(0,15,(500, 3)), columns=['x', 'y', 'strength']) sup = SpatialSuppression(df, (15,15), k = 200) sup.suppress() self.assertEqual(len(df[sup.mask]), 69) mask, k = outlier_detector.spatial_suppression(df, (15,15), k = 200) self.assertEqual(len(df[mask]), 69) def test_small_distribution(self): df = pd.DataFrame(self.r.uniform(0,25,(500, 3)), columns=['x', 'y', 'strength']) sup = SpatialSuppression(df, (25,25), k = 25) sup.suppress() self.assertEqual(len(df[sup.mask]), 28) mask, k = outlier_detector.spatial_suppression(df, (25,25), k = 25) self.assertEqual(len(df[mask]), 28) def test_normal_distribution(self): df = pd.DataFrame(self.r.uniform(0,100,(500, 3)), columns=['x', 'y', 'strength']) sup = SpatialSuppression(df, (100,100), k = 15) sup.suppress() self.assertEqual(len(df[sup.mask]), 17) mask, k = outlier_detector.spatial_suppression(df, (100,100), k = 15) self.assertEqual(len(df[mask]), 17) Loading
autocnet/graph/edge.py +4 −11 Original line number Diff line number Diff line Loading @@ -8,7 +8,6 @@ from scipy.spatial.distance import cdist import autocnet from autocnet.utils import utils from autocnet.matcher import health from autocnet.matcher import outlier_detector as od from autocnet.matcher import suppression_funcs as spf from autocnet.matcher import subpixel as sp Loading Loading @@ -57,6 +56,8 @@ class Edge(dict, MutableMapping): o = other.__dict__ for k, v in d.items(): if isinstance(v, pd.DataFrame): if not k in o.keys(): print(o) if not v.equals(o[k]): eq = False elif isinstance(v, np.ndarray): Loading Loading @@ -326,17 +327,9 @@ class Edge(dict, MutableMapping): merged = matches.merge(coords, left_on=['source_idx'], right_index=True) merged['strength'] = merged.apply(suppression_func, axis=1, args=([self])) if not hasattr(self, 'suppression'): # Instantiate the suppression object and suppress matches self.suppression = od.SpatialSuppression(merged, domain, **kwargs) self.suppression.suppress() else: for k, v in kwargs.items(): if hasattr(self.suppression, k): setattr(self.suppression, k, v) self.suppression.suppress() smask, k = od.spatial_suppression(merged, domain, **kwargs) mask[mask] = self.suppression.mask mask[mask] = smask self.masks = ('suppression', mask) def plot_source(self, ax=None, clean_keys=[], **kwargs): # pragma: no cover Loading
autocnet/matcher/outlier_detector.py +33 −61 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ import warnings import numpy as np import pandas as pd from autocnet.utils.observable import Observable def distance_ratio(matches, ratio=0.8, single=False): """ Loading Loading @@ -46,7 +45,7 @@ def distance_ratio(matches, ratio=0.8, single=False): return mask class SpatialSuppression(Observable): def spatial_suppression(df, domain, min_radius=1.5, k=250, error_k=0.1): """ Spatial suppression using disc based method. Loading Loading @@ -76,57 +75,35 @@ class SpatialSuppression(Observable): domain : tuple The (x,y) extent of the input domain Returns ------- mask : pd.Series Boolean suppression mask k : int The number of unsuppressed observations References ---------- [Gauglitz2011]_ """ def __init__(self, df, domain, min_radius=1.5, k=250, error_k=0.1): columns = df.columns for i in ['x', 'y', 'strength']: if i not in columns: raise ValueError('The dataframe is missing a {} column.'.format(i)) self.df = df.sort_values(by=['strength'], ascending=False).copy() self.max_radius = max(domain) self.min_radius = min_radius self.domain = domain self.mask = pd.Series(False, index=self.df.index) self.k = k self._error_k = error_k self.attrs = ['mask', 'k', 'error_k'] df = df.sort_values(by=['strength'], ascending=False).copy() max_radius = max(domain) mask = pd.Series(False, index=df.index) self._action_stack = deque(maxlen=10) self._current_action_stack = 0 self._observers = set() @property def nvalid(self): return self.mask.sum() @property def error_k(self): return self._error_k @error_k.setter def error_k(self, v): self._error_k = v def suppress(self): """ Suppress subpixel registered points so that k +- k * error_k points, with good spatial distribution, remain """ process = True if self.k > len(self.df): warnings.warn('Only {} valid points, but {} points requested'.format(len(self.df), self.k)) self.k = len(self.df) result = self.df.index if k > len(df): warnings.warn('Only {} valid points, but {} points requested'.format(len(df), k)) k = len(df) result = df.index process = False nsteps = max(self.domain) * 0.95 search_space = np.linspace(self.min_radius, self.max_radius, nsteps) nsteps = max(domain) * 0.95 search_space = np.linspace(min_radius, max_radius, nsteps) cell_sizes = search_space / math.sqrt(2) min_idx = 0 max_idx = len(search_space) - 1 Loading @@ -145,21 +122,21 @@ class SpatialSuppression(Observable): process = False cell_size = cell_sizes[mid_idx] n_x_cells = int(self.domain[0] / cell_size) n_y_cells = int(self.domain[1] / cell_size) n_x_cells = int(domain[0] / cell_size) n_y_cells = int(domain[1] / cell_size) grid = np.zeros((n_x_cells, n_y_cells), dtype=np.bool) # Assign all points to bins x_edges = np.linspace(0, self.domain[0], n_x_cells) y_edges = np.linspace(0, self.domain[1], n_y_cells) xbins = np.digitize(self.df['x'], bins=x_edges) ybins = np.digitize(self.df['y'], bins=y_edges) x_edges = np.linspace(0, domain[0], n_x_cells) y_edges = np.linspace(0, domain[1], n_y_cells) xbins = np.digitize(df['x'], bins=x_edges) ybins = np.digitize(df['y'], bins=y_edges) # Convert bins to cells xbins -= 1 ybins -= 1 pts = [] for i, (idx, p) in enumerate(self.df.iterrows()): for i, (idx, p) in enumerate(df.iterrows()): x_center = xbins[i] y_center = ybins[i] cell = grid[y_center, x_center] Loading @@ -167,7 +144,7 @@ class SpatialSuppression(Observable): if cell == False: result.append(idx) pts.append((p[['x', 'y']])) if len(result) > self.k + self.k * self.error_k: if len(result) > k + k * error_k: # Too many points, break min_idx = mid_idx break Loading @@ -193,26 +170,21 @@ class SpatialSuppression(Observable): x_min: x_max] = True # Check break conditions if self.k - self.k * self.error_k <= len(result) <= self.k + self.k * self.error_k: if k - k * error_k <= len(result) <= k + k * error_k: process = False elif len(result) < self.k - self.k * self.error_k: elif len(result) < k - k * error_k: # The radius is too large max_idx = mid_idx if max_idx == 0: warnings.warn('Unable to retrieve {} points. Consider reducing the amount of points you request(k)' .format(self.k)) .format(k)) process = False if min_idx == max_idx: process = False self.mask = pd.Series(False, self.df.index) self.mask.loc[list(result)] = True state_package = {'mask': self.mask, 'k': self.k, 'error_k': self.error_k} self._action_stack.append(state_package) self._notify_subscribers(self) self._current_action_stack = len(self._action_stack) - 1 # 0 based vs. 1 based mask = pd.Series(False, df.index) mask.loc[list(result)] = True return mask, k def self_neighbors(matches): Loading
autocnet/matcher/subpixel.py +0 −130 Original line number Diff line number Diff line Loading @@ -87,133 +87,3 @@ def subpixel_offset(template, search, **kwargs): x_offset, y_offset, strength = functions[method](template, search, **kwargs) return x_offset, y_offset, strength ''' Stub for an observable subpixel class class PatternMatch(Observable): """ Attributes ---------- df : dataframe A dataframe of point to be subpixel registered img1 : object or ndarray A file handle object or ndarray to use to subpixel register img2 : object or ndarray A file handle object or ndarray to use to subpixel register destination : object Destination node threshold_mask : series A pandas series masking values < threshold shift_mask : series A pandas series masking values with shifts larger than the allowed x, y shifts subpixel_mask : series A composite mask, threshold_mask & shift_mask """ def __init__(self, img1, img2, df, min_x_shift=-1.0, max_x_shift=1.0, min_y_shift=-1.0, max_y_shift=1.0, threshold=0.8): self.img1 = img1 self.img2 = img2 self.df = df self._min_x_shift = min_x_shift self._min_y_shift = min_y_shift self._max_x_shift = max_x_shift self._max_y_shift = max_y_shift self._threshold = threshold self.threshold_mask = pd.Series(True, index=self.df.index) self.shift_mask = pd.Series(True, index=self.df.index) self.subpixel_mask = self.threshold_mask & self.subpixel_mask self._action_stack = deque(maxlen=20) self._current_action_stack = 0 self._observers = set() self.attrs = ['threshold', 'min_x_shift', 'max_x_shift', 'min_y_shift', 'm_y_shift', 'threshold_mask', 'shift_mask', 'subpixel_mask'] def clip_roi(self, img, center): @property def threshold(self): return self._threshold @threshold.setter def threshold(self, v): if 0 <= v <= 1: self._threshold = v # Update the mask here self.threshold_mask = self.d current_state = self._action_stack[self._current_action_stack] current_state['threshold'] = self.threshold self._update_stack(current_state) @property def min_x_shift(self): return self._min_x_shift @min_x_shift.setter def min_x_shift(self, v): self._min_x_shift = v # Update mask here current_state = self._action_stack[self._current_action_stack] current_state['min_x_shift'] = self.min_x_shift self._update_stack() @property def min_y_shift(self): return self._min_y_shift @min_x_shift.setter def min_y_shift(self, v): self._min_y_shift = v # Update mask here current_state = self._action_stack[self._current_action_stack] current_state['min_y_shift'] = self.min_y_shift self._update_stack() @property def max_x_shift(self): return self._max_x_shift @max_x_shift.setter def max_x_shift(self, v): self._max_x_shift = v # Update mask here current_state = self._action_stack[self._current_action_stack] current_state['max_x_shift'] = self.max_x_shift self._update_stack() @property def max_y_shift(self): return self._max_y_shift @max_y_shift.setter def max_y_shift(self, v): self._max_y_shift = v # Update mask here current_state = self._action_stack[self._current_action_stack] current_state['max_y_shift'] = self.max_y_shift self._update_stack() '''
autocnet/matcher/tests/test_outlier_detector.py +15 −34 Original line number Diff line number Diff line Loading @@ -7,7 +7,6 @@ import numpy as np import pandas as pd from .. import outlier_detector from autocnet.matcher.outlier_detector import SpatialSuppression sys.path.append(os.path.abspath('..')) Loading Loading @@ -53,37 +52,23 @@ class TestSpatialSuppression(unittest.TestCase): y = seed.randint(0, 100, 100).astype(np.float32) strength = seed.rand(100) data = np.vstack((x, y, strength)).T df = pd.DataFrame(data, columns=['x', 'y', 'strength']) self.suppression_obj = outlier_detector.SpatialSuppression(df, (100, 100), k=25) def test_properties(self): self.assertEqual(self.suppression_obj.k, 25) self.suppression_obj.k = 26 self.assertTrue(self.suppression_obj.k, 26) self.assertEqual(self.suppression_obj.error_k, 0.1) self.suppression_obj.error_k = 0.05 self.assertEqual(self.suppression_obj.error_k, 0.05) self.assertEqual(self.suppression_obj.nvalid, 0) self.assertIsInstance(self.suppression_obj.df, pd.DataFrame) self.df = pd.DataFrame(data, columns=['x', 'y', 'strength']) self.domain = (100,100) def test_suppress_non_optimal(self): with warnings.catch_warnings(record=True) as w: self.suppression_obj.suppress() mask, k = outlier_detector.spatial_suppression(self.df, self.domain, k=25) self.assertEqual(len(w), 1) self.assertEqual(w[0].category, UserWarning) self.assertEqual(self.suppression_obj.mask.sum(), 28) self.assertEqual(mask.sum(), 28) def test_suppress(self): self.suppression_obj.k = 30 self.suppression_obj.suppress() self.assertIn(self.suppression_obj.mask.sum(), list(range(27, 35))) mask, k = outlier_detector.spatial_suppression(self.df, self.domain, k=30) self.assertIn(mask.sum(), list(range(27, 35))) with warnings.catch_warnings(record=True) as w: self.suppression_obj.k = 101 self.suppression_obj.suppress() mask, k = outlier_detector.spatial_suppression(self.df, self.domain, k=101) self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[0].category, UserWarning)) Loading @@ -95,24 +80,20 @@ class testSuppressionRanges(unittest.TestCase): def test_min_max(self): df = pd.DataFrame(self.r.uniform(0,2,(500, 3)), columns=['x', 'y', 'strength']) sup = SpatialSuppression(df, (1.5,1.5), k = 1) sup.suppress() self.assertEqual(len(df[sup.mask]), 1) mask, k = outlier_detector.spatial_suppression(df, (1.5,1.5), k = 1) self.assertEqual(len(df[mask]), 1) def test_point_overload(self): df = pd.DataFrame(self.r.uniform(0,15,(500, 3)), columns=['x', 'y', 'strength']) sup = SpatialSuppression(df, (15,15), k = 200) sup.suppress() self.assertEqual(len(df[sup.mask]), 69) mask, k = outlier_detector.spatial_suppression(df, (15,15), k = 200) self.assertEqual(len(df[mask]), 69) def test_small_distribution(self): df = pd.DataFrame(self.r.uniform(0,25,(500, 3)), columns=['x', 'y', 'strength']) sup = SpatialSuppression(df, (25,25), k = 25) sup.suppress() self.assertEqual(len(df[sup.mask]), 28) mask, k = outlier_detector.spatial_suppression(df, (25,25), k = 25) self.assertEqual(len(df[mask]), 28) def test_normal_distribution(self): df = pd.DataFrame(self.r.uniform(0,100,(500, 3)), columns=['x', 'y', 'strength']) sup = SpatialSuppression(df, (100,100), k = 15) sup.suppress() self.assertEqual(len(df[sup.mask]), 17) mask, k = outlier_detector.spatial_suppression(df, (100,100), k = 15) self.assertEqual(len(df[mask]), 17)