diff --git a/changelog.md b/changelog.md index a32fff5..06c80cd 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ ## ROC'n'ROLL +### Version 0.19.2 + Fixed calculation of distance between a mouse and a point + +### Version 0.19.1 + Code style and unit test fixes + +### Version 0.19.0 + The point values now have a range of 0 to 100 instead of 50 to 800 + ### Version 0.18.1 Fixed metrics layout diff --git a/setup.py b/setup.py index 2dfdab5..30a2b2f 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ name="ROC'n'ROLL", packages=['src'], author='SoftwareEngineeringDreamTeam', - version='0.18.1', + version='0.19.2', install_requires=[ 'numpy', 'dearpygui', diff --git a/src/app.py b/src/app.py index 8e6de9b..d459f1d 100644 --- a/src/app.py +++ b/src/app.py @@ -26,8 +26,8 @@ def _load_file(self, sender, app_data): def _save_file(self, sender, app_data): self.data.save(app_data['file_path_name']) - def _add_new_point(self, x_pos, value, popup): - self.data.add_point(x_pos, value) + def _add_new_point(self, value, label, popup): + self.data.add_point(value, label) self.axis.render_new_point() dpg.delete_item(popup) @@ -39,14 +39,16 @@ def _show_add_point_popup(self, sender, app_data): on_close=lambda: dpg.delete_item(popup) ) as popup: dpg.add_input_float( - tag="new_point_x_pos", - min_value=50, - max_value=800, + tag="new_point_value", + min_value=0, + max_value=100, + min_clamped=True, + max_clamped=True, step=1, default_value=0 ) dpg.add_checkbox( - tag="new_point_value", + tag="new_point_label", label="Class", default_value=False, ) @@ -55,8 +57,8 @@ def _show_add_point_popup(self, sender, app_data): label="Add", width=150, callback=lambda sender, app_data, user_data: self._add_new_point( - x_pos=dpg.get_value("new_point_x_pos"), value=dpg.get_value("new_point_value"), + label=dpg.get_value("new_point_label"), popup=popup ) ) diff --git a/src/axis.py b/src/axis.py index b46b6a5..12c74d9 100644 --- a/src/axis.py +++ b/src/axis.py @@ -3,6 +3,7 @@ # pylint: disable=unused-import # pylint: disable=import-error # pylint: disable=W +# pylint: disable=duplicate-code import copy @@ -10,6 +11,8 @@ from src.__init__ import dpg +from src.utils import remap + class Axis: thickness = None @@ -20,6 +23,8 @@ def __init__(self, data_ref): self.choosen_value = True self.start = 50 self.end = 800 + self.min = 0 + self.max = 100 def setup_axis(self): self.data_ref.init_axis_data() @@ -27,13 +32,10 @@ def setup_axis(self): def generate_random_points(self): old_points = copy.deepcopy(self.data_ref.points) - self.data_ref.generate_random_points(self.start, self.end) + self.data_ref.generate_random_points(self.min, self.max) self.override_points(old_points) self.data_ref.update() - def add_point(self, mouse_x_position): - self.data_ref.add_point(mouse_x_position, self.choosen_value) - def update_point(self, point): point.update_dragged_point() self.data_ref.update_point_moved() @@ -90,7 +92,7 @@ def check_interaction(self): self.data_ref.update() elif self.holding == threshhold: threshhold.update_dragged_threshhold() - self.data_ref.update() + self.data_ref.update_point_moved() else: for point in self.data_ref.points: if point.bounds_check() and not self.holding: @@ -123,23 +125,29 @@ def __show_popup_for(self, item): ) dpg.add_checkbox( label="Class", - default_value=item.get_value(), + default_value=item.get_label(), callback=lambda sender, app_data, user_data: item.flip_class() ) dpg.add_input_float( - min_value=50, - max_value=800, + min_value=0, + max_value=100, + min_clamped=True, + max_clamped=True, step=1, - default_value=item.get_position()[0], + default_value=item.get_value(), callback=lambda sender, app_data, user_data: - item.update_point_position(app_data) + item.update_point_value(app_data) ) elif isinstance(item, Threshold): dpg.add_input_float( min_value=0, - max_value=1 + max_value=100, + min_clamped=True, + max_clamped=True, + step=1, + default_value=item.get_value(), ) @@ -176,17 +184,20 @@ def get_half_length(self): class Point(Entity): radius = 10 - def __init__(self, x_pos, val): - if val: + def __init__(self, value, label): + self.value = value + x_pos = remap(value, 0, 100, 50, 800) + + if label: super().__init__(x_pos, self._green, self.radius) else: super().__init__(x_pos, self._red, self.radius) - self.value = val + self.label = label self.point = None def draw(self): self.point = dpg.draw_circle( - (self.x_pos, self.y_pos), + (self.x_pos - self.radius, self.y_pos), radius=self.radius, color=self.color, fill=self.color, @@ -202,17 +213,21 @@ def update_dragged_point(self): center=(self.x_pos - self.radius, self.y_pos) ) - def update_point_position(self, x_pos): - self.set_position(x_pos, self.y_pos) + self.value = remap(self.x_pos, 50, 800, 0, 100) + + def update_point_value(self, value): + self.value = value + self.x_pos = remap(value, 0, 100, 50, 800) + self.set_position(self.x_pos, self.y_pos) dpg.configure_item( self.point, center=(self.x_pos - self.radius, self.y_pos) ) def flip_class(self): - self.value = not self.value + self.label = not self.label - if self.value: + if self.label: self.color = self._green else: self.color = self._red @@ -223,21 +238,24 @@ def flip_class(self): fill=self.color ) - def get_value(self): - return self.value + def get_label(self): + return self.label def get_x_pos(self): return self.x_pos + def get_value(self): + return self.value + def _circle_distance(self, point_a, point_b): return sqrt((point_a)**2 + (point_b)**2) - def bounds_check(self, max_distance=20): + def bounds_check(self): dist = self._circle_distance( - (dpg.get_mouse_pos()[0] - self.x_pos - self.radius/2), - (dpg.get_mouse_pos()[1] - self.y_pos) + (dpg.get_mouse_pos()[0] - self.x_pos), + (dpg.get_mouse_pos()[1] - self.y_pos + self.radius), ) - if dist < max_distance: + if dist <= self.radius: return True return False @@ -248,8 +266,10 @@ def delete(self): class Threshold(Entity): - def __init__(self, x_pos): - super().__init__(x_pos, (230, 230, 230), 20) + def __init__(self, value): + self.value = value + x_pos = remap(value, 0, 100, 50, 800) + super().__init__(x_pos, (230, 230, 230), 10) self.thickness = 6 self.line = None @@ -280,3 +300,21 @@ def update_dragged_threshhold(self): p1=[self.x_pos - self.thickness * 3 / 2, self.y_pos - self.half_length], p2=[self.x_pos - self.thickness * 3 / 2, self.y_pos + self.half_length] ) + + self.value = remap(self.x_pos, 50, 800, 0, 100) + + def update_threshold_value(self, value): + self.value = value + self.x_pos = remap(value, 0, 100, 50, 800) + self.set_position(self.x_pos, self.y_pos) + dpg.configure_item( + item=self.line, + p1=[self.x_pos - self.thickness * 3 / 2, self.y_pos - self.half_length], + p2=[self.x_pos - self.thickness * 3 / 2, self.y_pos + self.half_length] + ) + + def get_x_pos(self): + return self.x_pos + + def get_value(self): + return self.value diff --git a/src/data.py b/src/data.py index 35afa23..dc5914d 100644 --- a/src/data.py +++ b/src/data.py @@ -68,22 +68,22 @@ def load_points(self, source_file_name): if len(row) == 2: # check if float if row[0].replace('.', '', 1).isdigit(): - x_pos = int(float(row[0])) + value = float(row[0]) # check if int elif row[0].isdigit(): - x_pos = int(row[0]) + value = int(row[0]) else: # if it is not a number move to next iteration continue if row[1].lstrip('-').isdigit(): # lstrip to handle -1 case - val = not (int(row[1]) == 0 or int(row[1]) == -1) + label = not (int(row[1]) == 0 or int(row[1]) == -1) elif row[1].lower() == 'true': - val = True + label = True elif row[1].lower() == 'false': - val = False + label = False else: continue - self.add_point(x_pos, val) + self.add_point(value, label) return old_points @@ -165,13 +165,13 @@ def __update_metrics_panel_live(self): self.__mcc_score[1]] self.metrics_panel.update_live(vals) - def add_point(self, x_position, value, update=True): - self.points.append(Point(x_position, value)) + def add_point(self, value, label, update=True): + self.points.append(Point(value, label)) self.update() def switch_points_values(self, update=True): for point in self.points: - point.value = not point.value + point.label = not point.label def delete_point(self, point, update=True): self.points.remove(point) @@ -182,16 +182,16 @@ def generate_random_points(self, min, max): self.points = generate_example_points((min, max), Point) def __init__threshold(self): - self.threshold = Threshold(400) + self.threshold = Threshold(50) def _get_points_as_arrays(self): y_true = np.zeros(len(self.points)) self.y_vals = np.zeros(len(self.points)) for i, point in enumerate(self.points): - self.y_vals[i] = point.x_pos - y_true[i] = point.get_value() + self.y_vals[i] = point.get_value() + y_true[i] = point.get_label() self.y_pred = self.metrics.convert_to_binary(self.y_vals, - self.threshold.x_pos) + self.threshold.get_value()) self.y_true = y_true.astype(int) @property @@ -255,7 +255,7 @@ def __update_precision(self): def __update_precision_live(self): if self._old_precision is None: - self._precision = self.__precision + self._precision = self.__precision else: cur_precision = self.metrics.calculate_precision( self.y_pred, @@ -408,7 +408,8 @@ def __update_confusion_matrix_live(self): [self.__false_neg, self.__true_neg] ] ) - self._confusion_matrix = [self._old_confusion_matrix[1], cur_matrix] + self._confusion_matrix = [self._old_confusion_matrix[1], + cur_matrix] def __update_confusion_matrix(self): self._old_confusion_matrix = self.__confusion_matrix @@ -469,7 +470,6 @@ def __update_roc_curve_live(self): else: self._roc_curve = [self._old_roc_curve[1], cur_roc] - @property def __auc_score(self): if self._auc_score is None: @@ -482,7 +482,7 @@ def __update_auc_score(self): self._old_auc_score = self.__auc_score cur_auc = self.metrics.calculate_auc(self.__roc_curve[1]["fpr"], self.__roc_curve[1]["tpr"]) - self._auc = [self._old_auc_score[1], cur_auc] + self._auc_score = [self._old_auc_score[1], cur_auc] def __update_auc_score_live(self): if self._old_auc_score is None: @@ -492,12 +492,12 @@ def __update_auc_score_live(self): self.__roc_curve[1]["fpr"], self.__roc_curve[1]["tpr"] ) - self._auc_scoree = [self._old_auc_score[1], cur_auc] + self._auc_score = [self._old_auc_score[1], cur_auc] def save(self, full_file_path: str): with open(full_file_path, 'w') as file: writer = csv.writer(file) - writer.writerow(('x_pos', 'val')) + writer.writerow(('value', 'label')) for point in self.points: - writer.writerow((point.get_x_pos(), int(point.get_value()))) + writer.writerow((point.get_value(), int(point.get_label()))) diff --git a/src/plot.py b/src/plot.py index 10c6f9f..a9398d6 100644 --- a/src/plot.py +++ b/src/plot.py @@ -64,10 +64,6 @@ def update(self, roc): def save_to_png(self): fig = graph_obj.Figure() - fig.add_trace(graph_obj.Scatter(x=self.prev_roc["fpr"], - y=self.prev_roc["tpr"], - name="Previous ROC Curve", - mode='lines')) fig.add_trace(graph_obj.Scatter(x=self.roc["fpr"], y=self.roc["tpr"], name="ROC Curve", diff --git a/src/utils.py b/src/utils.py index b96b4d2..ba7e103 100644 --- a/src/utils.py +++ b/src/utils.py @@ -22,3 +22,10 @@ def generate_example_points( )[0] ) for _ in range(nr_of_points) ] + +def remap(number, old_min, old_max, new_min, new_max): + old_range = (old_max - old_min) + new_range = (new_max - new_min) + new_number = (((number - old_min) * new_range) / old_range) + new_min + + return new_number diff --git a/tests/unit_tests/utils_test.py b/tests/unit_tests/utils_test.py index 222988a..be34011 100644 --- a/tests/unit_tests/utils_test.py +++ b/tests/unit_tests/utils_test.py @@ -4,36 +4,36 @@ from src.utils import generate_example_points def test_utils_value_range(): - min_val = 50 - max_val = 800 + min_val = 0 + max_val = 100 generated_points = generate_example_points((min_val, max_val), Point) for point in generated_points: - assert point.x_pos >= 50 and point.x_pos <= 800 + assert point.value >= 0 and point.value <= 100 def test_utils_point_class(): - min_val = 50 - max_val = 800 + min_val = 0 + max_val = 100 generated_points = generate_example_points((min_val, max_val), Point) for point in generated_points: assert isinstance(point, Point) def test_utils_number_of_points(): - min_val = 50 - max_val = 800 + min_val = 0 + max_val = 100 nr_of_points = 10 generated_points = generate_example_points((min_val, max_val), Point, nr_of_points) assert len(generated_points) == nr_of_points def test_utils_always_true(): - min_val = 50 - max_val = 800 + min_val = 0 + max_val = 100 generated_points = generate_example_points((min_val, max_val), Point, true_or_false_prc = 1.0) for point in generated_points: - assert point.value == True + assert point.label == True def test_utils_always_false(): - min_val = 50 - max_val = 800 + min_val = 0 + max_val = 100 generated_points = generate_example_points((min_val, max_val), Point, true_or_false_prc = 0.0) for point in generated_points: - assert point.value == False + assert point.label == False