source: orange/orange/OrangeWidgets/Data/OWMergeData.py @ 6701:c31f4dbb6dfe

Revision 6701:c31f4dbb6dfe, 17.9 KB checked in by ales_erjavec <ales.erjavec@…>, 4 years ago (diff)
  • changed the way data is merged (does not clone attributes, in A+B A attributes (and values) take precedent and reverse in B+A
Line 
1"""
2<name>Merge Data</name>
3<description>Merge datasets based on values of selected attributes.</description>
4<icon>icons/MergeData.png</icon>
5<priority>1110</priority>
6<contact>Peter Juvan (peter.juvan@fri.uni-lj.si)</contact>
7"""
8import orange
9from OWWidget import *
10import OWGUI
11
12class OWMergeData(OWWidget):
13
14#    settingsList = ["varA", "varB"]
15
16    contextHandlers = {"A": DomainContextHandler("A", [ContextField("varA")], syncWithGlobal=False),
17                       "B": DomainContextHandler("B", [ContextField("varB")], syncWithGlobal=False)}                                           
18
19    def __init__(self, parent = None, signalManager = None, name = "Merge data"):
20        OWWidget.__init__(self, parent, signalManager, name, wantMainArea = 0)  #initialize base class
21
22        # set channels
23        self.inputs = [("Examples A", ExampleTable, self.onDataAInput), ("Examples B", ExampleTable, self.onDataBInput)]
24        self.outputs = [("Merged Examples A+B", ExampleTable), ("Merged Examples B+A", ExampleTable)]
25
26        # data
27        self.dataA = None
28        self.dataB = None
29        self.varListA = []
30        self.varListB = []
31        self.varA = None
32        self.varB = None
33        self.lbAttrAItems = []
34        self.lbAttrBItems = []
35
36        # load settings
37        self.loadSettings()
38
39        # GUI
40        w = QWidget(self)
41        self.controlArea.layout().addWidget(w)
42        grid = QGridLayout()
43        grid.setMargin(0)
44        w.setLayout(grid)
45
46        # attribute A
47        boxAttrA = OWGUI.widgetBox(self, 'Attribute A', orientation = "vertical", addToLayout=0)
48        grid.addWidget(boxAttrA, 0,0)
49        self.lbAttrA = OWGUI.listBox(boxAttrA, self, "lbAttrAItems", callback = self.lbAttrAChange)
50
51        # attribute  B
52        boxAttrB = OWGUI.widgetBox(self, 'Attribute B', orientation = "vertical", addToLayout=0)
53        grid.addWidget(boxAttrB, 0,1)
54        self.lbAttrB = OWGUI.listBox(boxAttrB, self, "lbAttrBItems", callback = self.lbAttrBChange)
55
56        # info A
57        boxDataA = OWGUI.widgetBox(self, 'Data A', orientation = "vertical", addToLayout=0)
58        grid.addWidget(boxDataA, 1,0)
59        self.lblDataAExamples = OWGUI.widgetLabel(boxDataA, "num examples")
60        self.lblDataAAttributes = OWGUI.widgetLabel(boxDataA, "num attributes")
61
62        # info B
63        boxDataB = OWGUI.widgetBox(self, 'Data B', orientation = "vertical", addToLayout=0)
64        grid.addWidget(boxDataB, 1,1)
65        self.lblDataBExamples = OWGUI.widgetLabel(boxDataB, "num examples")
66        self.lblDataBAttributes = OWGUI.widgetLabel(boxDataB, "num attributes")
67
68        # icons
69        self.icons = self.createAttributeIconDict()
70
71        # resize
72        self.resize(400,500)
73
74
75    ############################################################################################################################################################
76    ## Data input and output management
77    ############################################################################################################################################################
78
79    def inVarList(self, varList, var):
80        if var in varList:
81            return True, varList.index(var)
82        elif var and var.varType == orange.Variable.String and var.name in [v.name for v in varList]:
83            return True, [v.name for v in varList].index(var.name)
84        elif var and (var.name, var.varType) in [(v.name, v.varType) for v in varList]:
85            return True, [(v.name, v.varType) for v in varList].index((var.name, var.varType))
86        else:
87            return False, -1
88       
89       
90    def onDataAInput(self, data):
91        self.closeContext("A")
92        # set self.dataA, generate new domain if it is the same as of self.dataB.domain
93#        if data and self.dataB and data.domain == self.dataB.domain:
94#            if data.domain.classVar:
95#                classVar = data.domain.classVar.clone()
96#            else:
97#                classVar = None
98#            dc = orange.Domain([x.clone() for x in data.domain], classVar)
99#            for i, a in enumerate(dc):
100#                a.getValueFrom = lambda ex,f,i=i: ex[i]
101##             no need to clone meta attributes: dc.addmetas(dict([(orange.newmetaid(), x.clone()) for x in data.domain.getmetas().values()])); for i,id,a in enumerate(dc.getmetas().items()): ...
102#            dc.addmetas(data.domain.getmetas())
103#            self.dataA = orange.ExampleTable(dc, data)
104#            self.dataA = data
105#        else:
106#            self.dataA = data
107        self.dataA = data
108        # update self.varListA and self.varA
109        if self.dataA:
110            self.varListA = list(self.dataA.domain.variables) + self.dataA.domain.getmetas().values()
111        else:
112            self.varListA = []
113#        if not self.varA in self.varListA:
114#            self.varA = None
115        # update info
116        self.updateInfoA()
117        # update attribute A listbox
118        self.lbAttrA.clear()
119        for var in self.varListA:
120            self.lbAttrA.addItem(QListWidgetItem(self.icons[var.varType], var.name))
121        if self.dataA:
122            self.openContext("A", self.dataA)
123        match, index = self.inVarList(self.varListA, self.varA)
124        if match:
125            self.varA = self.varListA[index]
126            self.lbAttrA.setCurrentItem(self.lbAttrA.item(index))
127    #        elif self.dataA:
128    #            self.varA = None
129        self.sendData()
130
131    def onDataBInput(self, data):
132        # set self.dataB, generate new domain if it is the same as of self.dataA.domain
133        self.closeContext("B")
134#        if data and self.dataA and data.domain == self.dataA.domain:
135#            if data.domain.classVar:
136#                classVar = data.domain.classVar.clone()
137#            else:
138#                classVar = None
139#            dc = orange.Domain([x.clone() for x in data.domain.attributes], classVar)
140#            for i, a in enumerate(dc):
141#                a.getValueFrom = lambda ex,f,i=i: ex[i]
142#            # no need to clone meta attributes: dc.addmetas(dict([(orange.newmetaid(), x.clone()) for x in data.domain.getmetas().values()])); for i,id,a in enumerate(dc.getmetas().items()): ...
143#            dc.addmetas(data.domain.getmetas())
144#            self.dataB = orange.ExampleTable(dc, data)
145#            self.dataB = data
146#        else:
147#            self.dataB = data
148        self.dataB = data
149        # update self.varListB and self.varB
150        if self.dataB:
151            self.varListB = list(self.dataB.domain.variables) + self.dataB.domain.getmetas().values()
152        else:
153            self.varListB = []
154#        if not self.varB in self.varListB:
155#            self.varB = None
156        # update info
157        self.updateInfoB()
158        # update attribute B listbox
159        self.lbAttrB.clear()
160        for var in self.varListB:
161            self.lbAttrB.addItem(QListWidgetItem(self.icons[var.varType], var.name))
162       
163        if self.dataB:
164            self.openContext("B", self.dataB)
165        match, index = self.inVarList(self.varListB, self.varB)
166        if match:
167            self.varB = self.varListB[index]
168            self.lbAttrB.setCurrentItem(self.lbAttrB.item(index))
169#        elif self.dataB:
170#            self.varB = None
171           
172        self.sendData()
173
174
175    def updateInfoA(self):
176        """Updates data A info box.
177        """
178        if self.dataA:
179            self.lblDataAExamples.setText("%s example%s" % self._sp(self.dataA))
180            self.lblDataAAttributes.setText("%s attribute%s" % self._sp(self.varListA))
181        else:
182            self.lblDataAExamples.setText("No data on input A.")
183            self.lblDataAAttributes.setText("")
184
185
186    def updateInfoB(self):
187        """Updates data B info box.
188        """
189        if self.dataB:
190            self.lblDataBExamples.setText("%s example%s" % self._sp(self.dataB))
191            self.lblDataBAttributes.setText("%s attribute%s" % self._sp(self.varListB))
192        else:
193            self.lblDataBExamples.setText("No data on input B.")
194            self.lblDataBAttributes.setText("")
195
196
197#    def sendData(self):
198#        """Sends out data.
199#        """
200#        if self.varA and self.varB and self.dataA and self.dataB:
201#            # create dictionaries: attribute values -> example index
202#            val2idxDictA = {}
203#            for eIdx, e in enumerate(self.dataA):
204#                val2idxDictA[e[self.varA].native()] = eIdx
205#            val2idxDictB = {}
206#            for eIdx, e in enumerate(self.dataB):
207#                val2idxDictB[e[self.varB].native()] = eIdx
208#            # remove DC and DK from dictionaries (change when bug 62 is fixed)
209###            if val2idxDictA.has_key(orange.Value(self.varA.varType, orange.ValueTypes.DC).native()):
210###                val2idxDictA.pop(orange.Value(self.varA.varType, orange.ValueTypes.DC).native())
211###            if val2idxDictA.has_key(orange.Value(self.varA.varType, orange.ValueTypes.DK).native()):
212###                val2idxDictA.pop(orange.Value(self.varA.varType, orange.ValueTypes.DK).native())
213###            if val2idxDictB.has_key(orange.Value(self.varB.varType, orange.ValueTypes.DC).native()):
214###                val2idxDictB.pop(orange.Value(self.varB.varType, orange.ValueTypes.DC).native())
215###            if val2idxDictB.has_key(orange.Value(self.varB.varType, orange.ValueTypes.DK).native()):
216###                val2idxDictB.pop(orange.Value(self.varB.varType, orange.ValueTypes.DK).native())
217#            if val2idxDictA.has_key("?"):
218#                val2idxDictA.pop("?")
219#            if val2idxDictA.has_key("~"):
220#                val2idxDictA.pop("~")
221#            if val2idxDictA.has_key(""):
222#                val2idxDictA.pop("")
223#            if val2idxDictB.has_key("?"):
224#                val2idxDictB.pop("?")
225#            if val2idxDictB.has_key("~"):
226#                val2idxDictB.pop("~")
227#            if val2idxDictB.has_key(""):
228#                val2idxDictB.pop("")
229#            # example table names
230#            nameA = self.dataA.name
231#            if not nameA: nameA = "Examples A"
232#            nameB = self.dataB.name
233#            if not nameB: nameB = "Examples B"
234#            # create example B with all values unknown
235#            exBDK = orange.Example(self.dataB[0])
236#            for var in self.varListB:
237###                exBDK[var] = orange.Value(var.varType, orange.ValueTypes.DK)
238#                exBDK[var] = "?"
239#            # build example table to append to the right of A
240#            vlBreduced = list(self.varListB)
241#            vlBreduced.remove(self.varB)
242#            domBreduced = orange.Domain(vlBreduced, None)
243#            etBreduced = orange.ExampleTable(domBreduced)
244#            for e in self.dataA:
245#                dataBidx = val2idxDictB.get(e[self.varA].native(), None)
246#                if dataBidx <> None:
247#                    etBreduced.append(self.dataB[dataBidx])
248#                else:
249#                    etBreduced.append(orange.Example(domBreduced, exBDK))
250#            etAB = orange.ExampleTable([self.dataA, etBreduced])
251#            etAB.name = "%(nameA)s (merged with %(nameB)s)" % vars()
252#            self.send("Merged Examples A+B", etAB)
253#
254#            # create example A with all values unknown
255#            exADK = orange.Example(self.dataA[0])
256#            for var in self.varListA:
257###                exADK[var] = orange.Value(var.varType, orange.ValueTypes.DK)
258#                exADK[var] = "?"
259#            # build example table to append to the right of B
260#            vlAreduced = list(self.varListA)
261#            vlAreduced.remove(self.varA)
262#            domAreduced = orange.Domain(vlAreduced, None)
263#            etAreduced = orange.ExampleTable(domAreduced)
264#            for e in self.dataB:
265#                dataAidx = val2idxDictA.get(e[self.varB].native(), None)
266#                if dataAidx <> None:
267#                    etAreduced.append(self.dataA[dataAidx])
268#                else:
269#                    etAreduced.append(orange.Example(domAreduced, exADK))
270#            etBA = orange.ExampleTable([self.dataB, etAreduced])
271#            etBA.name = nameB + " (merged with %s)" % nameA
272#            self.send("Merged Examples B+A", etBA)
273#        else:
274#            self.send("Merged Examples A+B", None)
275#            self.send("Merged Examples B+A", None)
276
277    def sendData(self):
278        if self.dataA and self.dataB and self.varA and self.varB:
279            self.send("Merged Examples A+B", self.merge(self.dataA, self.dataB, self.varA, self.varB))
280            self.send("Merged Examples B+A", self.merge(self.dataB, self.dataA, self.varB, self.varA))
281        else:
282            self.send("Merged Examples A+B", None)
283            self.send("Merged Examples B+A", None)
284
285    ############################################################################################################################################################
286    ## Event handlers
287    ############################################################################################################################################################
288
289    def lbAttrAChange(self):
290        if self.dataA:
291            if self.lbAttrA.selectedItems() != []:
292                ind = self.lbAttrA.row(self.lbAttrA.selectedItems()[0])
293                self.varA = self.varListA[ind]
294            else:
295                self.varA = None
296        else:
297            self.varA = None
298        self.sendData()
299
300
301    def lbAttrBChange(self):
302        if self.dataB:
303            if self.lbAttrB.selectedItems() != []:
304                ind = self.lbAttrB.row(self.lbAttrB.selectedItems()[0])
305                self.varB = self.varListB[ind]
306            else:
307                self.varB = None
308        else:
309            self.varB = None
310        self.sendData()
311
312
313    ############################################################################################################################################################
314    ## Utility functions
315    ############################################################################################################################################################
316
317    def _sp(self, l, capitalize=True):
318        """Input: list; returns tupple (str(len(l)), "s"/"")
319        """
320        n = len(l)
321        if n == 0:
322            if capitalize:
323                return "No", "s"
324            else:
325                return "no", "s"
326        elif n == 1:
327            return str(n), ''
328        else:
329            return str(n), 's'
330
331    def merge(self, dataA, dataB, varA, varB):
332        """ Merge two tables
333        """
334       
335        val2idx = dict([(e[varB].native(), i) for i, e in reversed(list(enumerate(dataB)))])
336       
337        for key in ["?", "~", ""]:
338            if key in val2idx:
339                val2idx.pop(key)
340                 
341        metasA = dataA.domain.getmetas().items()
342        metasB = dataB.domain.getmetas().items()
343       
344        includedAttsB = [attrB for attrB in dataB.domain if attrB not in dataA.domain]
345        includedMetaB = [(mid, meta) for mid, meta in metasB if (mid, meta) not in metasA]
346        includedClassVarB = dataB.domain.classVar and dataB.domain.classVar not in dataA.domain
347       
348        reducedDomainB = orange.Domain(includedAttsB, includedClassVarB)
349        reducedDomainB.addmetas(dict(includedMetaB))
350       
351       
352        mergingB = orange.ExampleTable(reducedDomainB)
353       
354        for ex in dataA:
355            ind = val2idx.get(ex[varA].native(), None)
356            if ind is not None:
357                mergingB.append(orange.Example(reducedDomainB, dataB[ind]))
358               
359            else:
360                mergingB.append(orange.Example(reducedDomainB, ["?"] * len(reducedDomainB)))
361               
362        return orange.ExampleTable([dataA, mergingB])
363
364   
365#    def setVarA(self, var):
366#        if var in self.varListA:
367#            ind = self.varListA.index(var)
368#            self.lbAttrA.setCurrentItem(self.lbAttrA.item(ind))
369#       
370#    def varA(self):
371#        selectedItems = self.lbAttrB.selectedItems()
372#        if selectedItems:
373#            ind = self.lbAttrB.row(selectedItems[0])
374#            return self.varListA[ind]
375#        else:
376#            return None
377#    varA = property(varA, setVarA)
378#   
379#    def setVarB(self, var):
380#        if var in self.varListB:
381#            ind = self.varListB.index(var)
382#            self.lbAttrB.setCurrentItem(self.lbAttrB.item(ind))
383#           
384#    def varB(self):
385#        selectedItems = self.lbAttrB.selectedItems()
386#        if selectedItems:
387#            ind = self.lbAttrB.row(selectedItems[0])
388#            return self.varListB[ind]
389#        else:
390#            return None
391#   
392#    varB = property(varB, setVarB)
393   
394if __name__=="__main__":
395    """
396    import sys
397    import OWDataTable, orngSignalManager
398    signalManager = orngSignalManager.SignalManager(0)
399    #data = orange.ExampleTable('dicty_800_genes_from_table07.tab')
400##    data = orange.ExampleTable(r'..\..\doc\datasets\adult_sample.tab')
401##    dataA = orange.ExampleTable(r'c:\Documents and Settings\peterjuv\My Documents\STEROLTALK\Sterolgene v.0 mouse\sterolgene v.0 mouse probeRatios.tab')
402##    dataA = orange.ExampleTable(r'c:\Documents and Settings\peterjuv\My Documents\STEROLTALK\Sterolgene v.0 mouse\Copy of sterolgene v.0 mouse probeRatios.tab')
403##    dataB = orange.ExampleTable(r'c:\Documents and Settings\peterjuv\My Documents\STEROLTALK\Sterolgene v.0 mouse\sterolgene v.0 mouse probeRatios.tab')
404    dataA = orange.ExampleTable(r'c:\Documents and Settings\peterjuv\My Documents\et1.tab')
405    dataB = orange.ExampleTable(r'c:\Documents and Settings\peterjuv\My Documents\et2.tab')
406    a=QApplication(sys.argv)
407    ow=OWMergeData()
408    a.setMainWidget(ow)
409    ow.show()
410    ow.onDataAInput(dataA)
411    ow.onDataBInput(dataB)
412    # data table
413    dt = OWDataTable.OWDataTable(signalManager = signalManager)
414    signalManager.addWidget(ow)
415    signalManager.addWidget(dt)
416    signalManager.setFreeze(1)
417    signalManager.addLink(ow, dt, 'Merged Examples A+B', 'Examples', 1)
418    signalManager.addLink(ow, dt, 'Merged Examples B+A', 'Examples', 1)
419    signalManager.setFreeze(0)
420    dt.show()
421    a.exec_()
422    """
423    import sys
424    a=QApplication(sys.argv)
425    ow=OWMergeData()
426    ow.show()
427    data = orange.ExampleTable(r"E:\Development\Orange Datasets\UCI\iris.tab")
428    ow.onDataAInput(data)
429    a.exec_()
Note: See TracBrowser for help on using the repository browser.