Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable categorical subsets #2288

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion glue/app/qt/layer_tree_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from glue.icons.qt import get_icon
from glue.dialogs.component_arithmetic.qt import ArithmeticEditorWidget
from glue.dialogs.component_manager.qt import ComponentManagerWidget
from glue.dialogs.subset_facet.qt import SubsetFacetDialog
from glue.dialogs.subset_facet.qt import SubsetFacetDialog, SubsetCategoricalsDialog
from glue.dialogs.data_wizard.qt import data_wizard
from glue.utils import nonpartial
from glue.utils.decorators import avoid_circular
Expand Down Expand Up @@ -144,6 +144,25 @@ def _do_action(self):
parent=self._layer_tree, default=default)


class SubsetCategoriesAction(LayerAction):

"""Add a sequence of subsets from all the categories in a ComponentID"""
_title = "Create subsets from categories"
_tooltip = "Create subsets from categories"

def _can_trigger(self):
return len(self._layer_tree.data_collection) > 0

def _do_action(self):
layers = self.selected_layers()
try:
default = layers[0].data
except (AttributeError, TypeError, IndexError):
default = None
SubsetCategoricalsDialog.facet(self._layer_tree.data_collection,
parent=self._layer_tree, default=default)


class MetadataAction(LayerAction):

_title = "View metadata/header"
Expand Down Expand Up @@ -599,6 +618,7 @@ def _create_actions(self):
self._actions['clear'] = ClearAction(self)
self._actions['delete'] = DeleteAction(self)
self._actions['facet'] = FacetAction(self)
self._actions['subsetcategories'] = SubsetCategoriesAction(self)
self._actions['metadata'] = MetadataAction(self)
self._actions['merge'] = MergeAction(self)
self._actions['maskify'] = MaskifySubsetAction(self)
Expand Down
2 changes: 2 additions & 0 deletions glue/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,8 @@ def default_members(self):
members.append(['Red-Yellow-Blue', cm.RdYlBu])
members.append(['Purple-Orange', cm.PuOr])
members.append(['Purple-Green', cm.PRGn])
members.append(['Tab20', cm.tab20])

return members

def add(self, label, cmap):
Expand Down
35 changes: 34 additions & 1 deletion glue/core/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import matplotlib.ticker as mticker

__all__ = ["ThetaRadianFormatter", "relim", "split_component_view", "join_component_view",
"facet_subsets", "colorize_subsets", "disambiguate",
"subset_categorical", "facet_subsets", "colorize_subsets", "disambiguate",
'small_view', 'small_view_array', 'visible_limits',
'tick_linker', 'update_ticks']

Expand Down Expand Up @@ -191,6 +191,39 @@ def join_component_view(component, view):
return tuple(result)


def subset_categorical(data_collection, data, cid, prefix=''):
"""
Create a series of subsets based on the categories present in
a particular attribute.
"""
TOO_MANY_CATEGORIES = 20

labels = []
states = []
component = data.get_kind(cid)
if component == 'categorical':
category_names = data[cid].categories
elif component == 'numerical':
category_names = np.unique(data[cid])

print(component)
if len(category_names) > TOO_MANY_CATEGORIES:
print("There are {0} categories in this component. Continuing will create a lot of subsets."
"Are you sure you want to continue?".format(len(category_names)))
for catname in category_names:
subsetstate = data.id[cid] == catname
states.append(subsetstate)
label = prefix + '{0}={1}'.format(cid, catname)
labels.append(label)

result = []
for lbl, s in zip(labels, states):
sg = data_collection.new_subset_group(label=lbl, subset_state=s)
result.append(sg)

return result


def facet_subsets(data_collection, cid, lo=None, hi=None, steps=5,
prefix='', log=False):
"""
Expand Down
132 changes: 132 additions & 0 deletions glue/dialogs/subset_facet/qt/subset_categorical.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SubsetFacet</class>
<widget class="QDialog" name="SubsetFacet">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>359</width>
<height>411</height>
</rect>
</property>
<property name="windowTitle">
<string>Subset Facet</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Datasets</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combosel_data">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Attributes</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listsel_att">
<property name="toolTip">
<string>The attributes associated with this data set</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Color Scale</string>
</property>
</widget>
</item>
<item>
<widget class="QColormapCombo" name="combodata_cmap">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="button_cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="button_ok">
<property name="text">
<string>OK</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QColormapCombo</class>
<extends>QComboBox</extends>
<header>glue.utils.qt.colors</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
78 changes: 75 additions & 3 deletions glue/dialogs/subset_facet/qt/subset_facet.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
from matplotlib import cm

from qtpy import QtWidgets
from glue.core.util import colorize_subsets, facet_subsets
from glue.core.util import colorize_subsets, facet_subsets, subset_categorical
from glue.core.state_objects import State
from echo import CallbackProperty, SelectionCallbackProperty
from glue.utils.qt import load_ui
from glue.core.data_combo_helper import DataCollectionComboHelper, ComponentIDComboHelper
from echo.qt import autoconnect_callbacks_to_qt
from glue.core.state_objects import StateAttributeLimitsHelper

__all__ = ['SubsetFacetDialog']
__all__ = ['SubsetFacetDialog', 'SubsetCategoricalsDialog']


class SubsetFacetState(State):
Expand Down Expand Up @@ -90,7 +90,79 @@ def _apply(self):
@classmethod
def facet(cls, collect, default=None, parent=None):
"""
Class method to create facted subsets.
Class method to create faceted subsets.

The arguments are the same as __init__.
"""
self = cls(collect, parent=parent, default=default)
value = self.exec_()

if value == QtWidgets.QDialog.Accepted:
self._apply()


class SubsetCategoricalsState(State):

data = SelectionCallbackProperty()
att = SelectionCallbackProperty()
cmap = CallbackProperty()

def __init__(self, data_collection):

super(SubsetCategoricalsState, self).__init__()

self.data_helper = DataCollectionComboHelper(self, 'data', data_collection)
# Some categoricals may be encoded as integers, so we allow that as an option
self.att_helper = ComponentIDComboHelper(self, 'att', numeric=True, categorical=True)

self.add_callback('data', self._on_data_change)
self._on_data_change()

def _on_data_change(self, *args, **kwargs):
self.att_helper.set_multiple_data([] if self.data is None else [self.data])


class SubsetCategoricalsDialog(QtWidgets.QDialog):
"""
Create a new dialog to create subsets on a categorical component

Parameters
----------
collect : :class:`~glue.core.data_collection.DataCollection`
The data collection to use
default : :class:`~glue.core.data.Data`, optional
The default dataset in the collection (optional)
"""

def __init__(self, collect, default=None, parent=None):

super(SubsetCategoricalsDialog, self).__init__(parent=parent)

self.state = SubsetCategoricalsState(collect)

self.ui = load_ui('subset_categorical.ui', self,
directory=os.path.dirname(__file__))
self._connections = autoconnect_callbacks_to_qt(self.state, self.ui)

self._collect = collect

if default is not None:
self.state.data = default

self.state.cmap = cm.tab20

self.ui.button_ok.clicked.connect(self.accept)
self.ui.button_cancel.clicked.connect(self.reject)

def _apply(self):

subsets = subset_categorical(self._collect, self.state.data, self.state.att)
# colorize_subsets(subsets, self.state.cmap)

@classmethod
def facet(cls, collect, default=None, parent=None):
"""
Class method to create faceted subsets.

The arguments are the same as __init__.
"""
Expand Down