|
| 1 | +from itertools import chain |
| 2 | + |
| 3 | +from AnyQt.QtCore import Qt |
| 4 | + |
| 5 | +from Orange.data import (Table, ContinuousVariable, DiscreteVariable, |
| 6 | + StringVariable, Domain) |
| 7 | +from Orange.widgets.settings import (Setting, ContextSetting, |
| 8 | + PerfectDomainContextHandler) |
| 9 | +from Orange.widgets.utils.itemmodels import DomainModel |
| 10 | +from Orange.widgets.widget import OWWidget, Msg |
| 11 | +from Orange.widgets import gui |
| 12 | + |
| 13 | + |
| 14 | +class OWTranspose(OWWidget): |
| 15 | + name = "Transpose" |
| 16 | + description = "Transpose data table." |
| 17 | + icon = "icons/Transpose.svg" |
| 18 | + priority = 2000 |
| 19 | + |
| 20 | + inputs = [("Data", Table, "set_data")] |
| 21 | + outputs = [("Data", Table)] |
| 22 | + |
| 23 | + resizing_enabled = False |
| 24 | + want_main_area = False |
| 25 | + |
| 26 | + settingsHandler = PerfectDomainContextHandler(metas_in_res=True) |
| 27 | + feature_names_column = ContextSetting("None") |
| 28 | + class_variable_index = ContextSetting(0) |
| 29 | + auto_apply = Setting(True) |
| 30 | + |
| 31 | + class Error(OWWidget.Error): |
| 32 | + value_error = Msg("{}") |
| 33 | + |
| 34 | + def __init__(self): |
| 35 | + super().__init__() |
| 36 | + self.data = None |
| 37 | + |
| 38 | + # GUI |
| 39 | + options = dict(callback=self.apply, orientation=Qt.Horizontal, |
| 40 | + labelWidth=100, contentsLength=12) |
| 41 | + self.feature_model = DomainModel( |
| 42 | + order=DomainModel.METAS, placeholder="None", |
| 43 | + valid_types=StringVariable, alphabetical=True) |
| 44 | + self.feature_combo = gui.comboBox( |
| 45 | + self.controlArea, self, "feature_names_column", |
| 46 | + box="Create feature names from", sendSelectedValue=True, **options) |
| 47 | + self.feature_combo.setModel(self.feature_model) |
| 48 | + |
| 49 | + self.class_model = DomainModel( |
| 50 | + order=DomainModel.ATTRIBUTES, placeholder="None", |
| 51 | + valid_types=DomainModel.PRIMITIVE, alphabetical=True) |
| 52 | + self.class_combo = gui.comboBox( |
| 53 | + self.controlArea, self, "class_variable_index", |
| 54 | + box="Class attribute", **options) |
| 55 | + self.class_combo.setModel(self.class_model) |
| 56 | + |
| 57 | + self.apply_button = gui.auto_commit( |
| 58 | + self.controlArea, self, "auto_apply", "&Apply", |
| 59 | + box=False, commit=self.apply) |
| 60 | + |
| 61 | + def set_data(self, data): |
| 62 | + self.closeContext() |
| 63 | + self.data = data |
| 64 | + self.update_controls() |
| 65 | + self.openContext(data) |
| 66 | + self.class_variable_index = min(self.class_variable_index, |
| 67 | + len(self.class_model) - 1) |
| 68 | + self.apply() |
| 69 | + |
| 70 | + def update_controls(self): |
| 71 | + self.feature_model.set_domain(None) |
| 72 | + self.class_model.set_domain(None) |
| 73 | + self.feature_names_column = "None" |
| 74 | + if self.data: |
| 75 | + self.feature_model.set_domain(self.data.domain) |
| 76 | + self.feature_names_column = self.data.domain.metas[0].name \ |
| 77 | + if self.data.domain.metas else "None" |
| 78 | + names = chain.from_iterable( |
| 79 | + list(a.attributes) for a in self.data.domain.attributes) |
| 80 | + variables = chain.from_iterable( |
| 81 | + (DiscreteVariable(name), ContinuousVariable(name)) |
| 82 | + for name in set(names)) |
| 83 | + self.class_model.set_domain(Domain(list(variables))) |
| 84 | + self.class_variable_index = min(1, len(self.class_model) - 1) |
| 85 | + |
| 86 | + def apply(self): |
| 87 | + self.clear_messages() |
| 88 | + transposed = None |
| 89 | + if self.data: |
| 90 | + options = dict() |
| 91 | + if self.feature_names_column != "None": |
| 92 | + options["feature_names_column"] = self.feature_names_column |
| 93 | + class_var = self.class_model[self.class_variable_index] |
| 94 | + if class_var is not None: |
| 95 | + options["class_name"] = class_var.name |
| 96 | + options["class_type"] = type(class_var) |
| 97 | + |
| 98 | + try: |
| 99 | + transposed = Table.transpose(self.data, **options) |
| 100 | + except ValueError as e: |
| 101 | + self.Error.value_error(e) |
| 102 | + self.send("Data", transposed) |
| 103 | + |
| 104 | + def send_report(self): |
| 105 | + class_var = self.class_model[self.class_variable_index] \ |
| 106 | + if len(self.class_model) else "None" |
| 107 | + self.report_items( |
| 108 | + "", [("Create feature names from", self.feature_names_column), |
| 109 | + ("Class variable as", class_var)]) |
| 110 | + if self.data: |
| 111 | + self.report_data("Data", self.data) |
| 112 | + |
| 113 | + |
| 114 | +if __name__ == "__main__": |
| 115 | + from AnyQt.QtWidgets import QApplication |
| 116 | + |
| 117 | + app = QApplication([]) |
| 118 | + ow = OWTranspose() |
| 119 | + d = Table("zoo") |
| 120 | + d.domain.attributes[0].attributes = {"key": "value"} |
| 121 | + ow.set_data(d) |
| 122 | + ow.show() |
| 123 | + app.exec_() |
| 124 | + ow.saveSettings() |
0 commit comments