Esempio n. 1
0
class OWQuickSelect(widget.OWWidget):
    name = 'Quick Select'
    icon = 'icons/QuickSelect.svg'
    description = 'Select instances with specific feature value.'

    class Inputs:
        data = widget.Input("Data", Table)

    class Outputs:
        matching = widget.Output("Matching Data", Table, default=True)
        unmatched = widget.Output("Unmatched Data", Table)
        annotated = widget.Output(ANNOTATED_DATA_SIGNAL_NAME, Table)

    class Error(widget.OWWidget.Error):
        no_categorical = Msg("No categorical variables")

    want_main_area = False
    resizing_enabled = False

    settingsHandler = settings.DomainContextHandler()
    variable = settings.ContextSetting(None)
    pending_value = settings.ContextSetting("")

    def __init__(self):
        super().__init__()

        # This is not a setting because openContext cannot retrieve it before
        # filling the combo. Settings store this as pending_value
        self.value = ""

        self.data = None
        self.n_matched = None

        form = QFormLayout()
        gui.widgetBox(self.controlArea, orientation=form)

        self.var_model = DomainModel(order=DomainModel.MIXED,
                                     valid_types=DiscreteVariable)
        var_combo = gui.comboBox(None,
                                 self,
                                 "variable",
                                 contentsLength=50,
                                 model=self.var_model,
                                 callback=self._on_variable_changed)

        self.value_model = PyListModel()
        value_combo = gui.comboBox(None,
                                   self,
                                   "value",
                                   label="Value: ",
                                   model=self.value_model,
                                   callback=self._on_value_changed,
                                   contentsLength=50,
                                   sizePolicy=(QSizePolicy.MinimumExpanding,
                                               QSizePolicy.Fixed))

        form.addRow("Variable:", var_combo)
        form.addRow("Value:", value_combo)

    @Inputs.data
    def set_data(self, data):
        self.closeContext()
        self.data = data
        self.Error.no_categorical.clear()
        # TODO: Check that contexts are retrieved properly, also when removing
        # and re-adding a connection

        if data:
            self.var_model.set_domain(data.domain)
            self.info.set_input_summary(len(data),
                                        format_summary_details(data))
            if not self.var_model.rowCount():
                self.data = None
                self.Error.no_categorical()
        else:
            self.var_model.set_domain(None)
            self.info.set_input_summary(self.info.NoInput)

        self.variable = self.var_model[0] if self.data else None
        self.openContext(self.data)
        self.set_value_list()
        if self.variable and self.pending_value in self.variable.values:
            self.value = self.pending_value
        self.commit()

    def set_value_list(self):
        if self.variable is None:
            self.value_model.clear()
            self.value = ""
        else:
            self.value_model[:] = self.variable.values
            self.value = self.value_model[0] if self.variable.values else ""

    def _on_variable_changed(self):
        self.set_value_list()
        self.commit()

    def _on_value_changed(self):
        self.pending_value = self.value
        self.commit()

    def commit(self):
        if not (self.data and self.variable and self.value):
            annotated = matching = unmatched = None
            self.n_matched = None
        else:
            column = self.data.get_column_view(self.variable)[0]
            valind = self.variable.values.index(self.value)
            mask = column == valind
            annotated = create_annotated_table(self.data, np.flatnonzero(mask))
            matching = self.data[mask]
            unmatched = self.data[~mask]
            self.n_matched = len(matching)

        self.Outputs.matching.send(matching)
        self.Outputs.unmatched.send(unmatched)
        self.Outputs.annotated.send(annotated)

    def send_report(self):
        if not self.data:
            return

        self.report_items(
            "", [("Filter", f"{self.variable.name} = '{self.value}'"),
                 ("Matching instances",
                  f"{self.n_matched} (out of {len(self.data)})")])
Esempio n. 2
0
class OWGeoTransform(OWWidget):
    name = "Geo Transform"
    description = "Transform geographic coordinates from one system to another."
    icon = "icons/GeoTransform.svg"
    priority = 320
    keywords = ["transform", "geo"]

    class Inputs:
        data = Input("Data", Table)

    class Outputs:
        data = Output("Data", Table)

    settingsHandler = settings.DomainContextHandler()
    attr_lat = settings.ContextSetting(None)
    attr_lon = settings.ContextSetting(None)
    from_idx = settings.Setting("Slovenia 1996 / Slovene National Grid")
    to_idx = settings.Setting("WGS 84")
    replace_original = settings.Setting(True)

    want_main_area = False
    resizing_enabled = False

    EPSG_CODES = get_projections()

    class Error(OWWidget.Error):
        no_lat_lon_vars = Msg("Data has no latitude and longitude variables.")

    def __init__(self):
        super().__init__()
        self.data = None

        layout = QFormLayout()
        gui.widgetBox(self.controlArea, box="Coordinates:", orientation=layout)
        self.variable_model = DomainModel(order=DomainModel.MIXED,
                                          valid_types=(ContinuousVariable, ))
        args = dict(contentsLength=100,
                    searchable=True,
                    model=self.variable_model,
                    orientation=Qt.Horizontal)
        layout.addRow("Latitude:", gui.comboBox(None, self, "attr_lat",
                                                **args))
        layout.addRow("Longitude:", gui.comboBox(None, self, "attr_lon",
                                                 **args))
        layout.addWidget(
            gui.checkBox(
                None,
                self,
                "replace_original",
                "Replace original coordinates",
                tooltip="If unchecked, the original coordinates are retained "
                "and new coordinates are added as separate variables."))

        layout = QFormLayout()
        gui.widgetBox(self.controlArea, "Transformation:", orientation=layout)
        args["model"] = PyListModelTooltip(self.EPSG_CODES,
                                           list(self.EPSG_CODES))
        layout.addRow("From:", gui.comboBox(None, self, "from_idx", **args))
        layout.addRow("To:", gui.comboBox(None, self, "to_idx", **args))

        self.commit_button = gui.button(self.controlArea, self, "&Commit",
                                        self.apply)

    def init_attr_values(self):
        self.variable_model.set_domain(self.data.domain if self.data else None)
        self.Error.no_lat_lon_vars.clear()

        if self.variable_model.rowCount() < 2:
            self.Error.no_lat_lon_vars()
            lat, lon = None, None
            self.data = None
        else:
            lat, lon = find_lat_lon(self.data,
                                    filter_hidden=True,
                                    fallback=False)
            if lat is None or lon is None:
                lat, lon = self.variable_model[:2]

        self.attr_lat, self.attr_lon = lat, lon

    @Inputs.data
    def set_data(self, data: Table = None):
        self.data = data
        self.closeContext()
        self.init_attr_values()
        self.openContext(self.data)
        self.apply()

    def apply(self):
        if not self.data:
            self.Outputs.data.send(None)
            return

        out = self.data.transform(self._transformed_domain())
        self.Outputs.data.send(out)

    def _transformed_domain(self):
        dom = self.data.domain
        orig_coords = (self.attr_lat, self.attr_lon)

        names = [var.name for var in orig_coords]
        if not self.replace_original:
            # If appending, use the same names, just with numbers for uniqueness
            existing = [v.name for v in chain(dom.variables, dom.metas)]
            names = get_unique_names(existing, names)

        transformer = Transformer.from_crs(self.EPSG_CODES[self.from_idx],
                                           self.EPSG_CODES[self.to_idx])
        transformer_common = GeoTransformerCommon(transformer, *orig_coords)
        coords = (ContinuousVariable(name,
                                     compute_value=GeoTransformer(
                                         transformer_common, col))
                  for col, name in enumerate(names))

        if self.replace_original:
            tr = dict(zip(orig_coords, coords))

            def r(variables):
                return [tr.get(var, var) for var in variables]

            return Domain(r(dom.attributes), r(dom.class_vars), r(dom.metas))

        # Put each new variable in attributes, if it was there, else to metas
        attrs, metas = list(dom.attributes), list(dom.metas)
        for orig, new in zip(orig_coords, coords):
            (attrs if orig in dom.attributes else metas).append(new)
        return Domain(attrs, dom.class_vars, metas)