|
| 1 | +import random |
| 2 | + |
| 3 | +from AnyQt.QtCore import Qt |
| 4 | +from AnyQt.QtWidgets import QSizePolicy |
| 5 | + |
| 6 | +from Orange.data import Table |
| 7 | +from Orange.preprocess import Randomize |
| 8 | +from Orange.widgets.settings import Setting |
| 9 | +from Orange.widgets.widget import OWWidget |
| 10 | +from Orange.widgets import gui |
| 11 | + |
| 12 | + |
| 13 | +class OWRandomize(OWWidget): |
| 14 | + name = "Randomize" |
| 15 | + description = "Randomize features, class and/or metas in data table." |
| 16 | + icon = "icons/Random.svg" |
| 17 | + priority = 2100 |
| 18 | + |
| 19 | + inputs = [("Data", Table, "set_data")] |
| 20 | + outputs = [("Data", Table)] |
| 21 | + |
| 22 | + resizing_enabled = False |
| 23 | + want_main_area = False |
| 24 | + |
| 25 | + shuffle_class = Setting(True) |
| 26 | + shuffle_attrs = Setting(False) |
| 27 | + shuffle_metas = Setting(False) |
| 28 | + scope_prop = Setting(80) |
| 29 | + random_seed = Setting(0) |
| 30 | + auto_apply = Setting(True) |
| 31 | + |
| 32 | + def __init__(self): |
| 33 | + super().__init__() |
| 34 | + self.data = None |
| 35 | + |
| 36 | + # GUI |
| 37 | + box = gui.hBox(self.controlArea, "Shuffled columns") |
| 38 | + box.layout().setSpacing(20) |
| 39 | + self.class_check = gui.checkBox( |
| 40 | + box, self, "shuffle_class", "Classes", |
| 41 | + callback=self._shuffle_check_changed) |
| 42 | + self.attrs_check = gui.checkBox( |
| 43 | + box, self, "shuffle_attrs", "Features", |
| 44 | + callback=self._shuffle_check_changed) |
| 45 | + self.metas_check = gui.checkBox( |
| 46 | + box, self, "shuffle_metas", "Metas", |
| 47 | + callback=self._shuffle_check_changed) |
| 48 | + |
| 49 | + box = gui.vBox(self.controlArea, "Shuffled rows") |
| 50 | + hbox = gui.hBox(box) |
| 51 | + gui.widgetLabel(hbox, "None") |
| 52 | + self.scope_slider = gui.hSlider( |
| 53 | + hbox, self, "scope_prop", minValue=0, maxValue=100, width=140, |
| 54 | + createLabel=False, callback=self._scope_slider_changed) |
| 55 | + gui.widgetLabel(hbox, "All") |
| 56 | + self.scope_label = gui.widgetLabel( |
| 57 | + box, "", alignment=Qt.AlignCenter, |
| 58 | + sizePolicy=(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)) |
| 59 | + self._set_scope_label() |
| 60 | + gui.separator(box, 10, 10) |
| 61 | + self.replicable_check = gui.checkBox( |
| 62 | + box, self, "random_seed", "Replicable shuffling", |
| 63 | + callback=self._shuffle_check_changed) |
| 64 | + |
| 65 | + self.apply_button = gui.auto_commit( |
| 66 | + self.controlArea, self, "auto_apply", "&Apply", |
| 67 | + box=False, commit=self.apply) |
| 68 | + |
| 69 | + @property |
| 70 | + def parts(self): |
| 71 | + return [self.shuffle_class, self.shuffle_attrs, self.shuffle_metas] |
| 72 | + |
| 73 | + def _shuffle_check_changed(self): |
| 74 | + self.apply() |
| 75 | + |
| 76 | + def _scope_slider_changed(self): |
| 77 | + self._set_scope_label() |
| 78 | + self.apply() |
| 79 | + |
| 80 | + def _set_scope_label(self): |
| 81 | + self.scope_label.setText("{}%".format(self.scope_prop)) |
| 82 | + |
| 83 | + def set_data(self, data): |
| 84 | + self.data = data |
| 85 | + self.apply() |
| 86 | + |
| 87 | + def apply(self): |
| 88 | + data = None |
| 89 | + if self.data: |
| 90 | + rand_seed = self.random_seed or None |
| 91 | + size = int(len(self.data) * self.scope_prop / 100) |
| 92 | + random.seed(rand_seed) |
| 93 | + indices = sorted(random.sample(range(len(self.data)), size)) |
| 94 | + type_ = sum(t for t, p in zip(Randomize.Type, self.parts) if p) |
| 95 | + randomized = Randomize(type_, rand_seed)(self.data[indices]) |
| 96 | + data = self.data.copy() |
| 97 | + for i, instance in zip(indices, randomized): |
| 98 | + data[i] = instance |
| 99 | + self.send("Data", data) |
| 100 | + |
| 101 | + def send_report(self): |
| 102 | + labels = ["classes", "features", "metas"] |
| 103 | + include = [label for label, i in zip(labels, self.parts) if i] |
| 104 | + text = "none" if not include else \ |
| 105 | + " and ".join(filter(None, (", ".join(include[:-1]), include[-1]))) |
| 106 | + self.report_items( |
| 107 | + "Settings", |
| 108 | + [("Shuffled columns", text), |
| 109 | + ("Proportion of shuffled rows", "{}%".format(self.scope_prop)), |
| 110 | + ("Replicable", ["no", "yes"][self.random_seed])]) |
| 111 | + |
| 112 | + |
| 113 | +if __name__ == "__main__": |
| 114 | + from AnyQt.QtWidgets import QApplication |
| 115 | + |
| 116 | + app = QApplication([]) |
| 117 | + ow = OWRandomize() |
| 118 | + d = Table("iris") |
| 119 | + ow.set_data(d) |
| 120 | + ow.show() |
| 121 | + app.exec_() |
| 122 | + ow.saveSettings() |
0 commit comments