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