Skip to content

plotstate

PlotState class, used for plot settings.

palettes = {k.replace('256', '').lower(): kfor k in colormaps if '256' in k} module-attribute #

dict: all valid colorscales/palettes for plotting with. Created from colorcet's named palettes and Bokeh's continuous 256 color palettes

PlotState #

Combination of reactive states which instantiate a specific plot's settings/properties.

Initializes based on keyword arguments, which are passed via plot creation methods in the ViewCard/GridLayout.

Attributes:

Name Type Description
plottype str

the plottype; non-reactive and unchanging

subset str

subset key

columns list(str

a list of columns selected. Used in Table views.

x str

x column

y str

y column

color str

color data column

colorscale str

colormap

nbins int

number of bins for aggregations

bintype str

type of aggregation to perform

logcolor bool

whether the color data is log-scaled

logx bool

whether the x data is log-scaled

logy bool

whether the y data is log-scaled

flipx bool

whether the x dimension is flipped

flipy bool

whether the y dimension is flipped

xmapping dict

categorical datamapping for x data

ymapping dict

categorical datamapping for y data

colormapping dict

categorical datamapping for color data

Lookup dict

data for quick lookup

Source code in src/sdss_explorer/dashboard/dataclass/plotstate.py
 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
class PlotState:
    """
    Combination of reactive states which instantiate a specific plot's settings/properties.

    Initializes based on keyword arguments, which are passed via plot creation methods in the ViewCard/GridLayout.

    Attributes:
        plottype (str): the plottype; non-reactive and unchanging
        subset (str): subset key
        columns (list(str)): a list of columns selected. Used in Table views.

        x (str): x column
        y (str): y column

        color (str): color data column
        colorscale (str): colormap
        nbins (int): number of bins for aggregations
        bintype (str): type of aggregation to perform
        logcolor (bool): whether the color data is log-scaled

        logx (bool): whether the x data is log-scaled
        logy (bool): whether the y data is log-scaled
        flipx (bool): whether the x dimension is flipped
        flipy (bool): whether the y dimension is flipped

        xmapping (dict): categorical datamapping for x data
        ymapping (dict): categorical datamapping for y data
        colormapping (dict): categorical datamapping for color data

        Lookup (dict): data for quick lookup
    """

    def __init__(self, plottype, current_key, **kwargs):
        # subset and type states
        self.plottype = str(plottype)
        self.subset = sl.use_reactive(current_key)

        # want target data or stats?
        init_cols = (["sdss_id", "gaia_dr3_source_id"] if
                     (plottype == "targets") else ["g_mag", "snr"])
        self.columns = sl.use_reactive(init_cols)

        # setup init columns, also used for resets;
        # these are STABLE columns, so they should have minimal invalids; even if the dataset is small
        initx = "ra"
        inity = "dec"
        initc = "plx"

        self.x = sl.use_reactive(kwargs.get("x", initx))
        self.y = sl.use_reactive(kwargs.get("y", inity))
        self.color = sl.use_reactive(kwargs.get("color", initc))

        # categorical data mappings
        self.xmapping = dict()  # non-reactive
        self.ymapping = dict()  # non-reactive

        # color props
        self.colormapping = dict()  # non-reactive
        self.colorscale = sl.use_reactive(kwargs.get("colorscale", "inferno"))
        self.logcolor = sl.use_reactive(kwargs.get("logcolor", False))

        # binning props
        init_nbins = 100 if (plottype == "heatmap") else 20
        self.nbins = sl.use_reactive(kwargs.get("nbins", init_nbins))
        self.bintype = sl.use_reactive(kwargs.get("bintype", "mean"))

        # flips and logs
        self.flipx = sl.use_reactive(bool(kwargs.get("flipx", "")))
        self.flipy = sl.use_reactive(bool(kwargs.get("flipy", "")))
        self.logx = sl.use_reactive(bool(kwargs.get("logx", "")))
        self.logy = sl.use_reactive(bool(kwargs.get("logy", "")))

        # all lookup data for plottypes
        # TODO: move this lookup data elsewhere to reduce the size of the plotstate objects
        self.Lookup = dict(
            bintypes=[
                "count",
                "mean",
                "median",
                "sum",
                "min",
                "max",
            ],
            colorscales=palettes,
        )

    def swap_axes(self):
        """Swaps current x and y axes."""
        # saves current to p and q
        p = self.x.value
        q = self.y.value
        self.x.set(q)
        self.y.set(p)

    def reset_values(self):
        """Conditional reset based on if given column/subset is still in list. Triggered by threads in plot_settings."""
        # subset resets
        if self.subset.value not in SubsetState.subsets.value.keys():
            new_subset_key = list(SubsetState.subsets.value.keys())[-1]
            Alert.update(
                f"Subset in view was removed, reset to {SubsetState.subsets.value[new_subset_key].name}",
                color="info",
            )
            self.subset.value = new_subset_key
        try:
            if self.subset_b.value not in SubsetState.subsets.value.keys():
                new_subset_key = list(SubsetState.subsets.value.keys())[-2]
                self.subset_b.value = new_subset_key
        except:
            pass

        valid_columns = SubsetState.subsets.value.get(
            self.subset.value).columns + list(VCData.columns.value.keys())

        # columnar resets for table
        if (self.plottype == "stats") or (self.plottype == "targets"):
            for col in self.columns.value:
                removed_cols = set()
                if col not in valid_columns:
                    removed_cols.add(col)
                # NOTE: i choose to remove quietly on stats table -- its very obvious when it disappears
                # NOTE: you have to do it at the end, because a task can only send 1 state update for rerenders
                self.columns.set(
                    [q for q in self.columns.value if q not in removed_cols])
                return

        # columnar resets for plots
        else:
            if self.x.value not in valid_columns:
                Alert.update("Columns of subset changed! Column reset to 'ra'",
                             color="info")
                self.x.value = "dec"
            if self.plottype != "histogram":
                if self.y.value not in valid_columns:
                    Alert.update(
                        "Columns of subset changed! Column reset to 'dec'",
                        color="info")
                    self.y.value = "dec"
                if self.color.value not in valid_columns:
                    Alert.update(
                        "Columns of subset changed! Column reset to 'ra'",
                        color="info",
                    )
                    self.color.value = "ra"

    def update_subset(self, name: str, b: bool = False):
        """Callback to update subset by name."""
        if not b:
            subset = self.subset
        else:
            subset = self.subset_b
        for k, ss in SubsetState.subsets.value.items():
            if ss.name == name:
                subset.set(k)
                break
        return

reset_values() #

Conditional reset based on if given column/subset is still in list. Triggered by threads in plot_settings.

Source code in src/sdss_explorer/dashboard/dataclass/plotstate.py
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
def reset_values(self):
    """Conditional reset based on if given column/subset is still in list. Triggered by threads in plot_settings."""
    # subset resets
    if self.subset.value not in SubsetState.subsets.value.keys():
        new_subset_key = list(SubsetState.subsets.value.keys())[-1]
        Alert.update(
            f"Subset in view was removed, reset to {SubsetState.subsets.value[new_subset_key].name}",
            color="info",
        )
        self.subset.value = new_subset_key
    try:
        if self.subset_b.value not in SubsetState.subsets.value.keys():
            new_subset_key = list(SubsetState.subsets.value.keys())[-2]
            self.subset_b.value = new_subset_key
    except:
        pass

    valid_columns = SubsetState.subsets.value.get(
        self.subset.value).columns + list(VCData.columns.value.keys())

    # columnar resets for table
    if (self.plottype == "stats") or (self.plottype == "targets"):
        for col in self.columns.value:
            removed_cols = set()
            if col not in valid_columns:
                removed_cols.add(col)
            # NOTE: i choose to remove quietly on stats table -- its very obvious when it disappears
            # NOTE: you have to do it at the end, because a task can only send 1 state update for rerenders
            self.columns.set(
                [q for q in self.columns.value if q not in removed_cols])
            return

    # columnar resets for plots
    else:
        if self.x.value not in valid_columns:
            Alert.update("Columns of subset changed! Column reset to 'ra'",
                         color="info")
            self.x.value = "dec"
        if self.plottype != "histogram":
            if self.y.value not in valid_columns:
                Alert.update(
                    "Columns of subset changed! Column reset to 'dec'",
                    color="info")
                self.y.value = "dec"
            if self.color.value not in valid_columns:
                Alert.update(
                    "Columns of subset changed! Column reset to 'ra'",
                    color="info",
                )
                self.color.value = "ra"

swap_axes() #

Swaps current x and y axes.

Source code in src/sdss_explorer/dashboard/dataclass/plotstate.py
103
104
105
106
107
108
109
def swap_axes(self):
    """Swaps current x and y axes."""
    # saves current to p and q
    p = self.x.value
    q = self.y.value
    self.x.set(q)
    self.y.set(p)

update_subset(name, b=False) #

Callback to update subset by name.

Source code in src/sdss_explorer/dashboard/dataclass/plotstate.py
162
163
164
165
166
167
168
169
170
171
172
def update_subset(self, name: str, b: bool = False):
    """Callback to update subset by name."""
    if not b:
        subset = self.subset
    else:
        subset = self.subset_b
    for k, ss in SubsetState.subsets.value.items():
        if ss.name == name:
            subset.set(k)
            break
    return