source: orange/Orange/OrangeWidgets/Data/OWDataSampler.py @ 9671:a7b056375472

Revision 9671:a7b056375472, 15.5 KB checked in by anze <anze.staric@…>, 2 years ago (diff)

Moved orange to Orange (part 2)

Line 
1r"""
2<name>Data Sampler</name>
3<description>Selects a subset of instances from the data set.</description>
4<icon>icons/DataSampler.png</icon>
5<contact>Aleksander Sadikov (aleksander.sadikov(@at@)fri.uni-lj.si)</contact>
6<priority>1125</priority>
7"""
8from OWWidget import *
9import OWGUI
10import random
11
12class OWDataSampler(OWWidget):
13    settingsList=["Stratified", "Repeat", "UseSpecificSeed", "RandomSeed",
14    "GroupSeed", "outFold", "Folds", "SelectType", "useCases", "nCases", "selPercentage", "LOO",
15    "CVFolds", "CVFoldsInternal", "nGroups", "pGroups", "GroupText"]
16   
17    contextHandlers = {"":DomainContextHandler("", ["nCases","selPercentage"])}
18    def __init__(self, parent=None, signalManager=None):
19        OWWidget.__init__(self, parent, signalManager, 'SampleData', wantMainArea = 0)
20
21        self.inputs = [("Data", ExampleTable, self.setData)]
22        self.outputs = [("Data Sample", ExampleTable), ("Remaining Data", ExampleTable)]
23
24        # initialization of variables
25        self.data = None                        # dataset (incoming stream)
26        self.indices = None                     # indices that control sampling
27        self.ind = None                         # indices that control sampling
28
29        self.Stratified = 1                     # use stratified sampling if possible?
30        self.Repeat = 0                         # can elements repeat in a sample?
31        self.UseSpecificSeed = 0                # use a specific random seed?
32        self.RandomSeed = 1                     # specific seed used
33        self.GroupSeed = 1                      # current seed for multiple group selection
34        self.outFold = 1                        # folder/group to output
35        self.Folds = 1                          # total number of folds/groups
36
37        self.SelectType = 0                     # sampling type (LOO, CV, ...)
38        self.useCases = 0                       # use a specific number of cases?
39        self.nCases = 25                        # number of cases to use
40        self.selPercentage = 30                 # sample size in %
41        self.LOO = 1                            # use LOO?
42        self.CVFolds = 10                       # number of CV folds
43        self.CVFoldsInternal = 10               # number of CV folds (for internal use)
44        self.nGroups = 3                        # number of groups
45        self.pGroups = [0.1,0.25,0.5]           # sizes of groups
46        self.GroupText = '0.1,0.25,0.5'         # assigned to Groups Control (for internal use)
47
48        self.loadSettings()
49        # GUI
50       
51        # Info Box
52        box1 = OWGUI.widgetBox(self.controlArea, "Information", addSpace=True)
53        self.infoa = OWGUI.widgetLabel(box1, 'No data on input.')
54        self.infob = OWGUI.widgetLabel(box1, ' ')
55        self.infoc = OWGUI.widgetLabel(box1, ' ')
56       
57        # Options Box
58        box2 = OWGUI.widgetBox(self.controlArea, 'Options', addSpace=True)
59        OWGUI.checkBox(box2, self, 'Stratified', 'Stratified (if possible)', callback=self.settingsChanged)
60        OWGUI.checkWithSpin(box2, self, 'Set random seed:', 0, 32767, 'UseSpecificSeed', 'RandomSeed', checkCallback=self.settingsChanged, spinCallback=self.settingsChanged)
61
62        # Sampling Type Box
63        self.s = [None, None, None, None]
64        self.sBox = OWGUI.widgetBox(self.controlArea, "Sampling type", addSpace=True)
65        self.sBox.buttons = []
66
67        # Random Sampling
68        self.s[0] = OWGUI.appendRadioButton(self.sBox, self, "SelectType", 'Random sampling')
69       
70        # indent
71        indent = sep=OWGUI.checkButtonOffsetHint(self.s[0])
72        # repeat checkbox
73        self.h1Box = OWGUI.indentedBox(self.sBox, sep=indent, orientation = "horizontal")
74        OWGUI.checkBox(self.h1Box, self, 'Repeat', 'With replacement', callback=self.settingsChanged)
75
76        # specified number of elements checkbox
77        self.h2Box = OWGUI.indentedBox(self.sBox, sep=indent, orientation = "horizontal")
78        OWGUI.checkWithSpin(self.h2Box, self, 'Sample size (instances):', 1, 1000000000, 'useCases', 'nCases', checkCallback=[self.uCases, self.settingsChanged], spinCallback=self.settingsChanged)
79        OWGUI.rubber(self.h2Box)
80       
81        # percentage slider
82        self.h3Box = OWGUI.indentedBox(self.sBox, sep=indent, orientation = "horizontal")
83        OWGUI.widgetLabel(self.h3Box, "Sample size:")
84        self.slidebox = OWGUI.indentedBox(self.sBox, sep=indent, orientation = "horizontal")
85        OWGUI.hSlider(self.slidebox, self, 'selPercentage', minValue=1, maxValue=100, step=1, ticks=10, labelFormat="   %d%%", callback=self.settingsChanged)
86
87        # Cross Validation
88        self.s[1] = OWGUI.appendRadioButton(self.sBox, self, "SelectType", 'Cross validation')
89       
90        box = OWGUI.indentedBox(self.sBox, sep=indent, orientation = "horizontal")
91        OWGUI.spin(box, self, 'CVFolds', 2, 100, step=1, label='Number of folds:  ', callback=[self.changeCombo, self.settingsChanged])
92        OWGUI.rubber(box)
93
94        # Leave-One-Out
95        self.s[2] = OWGUI.appendRadioButton(self.sBox, self, "SelectType", 'Leave-one-out')
96
97        # Multiple Groups
98        self.s[3] = OWGUI.appendRadioButton(self.sBox, self, "SelectType", 'Multiple subsets')
99        gbox = OWGUI.indentedBox(self.sBox, sep=indent, orientation = "horizontal")
100        OWGUI.lineEdit(gbox, self, 'GroupText', label='Subset sizes (e.g. "0.1, 0.2, 0.5"):', callback=self.multipleChanged)
101
102        # Output Group Box
103        self.foldcombo = OWGUI.comboBox(self.controlArea, self, "outFold", 'Output Data for Fold / Group', 'Fold / group:', orientation = "horizontal", items = range(1,101), callback = self.foldChanged, sendSelectedValue = 1, valueType = int)
104        self.foldcombo.setEnabled(False)
105
106        # Select Data Button
107        OWGUI.rubber(self.controlArea)
108        self.sampleButton = OWGUI.button(self.controlArea, self, 'Sample &Data', callback = self.process, addToLayout=False, default=True)
109        self.buttonBackground.layout().setDirection(QBoxLayout.TopToBottom)
110        self.buttonBackground.layout().insertWidget(0, self.sampleButton)
111        self.buttonBackground.show()
112        self.s[self.SelectType].setChecked(True)    # set initial radio button on (default sample type)
113
114        # CONNECTIONS
115        # set connections for RadioButton (SelectType)
116        self.dummy1 = [None]*len(self.s)
117        for i in range(len(self.s)):
118            self.dummy1[i] = lambda x, v=i: self.sChanged(x, v)
119            self.connect(self.s[i], SIGNAL("toggled(bool)"), self.dummy1[i])
120
121        # final touch
122        self.resize(200, 275)
123
124    # CONNECTION TRIGGER AND GUI ROUTINES
125    # enables RadioButton switching
126    def sChanged(self, value, id):
127        self.SelectType = id
128        self.process()
129
130    def multipleChanged(self):
131        try:
132            self.pGroups = [float(x) for x in self.GroupText.split(',')]
133            self.nGroups = len(self.pGroups)
134            self.error(1)
135        except:
136            self.error("Invalid specification for sizes of subsets.", 1)
137        self.changeCombo()
138        self.settingsChanged()
139
140    # reflect user's actions that change combobox contents
141    def changeCombo(self):
142        # refill combobox
143        self.Folds = 1
144        if self.SelectType == 1:
145            self.Folds = self.CVFolds
146        elif self.SelectType == 2:
147            if self.data:
148                self.Folds = len(self.data)
149            else:
150                self.Folds = 1
151        elif self.SelectType == 3:
152            self.Folds = self.nGroups
153        self.foldcombo.clear()
154        for x in range(self.Folds):
155            self.foldcombo.addItem(str(x+1))
156
157    # triggered on change in output fold combobox
158    def foldChanged(self):
159        #self.outFold = int(ix+1)
160        if self.data:
161            self.sdata()
162
163    # switches between cases and percentage (random sampling)
164    def uCases(self):
165        if self.useCases == 1:
166            self.h3Box.setEnabled(False)
167            self.slidebox.setEnabled(False)
168        else:
169            self.h3Box.setEnabled(True)
170            self.slidebox.setEnabled(True)
171
172    # I/O STREAM ROUTINES
173    # handles changes of input stream
174    def setData(self, dataset):
175        self.closeContext()
176        if dataset:
177            self.infoa.setText('%d instances in input data set.' % len(dataset))
178            self.data = dataset
179            self.openContext("", dataset)
180            self.process()
181        else:
182            self.infoa.setText('No data on input.')
183            self.infob.setText('')
184            self.infoc.setText('')
185            self.send("Data Sample", None)
186            self.send("Remaining Data", None)
187            self.data = None
188
189    # feeds the output stream
190    def sdata(self):
191        # select data
192        if self.SelectType == 0:
193            if self.useCases == 1 and self.Repeat == 1:
194                sample = orange.ExampleTable(self.data.domain)
195                for x in range(self.nCases):
196                    sample.append(self.data[random.randint(0,len(self.data)-1)])
197                remainder = None
198                self.infob.setText('Random sampling with repetitions, %d instances.' % self.nCases)
199            else:
200                sample = self.data.select(self.ind, 0)
201                remainder = self.data.select(self.ind, 1)
202            self.infoc.setText('Output: %d instances.' % len(sample))
203        elif self.SelectType == 3:
204            self.ind = self.indices(self.data, p0 = self.pGroups[self.outFold-1])
205            sample = self.data.select(self.ind, 0)
206            remainder = self.data.select(self.ind, 1)
207            self.infoc.setText('Output: subset %(outFold)d of %(folds)d, %(instances)d instance(s).' % {"outFold": self.outFold, "folds": self.Folds, "instances": len(sample)})
208        else:
209            sample = self.data.select(self.ind, self.outFold-1)
210            remainder = self.data.select(self.ind, self.outFold-1, negate=1)
211            self.infoc.setText('Output: fold %(outFold)d of %(folds)d, %(instances)d instance(s).' % {"outFold": self.outFold, "folds": self.Folds, "instances": len(sample)})
212        # set name (by PJ)
213        if sample:
214            sample.name = self.data.name
215        if remainder:
216            remainder.name = self.data.name
217        # send data
218        self.nSample = len(sample)
219        self.nRemainder = len(remainder) if remainder is not None else 0
220        self.send("Data Sample", sample)
221        self.send("Remaining Data", remainder)
222       
223        self.sampleButton.setEnabled(False)
224
225    # MAIN SWITCH
226    # processes data after the user requests it
227    def process(self):
228        # reset errors, fold selected
229        self.error(0)
230        self.outFold = 1
231
232        # check for data
233        if self.data == None:
234            return
235        else:
236            self.infob.setText('')
237            self.infoc.setText('')
238
239        # Random Selection
240        if self.SelectType == 0:
241            # apply selected options
242            if self.useCases == 1 and self.Repeat != 1:
243                if self.nCases > len(self.data):
244                    self.error(0, "Sample size (w/o repetitions) larger than dataset.")
245                    return
246                self.indices = orange.MakeRandomIndices2(p0=int(self.nCases))
247                self.infob.setText('Random sampling, using exactly %d instances.' % self.nCases)
248            else:
249                if self.selPercentage == 100:
250                    self.indices = orange.MakeRandomIndices2(p0=int(len(self.data)))
251                else:
252                    self.indices = orange.MakeRandomIndices2(p0=float(self.selPercentage/100.0))
253                self.infob.setText('Random sampling, %d%% of input instances.' % self.selPercentage)
254            if self.Stratified == 1: self.indices.stratified = self.indices.StratifiedIfPossible
255            else:                    self.indices.stratified = self.indices.NotStratified
256            if self.UseSpecificSeed == 1: self.indices.randseed = self.RandomSeed
257            else:                         self.indices.randomGenerator = orange.RandomGenerator(random.randint(0,65536))
258
259            # call output stream handler to send data
260            self.ind = self.indices(self.data)
261
262        # Cross Validation / LOO
263        elif self.SelectType == 1 or self.SelectType == 2:
264            # apply selected options
265            if self.SelectType == 2:
266                self.CVFoldsInternal = len(self.data)
267                self.infob.setText('Leave-one-out.')
268            else:
269                self.CVFoldsInternal = self.CVFolds
270                self.infob.setText('%d-fold cross validation.' % self.CVFolds)
271            self.indices = orange.MakeRandomIndicesCV(folds = self.CVFoldsInternal)
272            if self.Stratified == 1:
273                self.indices.stratified = self.indices.StratifiedIfPossible
274            else:
275                self.indices.stratified = self.indices.NotStratified
276            if self.UseSpecificSeed == 1:
277                #self.indices.randomGenerator = orange.RandomGenerator(random.randint(0,65536))
278                self.indices.randseed = self.RandomSeed
279            else:
280                #self.indices.randomGenerator = orange.RandomGenerator(random.randint(0,65536))
281                self.indices.randseed = random.randint(0,65536)
282
283            # call output stream handler to send data
284            self.ind = self.indices(self.data)
285
286        # MultiGroup
287        elif self.SelectType == 3:
288            self.infob.setText('Multiple subsets.')
289            #parse group specification string
290
291            #prepare indices generator
292            self.indices = orange.MakeRandomIndices2()
293            if self.Stratified == 1: self.indices.stratified = self.indices.StratifiedIfPossible
294            else:                    self.indices.stratified = self.indices.NotStratified
295            if self.UseSpecificSeed == 1: self.indices.randseed = self.RandomSeed
296            else:                         self.indices.randomGenerator = orange.RandomGenerator(random.randint(0,65536))
297
298        # enable fold selection and fill combobox if applicable
299        if self.SelectType == 0:
300            self.foldcombo.setEnabled(False)
301        else:
302            self.foldcombo.setEnabled(True)
303            self.changeCombo()
304
305        # call data output routine
306        self.sdata()
307       
308    def settingsChanged(self):
309        self.sampleButton.setEnabled(True)
310
311    def sendReport(self):
312        if self.SelectType == 0:
313            if self.useCases:
314                stype = "Random sample of %i instances" % self.nCases
315            else:
316                stype = "Random sample with %i%% instances" % self.selPercentage
317        elif self.SelectType == 1:
318            stype = "%i-fold cross validation" % self.CVFolds
319        elif self.SelectType == 2:
320            stype = "Leave one out"
321        elif self.SelectType == 3:
322            stype = "Multiple subsets"
323        self.reportSettings("Settings", [("Sampling type", stype), 
324                                         ("Stratification", OWGUI.YesNo[self.Stratified]),
325                                         ("Random seed", str(self.RandomSeed) if self.UseSpecificSeed else "auto")])
326        if self.data is not None:
327            self.reportSettings("Data", [("Input", "%i examples" % len(self.data)), 
328                                         ("Sample", "%i examples" % self.nSample), 
329                                         ("Rest", "%i examples" % self.nRemainder)])
330        else:
331            self.reportSettings("Data", [("Input", "None")])
332
333##############################################################################
334# Test the widget, run from prompt
335
336if __name__=="__main__":
337    appl = QApplication(sys.argv)
338    ow = OWDataSampler()
339    data = orange.ExampleTable('../../doc/datasets/iris.tab')
340    ow.setData(data)
341    ow.show()
342    appl.exec_()
343    ow.saveSettings()
Note: See TracBrowser for help on using the repository browser.