Skip to content

References

Visualizer

Attributes:

Name Type Description
df

pandas dataframe containing all data to be visualized.

embedding_features

list of features used for embedding.

hover features

list of features shown while hovering.

target features

feature used to create traces (same target value - same trace).

path_to_structures

true if dataframe contains a 'structure' columns with path to structures.

Source code in nomad_visu/__init__.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
class Visualizer:
    """
    Visualizer

    Attributes:
        df: pandas dataframe containing all data to be visualized.
        embedding_features: list of features used for embedding.
        hover features: list of features shown while hovering.
        target: feature used to create traces (same target value - same trace).
        path_to_structures: true if dataframe contains a 'structure' columns with path to structures.
    """

    def __init__(
        self,
        df: pd.DataFrame,
        embedding_features: list[str],
        hover_features: list[str],
        target: str,
        path_to_structures: bool = False,
    ):

        self.path_to_structures = path_to_structures

        self.visualizer_config_widgets = ConfigWidgets()
        self.visualizer_figure = Figure(
            df, embedding_features, hover_features, target, path_to_structures
        )

        ConfigWidgets.hover_features = hover_features
        ConfigWidgets.embedding_features = embedding_features
        ConfigWidgets.feat_x = ConfigWidgets.embedding_features[0]
        ConfigWidgets.feat_y = ConfigWidgets.embedding_features[1]
        ConfigWidgets.fract = self.visualizer_figure.init_fract

        self.visualizer_top_widgets = TopWidgets()
        self.visualizer_utils_widgets = UtilsWidgets()
        self.visualizer_viewers_widgets = ViewersWidgets()
        self.visualizer_utils_button = UtilsButton()

        self.visualizer_top_widgets.observe_changes(
            self.visualizer_figure, self.visualizer_utils_widgets
        )
        self.visualizer_utils_widgets.observe_changes(self.visualizer_figure)
        self.visualizer_viewers_widgets.observe_changes(self.visualizer_figure)
        self.visualizer_utils_button.observe_changes(
            self.visualizer_figure,
            self.visualizer_utils_widgets,
            self.visualizer_viewers_widgets,
        )

    def show(self):
        """
        Displays the map and all widgets.

        """


        top_box = self.visualizer_top_widgets.widg_box
        figure_widget = self.visualizer_figure.FigureWidget
        utils_box = self.visualizer_utils_widgets.widg_box
        utils_button = self.visualizer_utils_button.widget
        viewer_box = self.visualizer_viewers_widgets.widg_box

        top_box.layout.height = "140px"
        top_box.layout.top = "30px"
        utils_button.layout.left = "50px"
        utils_box.layout.border = "dashed 1px"
        utils_box.layout.max_width = "700px"
        utils_box.layout.visibility = "hidden"

        # Structure visualizer is displayed only if there is a path to structures
        if self.path_to_structures:
            container = widgets.VBox(
                [
                    top_box,
                    figure_widget,
                    utils_button,
                    viewer_box,
                    utils_box,
                ]
            )

        else:
            utils_box.layout.top = "10px"
            container = widgets.VBox(
                [
                    top_box,
                    figure_widget,
                    utils_button,
                    utils_box,
                ]
            )

        self.visualizer_figure.batch_update(self.visualizer_config_widgets)

        display(container)

        if self.path_to_structures:
            with self.visualizer_viewers_widgets.windows_output_l.widget:
                self.visualizer_viewers_widgets.viewer_l.viewer.show()
            with self.visualizer_viewers_widgets.windows_output_r.widget:
                self.visualizer_viewers_widgets.viewer_r.viewer.show()

    def add_convex_hull(self):
        """
        Add convex hull to the map.
        """

        self.visualizer_figure.convex_hull = True
        self.visualizer_utils_widgets.color_hull.widget.disabled = False
        self.visualizer_utils_widgets.width_hull.widget.disabled = False
        self.visualizer_utils_widgets.dash_hull.widget.disabled = False

        self.visualizer_figure.batch_update(self.visualizer_config_widgets)

    def remove_convex_hull(self):
        """
        Remove convex hull from the map.
        """

        self.visualizer_figure.convex_hull = False
        self.visualizer_utils_widgets.color_hull.widget.disabled = True
        self.visualizer_utils_widgets.width_hull.widget.disabled = True
        self.visualizer_utils_widgets.dash_hull.widget.disabled = True

        self.visualizer_figure.batch_update(self.visualizer_config_widgets)

    def add_regr_line(self, coefs, feat_x, feat_y):
        """
        Add regression line to the map.
        """

        self.visualizer_figure.add_regr_line(
            coefs,
            feat_x,
            feat_y,
            self.visualizer_config_widgets,
            self.visualizer_utils_widgets.color_line.widget,
            self.visualizer_utils_widgets.width_line.widget,
            self.visualizer_utils_widgets.dash_line.widget,
        )

    def remove_regr_line(self, feat_x, feat_y):
        """
        Remove regression line from the map.

        """
        self.visualizer_figure.remove_regr_line(
            feat_x,
            feat_y,
            self.visualizer_config_widgets,
            self.visualizer_utils_widgets.color_line.widget,
            self.visualizer_utils_widgets.width_line.widget,
            self.visualizer_utils_widgets.dash_line.widget,
        )

    def optimize_fract(self):
        """
        Optimize fractional .
        """
        self.visualizer_figure.optimize_fract(
            self.visualizer_top_widgets, self.visualizer_config_widgets
        )

add_convex_hull()

Add convex hull to the map.

Source code in nomad_visu/__init__.py
122
123
124
125
126
127
128
129
130
131
132
def add_convex_hull(self):
    """
    Add convex hull to the map.
    """

    self.visualizer_figure.convex_hull = True
    self.visualizer_utils_widgets.color_hull.widget.disabled = False
    self.visualizer_utils_widgets.width_hull.widget.disabled = False
    self.visualizer_utils_widgets.dash_hull.widget.disabled = False

    self.visualizer_figure.batch_update(self.visualizer_config_widgets)

add_regr_line(coefs, feat_x, feat_y)

Add regression line to the map.

Source code in nomad_visu/__init__.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def add_regr_line(self, coefs, feat_x, feat_y):
    """
    Add regression line to the map.
    """

    self.visualizer_figure.add_regr_line(
        coefs,
        feat_x,
        feat_y,
        self.visualizer_config_widgets,
        self.visualizer_utils_widgets.color_line.widget,
        self.visualizer_utils_widgets.width_line.widget,
        self.visualizer_utils_widgets.dash_line.widget,
    )

optimize_fract()

Optimize fractional .

Source code in nomad_visu/__init__.py
175
176
177
178
179
180
181
def optimize_fract(self):
    """
    Optimize fractional .
    """
    self.visualizer_figure.optimize_fract(
        self.visualizer_top_widgets, self.visualizer_config_widgets
    )

remove_convex_hull()

Remove convex hull from the map.

Source code in nomad_visu/__init__.py
134
135
136
137
138
139
140
141
142
143
144
def remove_convex_hull(self):
    """
    Remove convex hull from the map.
    """

    self.visualizer_figure.convex_hull = False
    self.visualizer_utils_widgets.color_hull.widget.disabled = True
    self.visualizer_utils_widgets.width_hull.widget.disabled = True
    self.visualizer_utils_widgets.dash_hull.widget.disabled = True

    self.visualizer_figure.batch_update(self.visualizer_config_widgets)

remove_regr_line(feat_x, feat_y)

Remove regression line from the map.

Source code in nomad_visu/__init__.py
161
162
163
164
165
166
167
168
169
170
171
172
173
def remove_regr_line(self, feat_x, feat_y):
    """
    Remove regression line from the map.

    """
    self.visualizer_figure.remove_regr_line(
        feat_x,
        feat_y,
        self.visualizer_config_widgets,
        self.visualizer_utils_widgets.color_line.widget,
        self.visualizer_utils_widgets.width_line.widget,
        self.visualizer_utils_widgets.dash_line.widget,
    )

show()

Displays the map and all widgets.

Source code in nomad_visu/__init__.py
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
def show(self):
    """
    Displays the map and all widgets.

    """


    top_box = self.visualizer_top_widgets.widg_box
    figure_widget = self.visualizer_figure.FigureWidget
    utils_box = self.visualizer_utils_widgets.widg_box
    utils_button = self.visualizer_utils_button.widget
    viewer_box = self.visualizer_viewers_widgets.widg_box

    top_box.layout.height = "140px"
    top_box.layout.top = "30px"
    utils_button.layout.left = "50px"
    utils_box.layout.border = "dashed 1px"
    utils_box.layout.max_width = "700px"
    utils_box.layout.visibility = "hidden"

    # Structure visualizer is displayed only if there is a path to structures
    if self.path_to_structures:
        container = widgets.VBox(
            [
                top_box,
                figure_widget,
                utils_button,
                viewer_box,
                utils_box,
            ]
        )

    else:
        utils_box.layout.top = "10px"
        container = widgets.VBox(
            [
                top_box,
                figure_widget,
                utils_button,
                utils_box,
            ]
        )

    self.visualizer_figure.batch_update(self.visualizer_config_widgets)

    display(container)

    if self.path_to_structures:
        with self.visualizer_viewers_widgets.windows_output_l.widget:
            self.visualizer_viewers_widgets.viewer_l.viewer.show()
        with self.visualizer_viewers_widgets.windows_output_r.widget:
            self.visualizer_viewers_widgets.viewer_r.viewer.show()

Bases: ConfigWidgets

Source code in nomad_visu/top_widgets/__init__.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
class TopWidgets(ConfigWidgets):
    def __init__(self):

        self.featx = Featx()
        self.featy = Featy()
        self.fract_slider = FractSlider()
        self.label_fract = LabelFract()
        self.feat_color = FeatColor()
        self.feat_color_type = FeatColorType()
        self.feat_color_list = FeatColorList()
        self.feat_marker = FeatMarker()
        self.feat_marker_min = FeatMarkerMin()
        self.feat_marker_min_label = FeatMarkerMinLabel()
        self.feat_marker_max = FeatMarkerMax()
        self.feat_marker_max_label = FeatMarkerMaxLabel()

        self.widg_box = widgets.VBox(
            [
                widgets.HBox(
                    [
                        widgets.VBox(
                            [
                                self.featx.widget,
                                self.featy.widget,
                                widgets.HBox(
                                    [self.label_fract.widget, self.fract_slider.widget]
                                ),
                            ]
                        ),
                        widgets.VBox(
                            [
                                self.feat_color.widget,
                                widgets.HBox(
                                    [
                                        self.feat_color_type.widget,
                                        self.feat_color_list.widget,
                                    ],
                                    layout=widgets.Layout(top="10px"),
                                ),
                            ]
                        ),
                        widgets.VBox(
                            [
                                self.feat_marker.widget,
                                widgets.VBox(
                                    [
                                        widgets.HBox(
                                            [
                                                self.feat_marker_min_label.widget,
                                                self.feat_marker_min.widget,
                                            ],
                                        ),
                                        widgets.HBox(
                                            [
                                                self.feat_marker_max_label.widget,
                                                self.feat_marker_max.widget,
                                            ],
                                        ),
                                    ]
                                ),
                            ]
                        ),
                    ]
                ),
            ]
        )

    def observe_changes(self, figure, visualizer_utils_widgets):

        self.featx.observe_change(
            figure,
            self.fract_slider.widget,
            visualizer_utils_widgets.color_line.widget,
            visualizer_utils_widgets.width_line.widget,
            visualizer_utils_widgets.dash_line.widget,
        )
        self.featy.observe_change(
            figure,
            self.fract_slider.widget,
            visualizer_utils_widgets.color_line.widget,
            visualizer_utils_widgets.width_line.widget,
            visualizer_utils_widgets.dash_line.widget,
        )
        self.fract_slider.observe_change(figure)
        self.feat_color.observe_change(
            figure, self.feat_color_type.widget, self.feat_color_list.widget
        )
        self.feat_color_type.observe_change(figure, self.feat_color_list.widget)
        self.feat_color_list.observe_change(figure)
        self.feat_marker.observe_change(
            figure, self.feat_marker_min.widget, self.feat_marker_max.widget
        )
        self.feat_marker_min.observe_change(figure, self.feat_marker_max.widget)
        self.feat_marker_max.observe_change(figure, self.feat_marker_min.widget)

Bases: object

Source code in nomad_visu/config_widgets.py
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
class ConfigWidgets(object):

    # Default value of the background color
    bg_color_default = "rgba(229,236,246, 0.5)"
    # List of possible marker symbols
    symbols_list = [
        "circle",
        "circle-open",
        "circle-dot",
        "circle-open-dot",
        "circle-cross",
        "circle-x",
        "square",
        "square-open",
        "square-dot",
        "square-open-dot",
        "square-cross",
        "square-x",
        "diamond",
        "diamond-open",
        "diamond-dot",
        "diamond-open-dot",
        "diamond-cross",
        "diamond-x",
        "triangle-up",
        "triangle-up-open",
        "triangle-up-dot",
        "triangle-up-open-dot",
        "triangle-down",
        "triangle-down-open",
        "triangle-down-dot",
        "triangle-down-open-dot",
    ]
    # List of possible colors of the hulls
    color_hull = [
        "Black",
        "Blue",
        "Cyan",
        "Green",
        "Grey",
        "Orange",
        "Red",
        "Yellow",
    ]
    # List of possible colors of the regression line
    color_line = [
        "Black",
        "Blue",
        "Cyan",
        "Green",
        "Grey",
        "Orange",
        "Red",
        "Yellow",
    ]
    # List of possible dash types for the regression line
    line_dashs = ["dash", "solid", "dot", "longdash", "dashdot", "longdashdot"]
    # List of possible dash types for the hulls
    hull_dashs = ["dash", "solid", "dot", "longdash", "dashdot", "longdashdot"]
    # List of possible font families
    font_families = [
        "Arial",
        "Courier New",
        "Helvetica",
        "Open Sans",
        "Times New Roman",
        "Verdana",
    ]
    # List of possible font colors
    font_colors = [
        "Black",
        "Blue",
        "Cyan",
        "Green",
        "Grey",
        "Orange",
        "Red",
        "Yellow",
    ]
    # List of possible discrete palette colors
    discrete_palette_colors = [
        "Plotly",
        "D3",
        "G10",
        "T10",
        "Alphabet",
        "Dark24",
        "Light24",
        "Set1",
        "Pastel1",
        "Dark2",
        "Set2",
        "Pastel2",
        "Set3",
        "Antique",
        "Bold",
        "Pastel",
        "Prism",
        "Safe",
        "Vivid",
    ]
    # List of possible continuous gradient colors
    continuous_gradient_colors = px.colors.named_colorscales()

    # Values below are initialized to a specific value that can be modified using widgets
    hover_features = []
    embedding_features = []
    feat_x = ""
    feat_y = ""
    marker_size = 7  # size of all markers
    cross_size = 15  # size of the crosses
    min_value_markerfeat = (
        4  # min value of markers size if sizes represent a certain feature value
    )
    max_value_markerfeat = (
        20  # max value of markers size if sizes represent a certain feature value
    )

    font_size = 12  # size of fonts
    hull_width = 1  # width of the  the convex hull
    line_width = 1  # width of the regression line
    hull_dash = "solid"  # dash of the convex hull
    line_dash = "dash"  # dash of the regression line
    hull_color = "Grey"  # color of the convex hull
    line_color = "Black"  # color of the regression line
    bg_color = "rgba(229,236,246, 0.5)"  # background color
    bg_toggle = True  # background color is shown
    structures_list = []
    replica_l = 0  # which file in the list is shown in the left visualizer
    replica_r = 0  # which file in the list is shown in the right visualizer
    fract = 1  # fraction of points visualized on the map
    palette = cycle(
        getattr(px.colors.qualitative, discrete_palette_colors[0])
    )  #  color palette used for the initial values
    color_palette = discrete_palette_colors[0]
    font_family = font_families[0]
    font_color = font_colors[0]

    structure_text_l = "..."
    structure_text_r = "..."

    featmarker = "Default size"  # feature used for markers size
    featcolor = "Default color"  # feature used for markers color
    featcolor_type = "Gradient"  # if a feature is used for color this can be 'Gradient' for continuous feature values or 'Discrete'
    featcolor_list = "viridis"  # color palette used for features

Bases: ConfigWidgets

Source code in nomad_visu/utils_widgets/__init__.py
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
class UtilsWidgets(ConfigWidgets):
    def __init__(self):

        self.bg_color = BgColor()
        self.bg_color_update = BgColorUpdate()
        self.bg_toggle = BgToggle()
        self.color_hull = ColorHull()
        self.color_line = ColorLine()
        self.color_palette = ColorPalette()
        self.cross_size = CrossSize()
        self.dash_hull = DashHull()
        self.dash_line = DashLine()
        self.font_color = FontColor()
        self.font_family = FontFamily()
        self.font_size = FontSize()
        self.markers_size = MarkersSize()
        self.markers_symbol = MarkersSymbol()
        self.plot_format = PlotFormat()
        self.plot_name = PlotName()
        self.plot_resolution = PlotResolution()
        self.print = Print()
        self.print_label = PrintLabel()
        self.print_output = PrintOutput()
        self.reset_button = ResetButton()
        self.trace_symbol = TraceSymbol()
        self.width_hull = WidthHull()
        self.width_line = WidthLine()
        self.window_label = WindowLabel()

        self.color_hull.widget.disabled = True
        self.width_hull.widget.disabled = True
        self.dash_hull.widget.disabled = True

        self.color_line.widget.disabled = True
        self.width_line.widget.disabled = True
        self.dash_line.widget.disabled = True

        self.widg_box = widgets.VBox(
            [
                widgets.HBox(
                    [
                        self.markers_size.widget,
                        self.cross_size.widget,
                        self.color_palette.widget,
                    ]
                ),
                widgets.HBox(
                    [
                        self.font_size.widget,
                        self.font_family.widget,
                        self.font_color.widget,
                    ]
                ),
                widgets.HBox(
                    [
                        self.trace_symbol.widget,
                        self.markers_symbol.widget,
                        self.reset_button.widget,
                    ]
                ),
                widgets.HBox(
                    [
                        self.color_hull.widget,
                        self.width_hull.widget,
                        self.dash_hull.widget,
                    ]
                ),
                widgets.HBox(
                    [
                        self.color_line.widget,
                        self.width_line.widget,
                        self.dash_line.widget,
                    ]
                ),
                widgets.HBox(
                    [
                        self.bg_toggle.widget,
                        self.bg_color.widget,
                        self.bg_color_update.widget,
                    ]
                ),
                self.print_label.widget,
                widgets.HBox(
                    [
                        self.plot_name.widget,
                        self.plot_format.widget,
                        self.plot_resolution.widget,
                    ]
                ),
                self.print.widget,
                self.print_output.widget,
            ]
        )

    def observe_changes(self, Figure):

        self.bg_color_update.observe_change(Figure, self.bg_color)
        self.bg_toggle.observe_change(Figure)
        self.color_hull.observe_change(Figure)
        self.color_line.observe_change(Figure)
        self.color_palette.observe_change(Figure)
        self.cross_size.observe_change(Figure)
        self.dash_hull.observe_change(Figure)
        self.dash_line.observe_change(Figure)
        self.font_color.observe_change(Figure)
        self.font_family.observe_change(Figure)
        self.font_size.observe_change(Figure)
        self.markers_size.observe_change(Figure)
        self.markers_symbol.observe_change(Figure, self.trace_symbol)
        self.print.observe_change(
            Figure,
            self.print_output,
            self.plot_name,
            self.plot_format,
            self.plot_resolution,
        )
        self.reset_button.observe_change(Figure)
        self.trace_symbol.observe_change(Figure)
        self.width_hull.observe_change(Figure)
        self.width_line.observe_change(Figure)

Bases: ConfigWidgets

Source code in nomad_visu/viewers_widgets/__init__.py
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
class ViewersWidgets(ConfigWidgets):
    def __init__(self):

        self.cross_image_l = CrossImageL()
        self.cross_image_r = CrossImageR()
        self.display_button_l = DisplayButtonL()
        self.display_button_r = DisplayButtonR()
        self.structure_name_l = StructureNameL()
        self.structure_name_r = StructureNameR()
        self.viewer_l = ViewerL()
        self.viewer_r = ViewerR()
        self.windows_checkbox_l = WindowsCheckboxL()
        self.windows_checkbox_r = WindowsCheckboxR()
        self.windows_label = WindowsLabel()
        self.windows_output_l = WindowsOutputL()
        self.windows_output_r = WindowsOutputR()

        self.widg_box = widgets.VBox(
            [
                self.windows_label.widget,
                widgets.HBox(
                    [
                        widgets.VBox(
                            [
                                widgets.HBox(
                                    [
                                        self.structure_name_l.widget,
                                        self.display_button_l.widget,
                                        self.cross_image_l.widget,
                                        self.windows_checkbox_l.widget,
                                    ]
                                ),
                                self.windows_output_l.widget,
                            ]
                        ),
                        widgets.VBox(
                            [
                                widgets.HBox(
                                    [
                                        self.structure_name_r.widget,
                                        self.display_button_r.widget,
                                        self.cross_image_r.widget,
                                        self.windows_checkbox_r.widget,
                                    ]
                                ),
                                self.windows_output_r.widget,
                            ]
                        ),
                    ]
                ),
            ]
        )

    def observe_changes(self, Figure):

        self.display_button_l.observe_change(
            Figure, self.viewer_l, self.structure_name_l, self.windows_output_l
        )
        self.display_button_r.observe_change(
            Figure, self.viewer_r, self.structure_name_r, self.windows_output_r
        )
        self.windows_checkbox_l.observe_change(self.windows_checkbox_r)
        self.windows_checkbox_r.observe_change(self.windows_checkbox_l)

        def handle_point_clicked(trace, points, selector):
            """
            visualizes structure of clicked point and changes its marker symbol to a cross
            """

            if not points.point_inds:
                return

            trace = points.trace_index
            formula = Figure.FigureWidget.data[trace].text[points.point_inds[0]]
            structure = Figure.df.iloc[points.point_inds[0]]["Structure"]

            if self.windows_checkbox_l.widget.value:
                self.structure_name_l.widget.value = formula
                self.viewer_l.view_structure(formula, Figure, self.windows_output_l)
            if self.windows_checkbox_r.widget.value:
                self.structure_name_l.widget.value = formula
                self.viewer_r.view_structure(formula, Figure, self.windows_output_r)

            Figure.batch_update(self)

        if Figure.path_to_structures:
            for name_trace in Figure.name_traces:
                Figure.trace[str(name_trace)].on_click(
                    handle_point_clicked  # actions performed after clicking points on the map
                )

Bases: ConfigWidgets

Source code in nomad_visu/utils_button.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class UtilsButton(ConfigWidgets):
    def __init__(self):

        self.widget = widgets.Button(
            description="For a high-quality print of the plot, click to access the plot appearance utils",
            layout=widgets.Layout(width="600px"),
        )

    def observe_changes(
        self, Figure, visualizer_utils_widgets, visualizer_viewers_widgets
    ):
        def button_clicked(button):
            """
            shows the plot utils box
            """

            if Figure.path_to_structures:
                if visualizer_utils_widgets.widg_box.layout.visibility == "visible":
                    visualizer_utils_widgets.widg_box.layout.visibility = "hidden"
                    for i in range(340, -1, -1):
                        visualizer_viewers_widgets.widg_box.layout.top = str(i) + "px"
                    visualizer_utils_widgets.widg_box.layout.bottom = "0px"
                else:
                    for i in range(341):
                        visualizer_viewers_widgets.widg_box.layout.top = str(i) + "px"
                    visualizer_utils_widgets.widg_box.layout.bottom = "460px"
                    visualizer_utils_widgets.widg_box.layout.visibility = "visible"
            else:
                if visualizer_utils_widgets.widg_box.layout.visibility == "visible":
                    visualizer_utils_widgets.widg_box.layout.visibility = "hidden"
                else:
                    visualizer_utils_widgets.widg_box.layout.visibility = "visible"

        self.widget.on_click(button_clicked)

Bases: object

Source code in nomad_visu/figure/__init__.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
class Figure(object):
    def __init__(
        self, df, embedding_features, hover_features, target, path_to_structures
    ):

        self.df = df.copy()
        self.embedding_features = embedding_features
        self.hover_features = hover_features
        self.path_to_structures = path_to_structures
        self.target = target

        self.FigureWidget = go.FigureWidget()
        # Permanent layout settings are defined here
        self.FigureWidget.update_layout(
            hoverlabel=dict(bgcolor="white", font_size=16, font_family="Rockwell"),
            width=800,
            height=400,
            margin=dict(l=50, r=50, b=70, t=20, pad=4),
        )
        self.FigureWidget.update_xaxes(
            ticks="outside", tickwidth=1, ticklen=10, linewidth=1, linecolor="black"
        )
        self.FigureWidget.update_yaxes(
            ticks="outside", tickwidth=1, ticklen=10, linewidth=1, linecolor="black"
        )

        # The 'target' feature is used to divide data into different traces.
        # Each item in the following dictionaries will be related to a different trace in the dataframe.
        self.name_traces = self.df[target].unique()
        # a pair of features (feat_0, feat_1) returns 'True' if a regression line was added for those features
        self.trace = {}
        # a pair of features (feat_0, feat_1) returns the values of the regression line for those features
        self.regr_line_trace = {}
        # dataframe containing only the elements that are visualized on the map
        self.df_trace_on_map = {}
        self.symbols = {}  # list of symbols used for every marker in each trace
        self.sizes = {}  # list of sizes used for every marker in each trace
        self.colors = {}  # list of colors used for every marker in each trace
        self.trace_symbol = {}  # default symbol used for the trace

        # optimized sequence of entries that is visualized varying the fraction for each pair of features.
        self.optimized_sequence_indexes = {}
        # optimized initial fraction that is visualized for each pair of features.
        self.optimized_init_fract = {}
        # random sequence of entries that is visualized varying the fraction.
        self.random_permutation_indexes = {}

        self.init_fract = 1
        total_points = self.df.shape[0]
        if total_points > 1000:
            # The initial fraction of visualized points is by default 1, unless there are more than 1000 points.
            self.init_fract = 1000 / total_points

        self.convex_hull = False

        if path_to_structures:
            # List of all files found in the directory pointed by 'Structure'.
            self.df["File"] = self.df["Structure"].apply(lambda x: os.listdir(x))
            # Number of files found in the directory pointed by 'Structure'.
            self.df["Replicas"] = self.df["Structure"].apply(
                lambda x: len(os.listdir(x))
            )

        # Dictionaries initialized above are compiled for all different trace names.
        for cl in range(len(self.name_traces)):

            name_trace = str(self.name_traces[cl])

            # A 'Plotly go trace' is constructed and assigned to the 'trace' dictionary.
            self.FigureWidget.add_trace(
                go.Scatter(
                    name=name_trace,
                    mode="markers",
                )
            )
            self.trace[name_trace] = self.FigureWidget["data"][-1]

            # Add convex hull trace.
            name_trace = "Hull " + name_trace
            self.FigureWidget.add_trace(
                go.Scatter(
                    name=name_trace,
                )
            )
            self.trace[name_trace] = self.FigureWidget["data"][-1]

        for name_trace in self.name_traces:

            self.random_permutation_indexes[name_trace] = self.df.loc[
                self.df[target] == name_trace
            ].index.to_numpy()[
                np.random.permutation(
                    self.df.loc[self.df[self.target] == name_trace].shape[0]
                )
            ]

            n_points = self.df.loc[self.df[target] == name_trace].shape[0]

            self.df_trace_on_map[name_trace] = (
                self.df.loc[self.df[target] == name_trace]
                .loc[self.random_permutation_indexes[name_trace]]
                .head(n_points)
            )

            self.trace_symbol[
                name_trace
            ] = "circle"  # Circle is the init symbol used for each trace

    def add_regr_line(
        self,
        coefs,
        feat_x,
        feat_y,
        ConfigWidgets,
        ColorLineWidget,
        WidthLineWidget,
        DashLineWidget,
    ):

        if not (feat_x, feat_y) in self.regr_line_trace:

            self.regr_line_trace[(feat_x, feat_y)] = True
            line_x, line_y = self.make_line(feat_x, feat_y, coefs)

            name_trace = "Regr line" + str(feat_x) + " " + str(feat_y)
            self.FigureWidget.add_trace(
                go.Scatter(
                    name=name_trace,
                    x=line_x,
                    y=line_y,
                )
            )
            self.trace[name_trace] = self.FigureWidget["data"][-1]
            self.FigureWidget.update_traces(
                selector={"name": name_trace}, showlegend=False
            )

        else:

            self.regr_line_trace[(feat_x, feat_y)] = True
            line_x, line_y = self.make_line(feat_x, feat_y, coefs)

            name_trace = "Regr line" + str(feat_x) + " " + str(feat_y)
            self.trace[name_trace].x = line_x
            self.trace[name_trace].y = line_y
            self.FigureWidget.update_traces(
                selector={"name": name_trace}, showlegend=False
            )

        if feat_x == ConfigWidgets.feat_x and feat_y == ConfigWidgets.feat_y:
            ColorLineWidget.disabled = False
            WidthLineWidget.disabled = False
            DashLineWidget.disabled = False

        self.batch_update(ConfigWidgets)

    def remove_regr_line(
        self,
        feat_x,
        feat_y,
        ConfigWidgets,
        ColorLineWidget,
        WidthLineWidget,
        DashLineWidget,
    ):

        self.regr_line_trace[(feat_x, feat_y)] = False

        if feat_x == ConfigWidgets.feat_x and feat_y == ConfigWidgets.feat_y:
            ColorLineWidget.disabled = True
            WidthLineWidget.disabled = True
            DashLineWidget.disabled = True

        self.batch_update(ConfigWidgets)

    def optimize_fract(self, visualizerTopWidgets, ConfigWidgets):

        if (
            not (ConfigWidgets.feat_x, ConfigWidgets.feat_y)
            in self.optimized_sequence_indexes
        ):
            sequence_indexes, init_fract = self.optimize_sequence(
                ConfigWidgets.feat_x, ConfigWidgets.feat_y
            )

            self.optimized_sequence_indexes[
                (ConfigWidgets.feat_x, ConfigWidgets.feat_y)
            ] = sequence_indexes
            self.optimized_sequence_indexes[
                (ConfigWidgets.feat_y, ConfigWidgets.feat_x)
            ] = sequence_indexes
            self.optimized_init_fract[
                (ConfigWidgets.feat_x, ConfigWidgets.feat_y)
            ] = init_fract
            self.optimized_init_fract[
                (ConfigWidgets.feat_y, ConfigWidgets.feat_x)
            ] = init_fract

            ConfigWidgets.fract = init_fract
            visualizerTopWidgets.fract_slider.widget.value = init_fract
            self.batch_update(ConfigWidgets)

    def batch_update(self, ConfigWidgets):
        """
        Updates the layout of the map according to values stored in "ConfigWidgets".
        """

        self.marker_style_updates(ConfigWidgets)
        self.fract_change_updates(ConfigWidgets)

        x_min = []
        x_max = []
        y_min = []
        y_max = []

        for name_trace in self.name_traces:

            x_min.append(min(self.df_trace_on_map[name_trace][ConfigWidgets.feat_x]))
            x_max.append(max(self.df_trace_on_map[name_trace][ConfigWidgets.feat_x]))
            y_min.append(min(self.df_trace_on_map[name_trace][ConfigWidgets.feat_y]))
            y_max.append(max(self.df_trace_on_map[name_trace][ConfigWidgets.feat_y]))

        x_min = min(x_min)
        y_min = min(y_min)
        x_max = max(x_max)
        y_max = max(y_max)
        x_delta = 0.05 * abs(x_max - x_min)
        y_delta = 0.05 * abs(y_max - y_min)

        # range of the x-,y- values that are visualized on the map
        xaxis_range = [x_min - x_delta, x_max + x_delta]
        yaxis_range = [y_min - y_delta, y_max + y_delta]

        if ConfigWidgets.bg_toggle:
            bg_color = ConfigWidgets.bg_color
            gridcolor = "white"
        else:
            bg_color = "white"
            gridcolor = "rgb(229,236,246)"

        with self.FigureWidget.batch_update():

            self.FigureWidget.update_layout(
                showlegend=True,
                plot_bgcolor=bg_color,
                xaxis=dict(gridcolor=gridcolor, showgrid=True, zeroline=False),
                yaxis=dict(gridcolor=gridcolor, showgrid=True, zeroline=False),
                font=dict(
                    size=int(ConfigWidgets.font_size),
                    family=ConfigWidgets.font_family,
                    color=ConfigWidgets.font_color,
                ),
                xaxis_title=ConfigWidgets.feat_x,
                yaxis_title=ConfigWidgets.feat_y,
                xaxis_range=xaxis_range,
                yaxis_range=yaxis_range,
            )

            for name_trace in self.name_traces:
                # all elements on the map and their properties are reinitialized at each change

                self.FigureWidget.update_traces(
                    selector={"name": str(name_trace)},
                    text=self.hover_text[name_trace],
                    customdata=self.hover_custom[name_trace],
                    hovertemplate=self.hover_template[name_trace],
                    x=self.df_trace_on_map[name_trace][ConfigWidgets.feat_x],
                    y=self.df_trace_on_map[name_trace][ConfigWidgets.feat_y],
                    marker=dict(
                        size=self.sizes[name_trace], symbol=self.symbols[name_trace]
                    ),
                )
                if (
                    ConfigWidgets.featcolor != "Default color"
                    and ConfigWidgets.featcolor_type == "Gradient"
                ):
                    feature = ConfigWidgets.featcolor
                    gradient = ConfigWidgets.featcolor_list
                    min_value = self.df[feature].min()
                    max_value = self.df[feature].max()

                    self.FigureWidget.update_traces(
                        selector={"name": str(name_trace)},
                        marker=dict(
                            colorscale=gradient,
                            showscale=True,
                            color=self.colors[name_trace],
                            cmin=min_value,
                            cmax=max_value,
                            colorbar=dict(
                                thickness=10,
                                orientation="v",
                                len=0.5,
                                y=0.25,
                                title=dict(
                                    text=feature, side="right", font={"size": 10}
                                ),
                            ),
                        ),
                    )
                else:
                    self.FigureWidget.update_traces(
                        selector={"name": str(name_trace)},
                        marker=dict(showscale=False, color=self.colors[name_trace]),
                    )
            if (ConfigWidgets.feat_x, ConfigWidgets.feat_y) in self.regr_line_trace:
                name_trace = (
                    "Regr line"
                    + str(ConfigWidgets.feat_x)
                    + " "
                    + (ConfigWidgets.feat_y)
                )

                if self.regr_line_trace[(ConfigWidgets.feat_x, ConfigWidgets.feat_y)]:
                    self.trace[name_trace].line = dict(
                        color=ConfigWidgets.line_color,
                        width=ConfigWidgets.line_width,
                        dash=ConfigWidgets.line_dash,
                    )
                else:
                    self.trace[name_trace].line = dict(width=0)

            if self.convex_hull == True:

                if ConfigWidgets.feat_x == ConfigWidgets.feat_y:

                    for name_trace in self.name_traces:

                        self.trace["Hull " + str(name_trace)].line = dict(width=0)
                        self.FigureWidget.update_traces(
                            selector={"name": "Hull " + name_trace},
                        )
                else:
                    hullx, hully = self.make_hull(
                        ConfigWidgets.feat_x, ConfigWidgets.feat_y
                    )
                    for name_trace in self.name_traces:

                        self.trace["Hull " + str(name_trace)]["x"] = hullx[name_trace]
                        self.trace["Hull " + str(name_trace)]["y"] = hully[name_trace]
                        self.trace["Hull " + str(name_trace)].line = dict(
                            color=ConfigWidgets.hull_color,
                            width=ConfigWidgets.hull_width,
                            dash=ConfigWidgets.hull_dash,
                        )
                        self.FigureWidget.update_traces(
                            selector={"name": "Hull " + name_trace}, showlegend=False
                        )
            else:
                for name_trace in self.name_traces:

                    self.trace["Hull " + str(name_trace)].line = dict(width=0)
                    self.FigureWidget.update_traces(
                        selector={"name": "Hull " + str(name_trace)},
                    )

    def fract_change_updates(self, ConfigWidgets):
        """
        All updates caused by a change in the fraction value.
        """

        self.update_df_on_map(ConfigWidgets)
        self.update_hover_variables()

    def update_df_on_map(self, ConfigWidgets):
        """
        Updates the number of points based on the fraction value,
        then 'df_trace_on_map' which is the fraction of the dataframe that is visualized
        """

        for name_trace in self.name_traces:

            n_points = int(
                ConfigWidgets.fract
                * self.df.loc[self.df[self.target] == name_trace].shape[0]
            )

            if n_points < 1:
                n_points = 1

            if (
                ConfigWidgets.feat_x,
                ConfigWidgets.feat_y,
            ) in self.optimized_sequence_indexes:
                sequence_indexes = self.optimized_sequence_indexes[
                    (ConfigWidgets.feat_x, ConfigWidgets.feat_y)
                ][name_trace]
            else:
                sequence_indexes = self.random_permutation_indexes[name_trace]

            self.df_trace_on_map[name_trace] = (
                self.df.loc[self.df[self.target] == name_trace]
                .loc[sequence_indexes]
                .head(n_points)
            )

            # if a structure is visualized, its dataframe entry is added to the visualized dataframe 'df_trace_on_map'
            # this to avoid that the entry relative to a visualized structure is not available on the map
            if (
                ConfigWidgets.structure_text_l
                in self.df.loc[self.df[self.target] == name_trace].index
            ):
                self.df_trace_on_map[name_trace] = pd.concat(
                    [
                        self.df_trace_on_map[name_trace],
                        self.df.loc[[ConfigWidgets.structure_text_l]],
                    ]
                )

            if (
                ConfigWidgets.structure_text_r
                in self.df.loc[self.df[self.target] == name_trace].index
            ):
                self.df_trace_on_map[name_trace] = pd.concat(
                    [
                        self.df_trace_on_map[name_trace],
                        self.df.loc[[ConfigWidgets.structure_text_r]],
                    ]
                )

    def update_hover_variables(self):
        """
        Updates the hover data based on the points that are visualized on the map.
        """

        self.hover_text = {}
        self.hover_custom = {}
        self.hover_template = {}

        for name_trace in self.name_traces:

            self.hover_text[name_trace] = self.df_trace_on_map[name_trace].index
            hover_template = r"<b>%{text}</b><br><br>"
            if self.hover_features:
                hover_custom = np.dstack(
                    [
                        self.df_trace_on_map[name_trace][
                            str(self.hover_features[0])
                        ].to_numpy()
                    ]
                )
                hover_template += str(self.hover_features[0]) + ": %{customdata[0]}<br>"
                for i in range(1, len(self.hover_features), 1):
                    hover_custom = np.dstack(
                        [
                            hover_custom,
                            self.df_trace_on_map[name_trace][
                                str(self.hover_features[i])
                            ].to_numpy(),
                        ]
                    )
                    hover_template += (
                        str(self.hover_features[i])
                        + ": %{customdata["
                        + str(i)
                        + "]}<br>"
                    )
                self.hover_custom[name_trace] = hover_custom[0]
                self.hover_template[name_trace] = hover_template
            else:
                self.hover_customp[name_trace] = [""]
                self.hover_template[name_trace] = [""]

    def make_hull(self, feat_x, feat_y):

        xhull_classes = {}
        yhull_classes = {}

        for name_trace in self.name_traces:

            name_trace = str(name_trace)
            points = self.df.loc[self.df[self.target] == name_trace][
                [feat_x, feat_y]
            ].to_numpy()

            delta_0 = max(points[:, 0]) - min(points[:, 0])
            delta_1 = max(points[:, 1]) - min(points[:, 1])
            exp_1 = int(np.log10(delta_0 / delta_1))
            exp_0 = int(np.log10(delta_1 / delta_0))
            if exp_1 > 6:
                points[:, 1] = points[:, 1] * 10**exp_1
            if exp_0 > 6:
                points[:, 0] = points[:, 0] * 10**exp_0
            hull = ConvexHull(points)
            vertexes = self.df.loc[self.df[self.target] == name_trace][
                [feat_x, feat_y]
            ].to_numpy()[hull.vertices]

            x_hullvx = vertexes[:, 0]
            y_hullvx = vertexes[:, 1]
            n_intervals = 100

            xhull = np.array([x_hullvx[0]])
            yhull = np.array([y_hullvx[0]])
            for xy in zip(x_hullvx, y_hullvx):
                xhull = np.concatenate(
                    [xhull, np.linspace(xhull[-1], xy[0], n_intervals)]
                )
                yhull = np.concatenate(
                    [yhull, np.linspace(yhull[-1], xy[1], n_intervals)]
                )

            xhull_classes[name_trace] = np.concatenate(
                [xhull, np.linspace(xhull[-1], x_hullvx[0], n_intervals)]
            )
            yhull_classes[name_trace] = np.concatenate(
                [yhull, np.linspace(yhull[-1], y_hullvx[0], n_intervals)]
            )

        return xhull_classes, yhull_classes

    def make_line(self, feat_x, feat_y, regr_line_coefs):

        idx_x = self.embedding_features.index(feat_x)
        idx_y = self.embedding_features.index(feat_y)
        line_x = np.linspace(self.df[feat_x].min(), self.df[feat_x].max(), 1000)

        # Gives the classifications line
        if feat_x == feat_y:
            return line_x, line_x
        else:
            line_y = (
                -line_x * regr_line_coefs[0][idx_x] / regr_line_coefs[0][idx_y]
                - regr_line_coefs[1] / regr_line_coefs[0][idx_y]
            )
            return line_x, line_y

    def marker_style_updates(self, ConfigWidgets):
        """
        All updates caused by a change in the markers properties.
        """

        self.update_marker_color(ConfigWidgets)
        self.update_marker_symbol(ConfigWidgets)
        self.update_marker_size(ConfigWidgets)

    def update_marker_symbol(self, ConfigWidgets):
        """
        Updates the list of marker symbols for each trace.
        All markers are initally set to have the symbol specific of the trace "trace_symbol".
        Points whose structure is visualized have a cross as marker.
        """

        for name_trace in self.name_traces:

            self.symbols[name_trace] = [self.trace_symbol[name_trace]] * len(
                self.df_trace_on_map[name_trace]
            )
            formula_l = ConfigWidgets.structure_text_l
            formula_r = ConfigWidgets.structure_text_r

            for i in range(2):
                # entries whose structure is visualized appear twice on 'df_trace_on_map'
                try:
                    point = np.where(
                        self.df_trace_on_map[name_trace].index.to_numpy() == formula_l
                    )[0][i]
                    self.symbols[name_trace][point] = "x"
                except:
                    pass
                try:
                    point = np.where(
                        self.df_trace_on_map[name_trace].index.to_numpy() == formula_r
                    )[0][i]
                    self.symbols[name_trace][point] = "cross"
                except:
                    pass

            if formula_l == formula_r and formula_l:
                try:
                    point = np.where(
                        self.df_trace_on_map[name_trace].index.to_numpy() == formula_l
                    )[0][1]
                    self.symbols[name_trace][point] = "x"
                    point = np.where(
                        self.df_trace_on_map[name_trace].index.to_numpy() == formula_l
                    )[0][2]
                    self.symbols[name_trace][point] = "cross"
                except:
                    pass

    def update_marker_size(self, ConfigWidgets):
        """
        Updates the size of the markers:
        in case of 'Default size' all markers have the same size, and points marked with x/cross are set with a specific cross size;
        in case 'Marker' has a feature value, marker sizes are selected according to that specific feature.
        """

        feature = ConfigWidgets.featmarker

        if feature == "Default size":

            for name_trace in self.name_traces:

                sizes = [ConfigWidgets.marker_size] * len(
                    self.df_trace_on_map[name_trace]
                )
                symbols = self.symbols[name_trace]

                indices_x = [i for i, symbol in enumerate(symbols) if symbol == "x"]
                indices_cross = [
                    i for i, symbol in enumerate(symbols) if symbol == "cross"
                ]

                if indices_x:
                    sizes[indices_x[0]] = ConfigWidgets.cross_size

                if len(indices_x) == 2:
                    # entries whose structure is visualized appear twice on 'df_trace_on_map'

                    sizes[indices_x[0]] = 0
                    sizes[indices_x[1]] = ConfigWidgets.cross_size

                if indices_cross:
                    sizes[indices_cross[0]] = ConfigWidgets.cross_size

                if len(indices_cross) == 2:
                    sizes[indices_cross[0]] = 0
                    sizes[indices_cross[1]] = ConfigWidgets.cross_size

                self.sizes[name_trace] = sizes
        else:

            min_value = ConfigWidgets.min_value_markerfeat
            max_value = ConfigWidgets.max_value_markerfeat
            min_feat = min(
                [
                    min(self.df_trace_on_map[name_trace][feature].to_numpy())
                    for name_trace in self.df_trace_on_map
                ]
            )
            max_feat = max(
                [
                    max(self.df_trace_on_map[name_trace][feature].to_numpy())
                    for name_trace in self.df_trace_on_map
                ]
            )

            coeff = (max_value - min_value) / (max_feat - min_feat)

            for name_trace in self.name_traces:

                sizes = min_value + coeff * (
                    self.df_trace_on_map[name_trace][feature].to_numpy() - min_feat
                )
                self.sizes[name_trace] = sizes

    def update_marker_color(self, ConfigWidgets):
        """
        Updates the color of markers:
        in case of "Default color" each trace has a different color;
        in case of a feature, each different feature value has a different color.
        """

        feature = ConfigWidgets.featcolor

        if feature == "Default color":

            palette = cycle(getattr(px.colors.qualitative, ConfigWidgets.color_palette))
            for name_trace in self.name_traces:
                self.colors[name_trace] = [next(palette)] * len(
                    self.df_trace_on_map[name_trace]
                )

        elif ConfigWidgets.featcolor_type == "Discrete":
            # each color represents a different discrete feature value

            colors_dict = {}
            palette = cycle(
                getattr(px.colors.qualitative, ConfigWidgets.featcolor_list)
            )
            for value in np.sort(self.df[feature].unique()):
                colors_dict[value] = next(palette)

            for name_trace in self.name_traces:

                self.colors[name_trace] = [" "] * len(self.df_trace_on_map[name_trace])
                for i, value in enumerate(self.df_trace_on_map[name_trace][feature]):

                    self.colors[name_trace][i] = colors_dict[value]

        elif ConfigWidgets.featcolor_type == "Gradient":
            # colors are interpolated in a gradient, according to the feature value

            feature = ConfigWidgets.featcolor

            for name_trace in self.name_traces:
                self.colors[name_trace] = self.df_trace_on_map[name_trace][feature]

    def optimize_sequence(self, feat_x, feat_y):

        n_neighbors = 10
        fraction_thres = 1
        optimized_sequence_indexes = {}

        for name_trace in self.name_traces:

            name_trace = str(name_trace)
            feat_x_norm = MinMaxScaler().fit_transform(
                self.df.loc[self.df[self.target] == name_trace][feat_x].values.reshape(
                    -1, 1
                )
            )
            feat_y_norm = MinMaxScaler().fit_transform(
                self.df.loc[self.df[self.target] == name_trace][feat_y].values.reshape(
                    -1, 1
                )
            )

            X = np.concatenate((feat_x_norm, feat_y_norm), axis=1)

            nbrs = NearestNeighbors(n_neighbors=n_neighbors, algorithm="ball_tree").fit(
                X
            )
            nbrs_distances, nbrs_indices = nbrs.kneighbors(X)

            n_values = len(self.df.loc[self.df[self.target] == name_trace])

            remaining_indices = np.arange(n_values)

            new_index = np.array(
                [remaining_indices[np.argmin(np.sum(nbrs_distances, axis=1))]]
            )
            selected_indices = np.array([new_index]).reshape(1)
            remaining_indices = np.delete(remaining_indices, selected_indices)
            mask = np.where(nbrs_indices == new_index, 1, 0)

            last_cost = 0
            bool_cost = True

            while len(remaining_indices) > 0:

                # prob = np.sum(np.exp(distances[remaininig_indexes,:][:,selected_indexes]), axis=1)
                # prob /= np.sum(prob)
                # new_index = np.array([np.random.choice(remaininig_indexes, p=prob)])

                # the numerator gives the sum of the distances over the indexes that do not appear on the map
                # the closer the points that do not appear on the map are, the more likely the point is selected
                num = np.sum((mask * nbrs_distances)[remaining_indices], axis=1)
                # the denominater gives the sum of the distances over the indexes that appear on the map
                # the closer the points that appear on the map are, the less likely the point is selected
                den = np.sum(((1 - mask) * nbrs_distances)[remaining_indices], axis=1)
                if np.min(num) == 0:
                    arg = np.argmax(den[np.where(num == 0)])
                    new_index = np.array([remaining_indices[np.where(num == 0)][arg]])
                    new_cost = 0
                elif np.min(den) == 0:
                    arg = np.argmin(num[np.where(den == 0)])
                    new_index = np.array([remaining_indices[np.where(den == 0)][arg]])
                    new_cost = float("inf")
                else:
                    arg = np.argmin(num / den)
                    new_index = np.array([remaining_indices[arg]])
                    new_cost = (num / den)[arg]

                if bool_cost:
                    if new_cost > 1 and last_cost < 1:
                        fraction_thres = len(selected_indices) / n_values
                        # fractions_thres [(feat_x, feat_y)] = len(selected_indexes)/len(distances)
                        # fractions_thres [(feat_y, feat_x)] = len(selected_indexes)/len(distances)
                        bool_cost = False
                    last_cost = new_cost

                remaining_indices = np.delete(
                    remaining_indices, np.where(remaining_indices == new_index)
                )
                selected_indices = np.concatenate((selected_indices, new_index))
                mask = mask + np.where(nbrs_indices == new_index, 1, 0)

                if len(remaining_indices > 0) and len(remaining_indices) % 10 == 0:
                    arg = np.argmax(
                        np.sum((mask * nbrs_distances)[remaining_indices], axis=1)
                    )
                    new_index = np.array([remaining_indices[arg]])
                    remaining_indices = np.delete(
                        remaining_indices, np.where(remaining_indices == new_index)
                    )
                    selected_indices = np.concatenate((selected_indices, new_index))
                    mask = mask + np.where(nbrs_indices == new_index, 1, 0)

            optimized_sequence_indexes[name_trace] = self.df[
                self.df[self.target] == name_trace
            ].index.to_numpy()[selected_indices]

        return optimized_sequence_indexes, fraction_thres

batch_update(ConfigWidgets)

Updates the layout of the map according to values stored in "ConfigWidgets".

Source code in nomad_visu/figure/__init__.py
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
def batch_update(self, ConfigWidgets):
    """
    Updates the layout of the map according to values stored in "ConfigWidgets".
    """

    self.marker_style_updates(ConfigWidgets)
    self.fract_change_updates(ConfigWidgets)

    x_min = []
    x_max = []
    y_min = []
    y_max = []

    for name_trace in self.name_traces:

        x_min.append(min(self.df_trace_on_map[name_trace][ConfigWidgets.feat_x]))
        x_max.append(max(self.df_trace_on_map[name_trace][ConfigWidgets.feat_x]))
        y_min.append(min(self.df_trace_on_map[name_trace][ConfigWidgets.feat_y]))
        y_max.append(max(self.df_trace_on_map[name_trace][ConfigWidgets.feat_y]))

    x_min = min(x_min)
    y_min = min(y_min)
    x_max = max(x_max)
    y_max = max(y_max)
    x_delta = 0.05 * abs(x_max - x_min)
    y_delta = 0.05 * abs(y_max - y_min)

    # range of the x-,y- values that are visualized on the map
    xaxis_range = [x_min - x_delta, x_max + x_delta]
    yaxis_range = [y_min - y_delta, y_max + y_delta]

    if ConfigWidgets.bg_toggle:
        bg_color = ConfigWidgets.bg_color
        gridcolor = "white"
    else:
        bg_color = "white"
        gridcolor = "rgb(229,236,246)"

    with self.FigureWidget.batch_update():

        self.FigureWidget.update_layout(
            showlegend=True,
            plot_bgcolor=bg_color,
            xaxis=dict(gridcolor=gridcolor, showgrid=True, zeroline=False),
            yaxis=dict(gridcolor=gridcolor, showgrid=True, zeroline=False),
            font=dict(
                size=int(ConfigWidgets.font_size),
                family=ConfigWidgets.font_family,
                color=ConfigWidgets.font_color,
            ),
            xaxis_title=ConfigWidgets.feat_x,
            yaxis_title=ConfigWidgets.feat_y,
            xaxis_range=xaxis_range,
            yaxis_range=yaxis_range,
        )

        for name_trace in self.name_traces:
            # all elements on the map and their properties are reinitialized at each change

            self.FigureWidget.update_traces(
                selector={"name": str(name_trace)},
                text=self.hover_text[name_trace],
                customdata=self.hover_custom[name_trace],
                hovertemplate=self.hover_template[name_trace],
                x=self.df_trace_on_map[name_trace][ConfigWidgets.feat_x],
                y=self.df_trace_on_map[name_trace][ConfigWidgets.feat_y],
                marker=dict(
                    size=self.sizes[name_trace], symbol=self.symbols[name_trace]
                ),
            )
            if (
                ConfigWidgets.featcolor != "Default color"
                and ConfigWidgets.featcolor_type == "Gradient"
            ):
                feature = ConfigWidgets.featcolor
                gradient = ConfigWidgets.featcolor_list
                min_value = self.df[feature].min()
                max_value = self.df[feature].max()

                self.FigureWidget.update_traces(
                    selector={"name": str(name_trace)},
                    marker=dict(
                        colorscale=gradient,
                        showscale=True,
                        color=self.colors[name_trace],
                        cmin=min_value,
                        cmax=max_value,
                        colorbar=dict(
                            thickness=10,
                            orientation="v",
                            len=0.5,
                            y=0.25,
                            title=dict(
                                text=feature, side="right", font={"size": 10}
                            ),
                        ),
                    ),
                )
            else:
                self.FigureWidget.update_traces(
                    selector={"name": str(name_trace)},
                    marker=dict(showscale=False, color=self.colors[name_trace]),
                )
        if (ConfigWidgets.feat_x, ConfigWidgets.feat_y) in self.regr_line_trace:
            name_trace = (
                "Regr line"
                + str(ConfigWidgets.feat_x)
                + " "
                + (ConfigWidgets.feat_y)
            )

            if self.regr_line_trace[(ConfigWidgets.feat_x, ConfigWidgets.feat_y)]:
                self.trace[name_trace].line = dict(
                    color=ConfigWidgets.line_color,
                    width=ConfigWidgets.line_width,
                    dash=ConfigWidgets.line_dash,
                )
            else:
                self.trace[name_trace].line = dict(width=0)

        if self.convex_hull == True:

            if ConfigWidgets.feat_x == ConfigWidgets.feat_y:

                for name_trace in self.name_traces:

                    self.trace["Hull " + str(name_trace)].line = dict(width=0)
                    self.FigureWidget.update_traces(
                        selector={"name": "Hull " + name_trace},
                    )
            else:
                hullx, hully = self.make_hull(
                    ConfigWidgets.feat_x, ConfigWidgets.feat_y
                )
                for name_trace in self.name_traces:

                    self.trace["Hull " + str(name_trace)]["x"] = hullx[name_trace]
                    self.trace["Hull " + str(name_trace)]["y"] = hully[name_trace]
                    self.trace["Hull " + str(name_trace)].line = dict(
                        color=ConfigWidgets.hull_color,
                        width=ConfigWidgets.hull_width,
                        dash=ConfigWidgets.hull_dash,
                    )
                    self.FigureWidget.update_traces(
                        selector={"name": "Hull " + name_trace}, showlegend=False
                    )
        else:
            for name_trace in self.name_traces:

                self.trace["Hull " + str(name_trace)].line = dict(width=0)
                self.FigureWidget.update_traces(
                    selector={"name": "Hull " + str(name_trace)},
                )

fract_change_updates(ConfigWidgets)

All updates caused by a change in the fraction value.

Source code in nomad_visu/figure/__init__.py
369
370
371
372
373
374
375
def fract_change_updates(self, ConfigWidgets):
    """
    All updates caused by a change in the fraction value.
    """

    self.update_df_on_map(ConfigWidgets)
    self.update_hover_variables()

marker_style_updates(ConfigWidgets)

All updates caused by a change in the markers properties.

Source code in nomad_visu/figure/__init__.py
540
541
542
543
544
545
546
547
def marker_style_updates(self, ConfigWidgets):
    """
    All updates caused by a change in the markers properties.
    """

    self.update_marker_color(ConfigWidgets)
    self.update_marker_symbol(ConfigWidgets)
    self.update_marker_size(ConfigWidgets)

update_df_on_map(ConfigWidgets)

Updates the number of points based on the fraction value, then 'df_trace_on_map' which is the fraction of the dataframe that is visualized

Source code in nomad_visu/figure/__init__.py
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
def update_df_on_map(self, ConfigWidgets):
    """
    Updates the number of points based on the fraction value,
    then 'df_trace_on_map' which is the fraction of the dataframe that is visualized
    """

    for name_trace in self.name_traces:

        n_points = int(
            ConfigWidgets.fract
            * self.df.loc[self.df[self.target] == name_trace].shape[0]
        )

        if n_points < 1:
            n_points = 1

        if (
            ConfigWidgets.feat_x,
            ConfigWidgets.feat_y,
        ) in self.optimized_sequence_indexes:
            sequence_indexes = self.optimized_sequence_indexes[
                (ConfigWidgets.feat_x, ConfigWidgets.feat_y)
            ][name_trace]
        else:
            sequence_indexes = self.random_permutation_indexes[name_trace]

        self.df_trace_on_map[name_trace] = (
            self.df.loc[self.df[self.target] == name_trace]
            .loc[sequence_indexes]
            .head(n_points)
        )

        # if a structure is visualized, its dataframe entry is added to the visualized dataframe 'df_trace_on_map'
        # this to avoid that the entry relative to a visualized structure is not available on the map
        if (
            ConfigWidgets.structure_text_l
            in self.df.loc[self.df[self.target] == name_trace].index
        ):
            self.df_trace_on_map[name_trace] = pd.concat(
                [
                    self.df_trace_on_map[name_trace],
                    self.df.loc[[ConfigWidgets.structure_text_l]],
                ]
            )

        if (
            ConfigWidgets.structure_text_r
            in self.df.loc[self.df[self.target] == name_trace].index
        ):
            self.df_trace_on_map[name_trace] = pd.concat(
                [
                    self.df_trace_on_map[name_trace],
                    self.df.loc[[ConfigWidgets.structure_text_r]],
                ]
            )

update_hover_variables()

Updates the hover data based on the points that are visualized on the map.

Source code in nomad_visu/figure/__init__.py
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
def update_hover_variables(self):
    """
    Updates the hover data based on the points that are visualized on the map.
    """

    self.hover_text = {}
    self.hover_custom = {}
    self.hover_template = {}

    for name_trace in self.name_traces:

        self.hover_text[name_trace] = self.df_trace_on_map[name_trace].index
        hover_template = r"<b>%{text}</b><br><br>"
        if self.hover_features:
            hover_custom = np.dstack(
                [
                    self.df_trace_on_map[name_trace][
                        str(self.hover_features[0])
                    ].to_numpy()
                ]
            )
            hover_template += str(self.hover_features[0]) + ": %{customdata[0]}<br>"
            for i in range(1, len(self.hover_features), 1):
                hover_custom = np.dstack(
                    [
                        hover_custom,
                        self.df_trace_on_map[name_trace][
                            str(self.hover_features[i])
                        ].to_numpy(),
                    ]
                )
                hover_template += (
                    str(self.hover_features[i])
                    + ": %{customdata["
                    + str(i)
                    + "]}<br>"
                )
            self.hover_custom[name_trace] = hover_custom[0]
            self.hover_template[name_trace] = hover_template
        else:
            self.hover_customp[name_trace] = [""]
            self.hover_template[name_trace] = [""]

update_marker_color(ConfigWidgets)

Updates the color of markers: in case of "Default color" each trace has a different color; in case of a feature, each different feature value has a different color.

Source code in nomad_visu/figure/__init__.py
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
def update_marker_color(self, ConfigWidgets):
    """
    Updates the color of markers:
    in case of "Default color" each trace has a different color;
    in case of a feature, each different feature value has a different color.
    """

    feature = ConfigWidgets.featcolor

    if feature == "Default color":

        palette = cycle(getattr(px.colors.qualitative, ConfigWidgets.color_palette))
        for name_trace in self.name_traces:
            self.colors[name_trace] = [next(palette)] * len(
                self.df_trace_on_map[name_trace]
            )

    elif ConfigWidgets.featcolor_type == "Discrete":
        # each color represents a different discrete feature value

        colors_dict = {}
        palette = cycle(
            getattr(px.colors.qualitative, ConfigWidgets.featcolor_list)
        )
        for value in np.sort(self.df[feature].unique()):
            colors_dict[value] = next(palette)

        for name_trace in self.name_traces:

            self.colors[name_trace] = [" "] * len(self.df_trace_on_map[name_trace])
            for i, value in enumerate(self.df_trace_on_map[name_trace][feature]):

                self.colors[name_trace][i] = colors_dict[value]

    elif ConfigWidgets.featcolor_type == "Gradient":
        # colors are interpolated in a gradient, according to the feature value

        feature = ConfigWidgets.featcolor

        for name_trace in self.name_traces:
            self.colors[name_trace] = self.df_trace_on_map[name_trace][feature]

update_marker_size(ConfigWidgets)

Updates the size of the markers: in case of 'Default size' all markers have the same size, and points marked with x/cross are set with a specific cross size; in case 'Marker' has a feature value, marker sizes are selected according to that specific feature.

Source code in nomad_visu/figure/__init__.py
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
def update_marker_size(self, ConfigWidgets):
    """
    Updates the size of the markers:
    in case of 'Default size' all markers have the same size, and points marked with x/cross are set with a specific cross size;
    in case 'Marker' has a feature value, marker sizes are selected according to that specific feature.
    """

    feature = ConfigWidgets.featmarker

    if feature == "Default size":

        for name_trace in self.name_traces:

            sizes = [ConfigWidgets.marker_size] * len(
                self.df_trace_on_map[name_trace]
            )
            symbols = self.symbols[name_trace]

            indices_x = [i for i, symbol in enumerate(symbols) if symbol == "x"]
            indices_cross = [
                i for i, symbol in enumerate(symbols) if symbol == "cross"
            ]

            if indices_x:
                sizes[indices_x[0]] = ConfigWidgets.cross_size

            if len(indices_x) == 2:
                # entries whose structure is visualized appear twice on 'df_trace_on_map'

                sizes[indices_x[0]] = 0
                sizes[indices_x[1]] = ConfigWidgets.cross_size

            if indices_cross:
                sizes[indices_cross[0]] = ConfigWidgets.cross_size

            if len(indices_cross) == 2:
                sizes[indices_cross[0]] = 0
                sizes[indices_cross[1]] = ConfigWidgets.cross_size

            self.sizes[name_trace] = sizes
    else:

        min_value = ConfigWidgets.min_value_markerfeat
        max_value = ConfigWidgets.max_value_markerfeat
        min_feat = min(
            [
                min(self.df_trace_on_map[name_trace][feature].to_numpy())
                for name_trace in self.df_trace_on_map
            ]
        )
        max_feat = max(
            [
                max(self.df_trace_on_map[name_trace][feature].to_numpy())
                for name_trace in self.df_trace_on_map
            ]
        )

        coeff = (max_value - min_value) / (max_feat - min_feat)

        for name_trace in self.name_traces:

            sizes = min_value + coeff * (
                self.df_trace_on_map[name_trace][feature].to_numpy() - min_feat
            )
            self.sizes[name_trace] = sizes

update_marker_symbol(ConfigWidgets)

Updates the list of marker symbols for each trace. All markers are initally set to have the symbol specific of the trace "trace_symbol". Points whose structure is visualized have a cross as marker.

Source code in nomad_visu/figure/__init__.py
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
def update_marker_symbol(self, ConfigWidgets):
    """
    Updates the list of marker symbols for each trace.
    All markers are initally set to have the symbol specific of the trace "trace_symbol".
    Points whose structure is visualized have a cross as marker.
    """

    for name_trace in self.name_traces:

        self.symbols[name_trace] = [self.trace_symbol[name_trace]] * len(
            self.df_trace_on_map[name_trace]
        )
        formula_l = ConfigWidgets.structure_text_l
        formula_r = ConfigWidgets.structure_text_r

        for i in range(2):
            # entries whose structure is visualized appear twice on 'df_trace_on_map'
            try:
                point = np.where(
                    self.df_trace_on_map[name_trace].index.to_numpy() == formula_l
                )[0][i]
                self.symbols[name_trace][point] = "x"
            except:
                pass
            try:
                point = np.where(
                    self.df_trace_on_map[name_trace].index.to_numpy() == formula_r
                )[0][i]
                self.symbols[name_trace][point] = "cross"
            except:
                pass

        if formula_l == formula_r and formula_l:
            try:
                point = np.where(
                    self.df_trace_on_map[name_trace].index.to_numpy() == formula_l
                )[0][1]
                self.symbols[name_trace][point] = "x"
                point = np.where(
                    self.df_trace_on_map[name_trace].index.to_numpy() == formula_l
                )[0][2]
                self.symbols[name_trace][point] = "cross"
            except:
                pass