Skip to content
Merged
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
54 changes: 31 additions & 23 deletions Orange/widgets/data/owconcatenate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
Concatenate (append) two or more datasets.

"""

from collections import OrderedDict, namedtuple, defaultdict
from functools import reduce
from itertools import chain, count
from typing import List
from typing import List, Optional, Sequence

import numpy as np
from AnyQt.QtWidgets import QFormLayout
Expand All @@ -21,9 +20,9 @@
from Orange.widgets import widget, gui, settings
from Orange.widgets.settings import Setting
from Orange.widgets.utils.annotated_data import add_columns
from Orange.widgets.utils.sql import check_sql_input
from Orange.widgets.utils.sql import check_sql_input, check_sql_input_sequence
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import Input, Output, Msg
from Orange.widgets.widget import Input, MultiInput, Output, Msg


class OWConcatenate(widget.OWWidget):
Expand All @@ -35,10 +34,9 @@ class OWConcatenate(widget.OWWidget):

class Inputs:
primary_data = Input("Primary Data", Orange.data.Table)
additional_data = Input("Additional Data",
Orange.data.Table,
multiple=True,
default=True)
additional_data = MultiInput(
"Additional Data", Orange.data.Table, default=True
)

class Outputs:
data = Output("Data", Orange.data.Table)
Expand Down Expand Up @@ -86,7 +84,7 @@ def __init__(self):
super().__init__()

self.primary_data = None
self.more_data = OrderedDict()
self._more_data_input: List[Optional[Orange.data.Table]] = []

self.mergebox = gui.vBox(self.controlArea, "Variable Merging")
box = gui.radioButtons(
Expand Down Expand Up @@ -158,12 +156,22 @@ def set_primary_data(self, data):
self.primary_data = data

@Inputs.additional_data
@check_sql_input
def set_more_data(self, data=None, sig_id=None):
if data is not None:
self.more_data[sig_id] = data
elif sig_id in self.more_data:
del self.more_data[sig_id]
@check_sql_input_sequence
def set_more_data(self, index, data):
self._more_data_input[index] = data

@Inputs.additional_data.insert
@check_sql_input_sequence
def insert_more_data(self, index, data):
self._more_data_input.insert(index, data)

@Inputs.additional_data.remove
def remove_more_data(self, index):
self._more_data_input.pop(index)

@property
def more_data(self) -> Sequence[Orange.data.Table]:
return [t for t in self._more_data_input if t is not None]

def handleNewSignals(self):
self.mergebox.setDisabled(self.primary_data is not None)
Expand All @@ -177,8 +185,8 @@ def incompatible_types(self):
types_ = set()
if self.primary_data is not None:
types_.add(type(self.primary_data))
for key in self.more_data:
types_.add(type(self.more_data[key]))
for table in self.more_data:
types_.add(type(table))
if len(types_) > 1:
return True

Expand All @@ -188,13 +196,13 @@ def apply(self):
self.Warning.renamed_variables.clear()
tables, domain, source_var = [], None, None
if self.primary_data is not None:
tables = [self.primary_data] + list(self.more_data.values())
tables = [self.primary_data] + list(self.more_data)
domain = self.primary_data.domain
elif self.more_data:
if self.ignore_compute_value:
tables = self._dumb_tables()
else:
tables = self.more_data.values()
tables = self.more_data
domains = [table.domain for table in tables]
domain = self.merge_domains(domains)

Expand Down Expand Up @@ -230,7 +238,7 @@ def enumerated_parts(domain):
return enumerate((domain.attributes, domain.class_vars, domain.metas))

compute_value_groups = defaultdict(set)
for table in self.more_data.values():
for table in self.more_data:
for part, part_vars in enumerated_parts(table.domain):
for var in part_vars:
desc = (var.name, type(var), part)
Expand All @@ -240,7 +248,7 @@ def enumerated_parts(domain):
if len(compute_values) > 1}

dumb_tables = []
for table in self.more_data.values():
for table in self.more_data:
dumb_domain = Orange.data.Domain(
*[[var.copy(compute_value=None)
if (var.name, type(var), part) in to_dumbify
Expand Down Expand Up @@ -352,5 +360,5 @@ def _unique_vars(seq: List[Orange.data.Variable]):

if __name__ == "__main__": # pragma: no cover
WidgetPreview(OWConcatenate).run(
set_more_data=[(Orange.data.Table("iris"), 0),
(Orange.data.Table("zoo"), 1)])
insert_more_data=[(0, Orange.data.Table("iris")),
(1, Orange.data.Table("zoo"))])
90 changes: 65 additions & 25 deletions Orange/widgets/data/owpythonscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from Orange.widgets.utils import itemmodels
from Orange.widgets.settings import Setting
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import OWWidget, Input, Output
from Orange.widgets.widget import OWWidget, MultiInput, Output

if TYPE_CHECKING:
from typing_extensions import TypedDict
Expand Down Expand Up @@ -526,14 +526,18 @@ class OWPythonScript(OWWidget):
keywords = ["program", "function"]

class Inputs:
data = Input("Data", Table, replaces=["in_data"],
default=True, multiple=True)
learner = Input("Learner", Learner, replaces=["in_learner"],
default=True, multiple=True)
classifier = Input("Classifier", Model, replaces=["in_classifier"],
default=True, multiple=True)
object = Input("Object", object, replaces=["in_object"],
default=False, multiple=True)
data = MultiInput(
"Data", Table, replaces=["in_data"], default=True
)
learner = MultiInput(
"Learner", Learner, replaces=["in_learner"], default=True
)
classifier = MultiInput(
"Classifier", Model, replaces=["in_classifier"], default=True
)
object = MultiInput(
"Object", object, replaces=["in_object"], default=False
)

class Outputs:
data = Output("Data", Table, replaces=["out_data"])
Expand Down Expand Up @@ -562,7 +566,7 @@ def __init__(self):
super().__init__()

for name in self.signal_names:
setattr(self, name, {})
setattr(self, name, [])

self.splitCanvas = QSplitter(Qt.Vertical, self.mainArea)
self.mainArea.layout().addWidget(self.splitCanvas)
Expand Down Expand Up @@ -779,29 +783,65 @@ def _saveState(self):
self.scriptText = self.text.toPlainText()
self.splitterState = bytes(self.splitCanvas.saveState())

def handle_input(self, obj, sig_id, signal):
def set_input(self, index, obj, signal):
dic = getattr(self, signal)
if obj is None:
if sig_id in dic.keys():
del dic[sig_id]
else:
dic[sig_id] = obj
dic[index] = obj

def insert_input(self, index, obj, signal):
dic = getattr(self, signal)
dic.insert(index, obj)

def remove_input(self, index, signal):
dic = getattr(self, signal)
dic.pop(index)

@Inputs.data
def set_data(self, data, sig_id):
self.handle_input(data, sig_id, "data")
def set_data(self, index, data):
self.set_input(index, data, "data")

@Inputs.data.insert
def insert_data(self, index, data):
self.insert_input(index, data, "data")

@Inputs.data.remove
def remove_data(self, index):
self.remove_input(index, "data")

@Inputs.learner
def set_learner(self, data, sig_id):
self.handle_input(data, sig_id, "learner")
def set_learner(self, index, learner):
self.set_input(index, learner, "learner")

@Inputs.learner.insert
def insert_learner(self, index, learner):
self.insert_input(index, learner, "learner")

@Inputs.learner.remove
def remove_learner(self, index):
self.remove_input(index, "learner")

@Inputs.classifier
def set_classifier(self, data, sig_id):
self.handle_input(data, sig_id, "classifier")
def set_classifier(self, index, classifier):
self.set_input(index, classifier, "classifier")

@Inputs.classifier.insert
def insert_classifier(self, index, classifier):
self.insert_input(index, classifier, "classifier")

@Inputs.classifier.remove
def remove_classifier(self, index):
self.remove_input(index, "classifier")

@Inputs.object
def set_object(self, data, sig_id):
self.handle_input(data, sig_id, "object")
def set_object(self, index, object):
self.set_input(index, object, "object")

@Inputs.object.insert
def insert_object(self, index, object):
self.insert_input(index, object, "object")

@Inputs.object.remove
def remove_object(self, index):
self.remove_input(index, "object")

def handleNewSignals(self):
# update fake signature labels
Expand Down Expand Up @@ -925,7 +965,7 @@ def initial_locals_state(self):
d = {}
for name in self.signal_names:
value = getattr(self, name)
all_values = list(value.values())
all_values = list(value)
one_value = all_values[0] if len(all_values) == 1 else None
d["in_" + name + "s"] = all_values
d["in_" + name] = one_value
Expand Down
41 changes: 25 additions & 16 deletions Orange/widgets/data/owrank.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
import warnings
from collections import OrderedDict, namedtuple
from collections import namedtuple
from functools import partial
from itertools import chain
from types import SimpleNamespace
Expand Down Expand Up @@ -33,7 +33,7 @@
from Orange.widgets.utils.itemmodels import PyTableModel
from Orange.widgets.utils.sql import check_sql_input
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import AttributeList, Input, Msg, Output, OWWidget
from Orange.widgets.widget import AttributeList, Input, MultiInput, Output, Msg, OWWidget

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -256,7 +256,7 @@ class OWRank(OWWidget, ConcurrentWidgetMixin):

class Inputs:
data = Input("Data", Table)
scorer = Input("Scorer", score.Scorer, multiple=True)
scorer = MultiInput("Scorer", score.Scorer, filter_none=True)

class Outputs:
reduced_data = Output("Reduced Data", Table, default=True)
Expand Down Expand Up @@ -292,7 +292,7 @@ class Warning(OWWidget.Warning):
def __init__(self):
OWWidget.__init__(self)
ConcurrentWidgetMixin.__init__(self)
self.scorers = OrderedDict()
self.scorers: List[ScoreMeta] = []
self.out_domain_desc = None
self.data = None
self.problem_type_mode = ProblemType.CLASSIFICATION
Expand Down Expand Up @@ -440,18 +440,27 @@ def handleNewSignals(self):
self.on_select()

@Inputs.scorer
def set_learner(self, scorer, id): # pylint: disable=redefined-builtin
if scorer is None:
self.scorers.pop(id, None)
else:
# Avoid caching a (possibly stale) previous instance of the same
# Scorer passed via the same signal
if id in self.scorers:
self.scorers_results = {}
def set_learner(self, index, scorer):
self.scorers[index] = ScoreMeta(
scorer.name, scorer.name, scorer,
ProblemType.from_variable(scorer.class_type),
False
)
self.scorers_results = {}

self.scorers[id] = ScoreMeta(scorer.name, scorer.name, scorer,
ProblemType.from_variable(scorer.class_type),
False)
@Inputs.scorer.insert
def insert_learner(self, index: int, scorer):
self.scorers.insert(index, ScoreMeta(
scorer.name, scorer.name, scorer,
ProblemType.from_variable(scorer.class_type),
False
))
self.scorers_results = {}

@Inputs.scorer.remove
def remove_learner(self, index):
self.scorers.pop(index)
self.scorers_results = {}

def _get_methods(self):
return [
Expand All @@ -469,7 +478,7 @@ def _get_methods(self):

def _get_scorers(self):
scorers = []
for scorer in self.scorers.values():
for scorer in self.scorers:
if scorer.problem_type in (
self.problem_type_mode,
ProblemType.UNSUPERVISED,
Expand Down
Loading