source: orange/orange/OrangeWidgets/Data/OWMergeData.py @ 7729:35ce4e062df4

Revision 7729:35ce4e062df4, 10.9 KB checked in by ales_erjavec <ales.erjavec@…>, 3 years ago (diff)

Fixed a bug with context settings (would save a pickled instance of orange.Variable, and then crash when loading because the pickle interface changed). This fix sets a new context version so all old settings will be discarded.

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    contextHandlers = {"A": DomainContextHandler("A", [ContextField("varA")], syncWithGlobal=False, contextDataVersion=2),
15                       "B": DomainContextHandler("B", [ContextField("varB")], syncWithGlobal=False, contextDataVersion=2)}                                           
16
17    def __init__(self, parent = None, signalManager = None, name = "Merge data"):
18        OWWidget.__init__(self, parent, signalManager, name, wantMainArea = 0)  #initialize base class
19
20        # set channels
21        self.inputs = [("Examples A", ExampleTable, self.onDataAInput), ("Examples B", ExampleTable, self.onDataBInput)]
22        self.outputs = [("Merged Examples A+B", ExampleTable), ("Merged Examples B+A", ExampleTable)]
23
24        # data
25        self.dataA = None
26        self.dataB = None
27        self.varListA = []
28        self.varListB = []
29        self.varA = None
30        self.varB = None
31        self.lbAttrAItems = []
32        self.lbAttrBItems = []
33
34        # load settings
35        self.loadSettings()
36
37        # GUI
38        w = QWidget(self)
39        self.controlArea.layout().addWidget(w)
40        grid = QGridLayout()
41        grid.setMargin(0)
42        w.setLayout(grid)
43
44        # attribute A
45        boxAttrA = OWGUI.widgetBox(self, 'Attribute A', orientation = "vertical", addToLayout=0)
46        grid.addWidget(boxAttrA, 0,0)
47        self.lbAttrA = OWGUI.listBox(boxAttrA, self, "lbAttrAItems", callback = self.lbAttrAChange)
48
49        # attribute  B
50        boxAttrB = OWGUI.widgetBox(self, 'Attribute B', orientation = "vertical", addToLayout=0)
51        grid.addWidget(boxAttrB, 0,1)
52        self.lbAttrB = OWGUI.listBox(boxAttrB, self, "lbAttrBItems", callback = self.lbAttrBChange)
53
54        # info A
55        boxDataA = OWGUI.widgetBox(self, 'Data A', orientation = "vertical", addToLayout=0)
56        grid.addWidget(boxDataA, 1,0)
57        self.lblDataAExamples = OWGUI.widgetLabel(boxDataA, "num examples")
58        self.lblDataAAttributes = OWGUI.widgetLabel(boxDataA, "num attributes")
59
60        # info B
61        boxDataB = OWGUI.widgetBox(self, 'Data B', orientation = "vertical", addToLayout=0)
62        grid.addWidget(boxDataB, 1,1)
63        self.lblDataBExamples = OWGUI.widgetLabel(boxDataB, "num examples")
64        self.lblDataBAttributes = OWGUI.widgetLabel(boxDataB, "num attributes")
65
66        # icons
67        self.icons = self.createAttributeIconDict()
68
69        # resize
70        self.resize(400,500)
71
72
73    ############################################################################################################################################################
74    ## Data input and output management
75    ############################################################################################################################################################
76       
77    def inVarList(self, varList, var):
78        varList = [(v.name, v.varType) for v in varList]
79        if var in varList:
80            return True, varList.index(var)
81        else:
82            return False, -1
83       
84    def onDataAInput(self, data):
85        self.closeContext("A")
86        self.dataA = data
87        # update self.varListA and self.varA
88        if self.dataA:
89            self.varListA = list(self.dataA.domain.variables) + self.dataA.domain.getmetas().values()
90        else:
91            self.varListA = []
92           
93        # update info
94        self.updateInfoA()
95        # update attribute A listbox
96        self.lbAttrA.clear()
97        for var in self.varListA:
98            self.lbAttrA.addItem(QListWidgetItem(self.icons[var.varType], var.name))
99        if self.dataA:
100            self.openContext("A", self.dataA)
101        match, index = self.inVarList(self.varListA, self.varA)
102        if match:
103            var = self.varListA[index]
104            self.varA = (var.name, var.varType)
105            self.lbAttrA.setCurrentItem(self.lbAttrA.item(index))
106           
107        self.sendData()
108
109    def onDataBInput(self, data):
110        self.closeContext("B")
111        self.dataB = data
112        # update self.varListB and self.varB
113        if self.dataB:
114            self.varListB = list(self.dataB.domain.variables) + self.dataB.domain.getmetas().values()
115        else:
116            self.varListB = []
117       
118        # update info
119        self.updateInfoB()
120        # update attribute B listbox
121        self.lbAttrB.clear()
122        for var in self.varListB:
123            self.lbAttrB.addItem(QListWidgetItem(self.icons[var.varType], var.name))
124       
125        if self.dataB:
126            self.openContext("B", self.dataB)
127        match, index = self.inVarList(self.varListB, self.varB)
128        if match:
129            var = self.varListB[index]
130            self.varB = (var.name, var.varType)
131            self.lbAttrB.setCurrentItem(self.lbAttrB.item(index))
132           
133        self.sendData()
134
135
136    def updateInfoA(self):
137        """Updates data A info box.
138        """
139        if self.dataA:
140            self.lblDataAExamples.setText("%s example%s" % self._sp(self.dataA))
141            self.lblDataAAttributes.setText("%s attribute%s" % self._sp(self.varListA))
142        else:
143            self.lblDataAExamples.setText("No data on input A.")
144            self.lblDataAAttributes.setText("")
145
146
147    def updateInfoB(self):
148        """Updates data B info box.
149        """
150        if self.dataB:
151            self.lblDataBExamples.setText("%s example%s" % self._sp(self.dataB))
152            self.lblDataBAttributes.setText("%s attribute%s" % self._sp(self.varListB))
153        else:
154            self.lblDataBExamples.setText("No data on input B.")
155            self.lblDataBAttributes.setText("")
156
157    def sendData(self):
158        if self.dataA and self.dataB and self.varA and self.varB:
159            self.send("Merged Examples A+B", self.merge(self.dataA, self.dataB, self.varA[0], self.varB[0]))
160            self.send("Merged Examples B+A", self.merge(self.dataB, self.dataA, self.varB[0], self.varA[0]))
161        else:
162            self.send("Merged Examples A+B", None)
163            self.send("Merged Examples B+A", None)
164
165    ############################################################################################################################################################
166    ## Event handlers
167    ############################################################################################################################################################
168
169    def lbAttrAChange(self):
170        if self.dataA:
171            if self.lbAttrA.selectedItems() != []:
172                ind = self.lbAttrA.row(self.lbAttrA.selectedItems()[0])
173                var = self.varListA[ind]
174                self.varA = (var.name, var.varType)
175            else:
176                self.varA = None
177        else:
178            self.varA = None
179        self.sendData()
180
181
182    def lbAttrBChange(self):
183        if self.dataB:
184            if self.lbAttrB.selectedItems() != []:
185                ind = self.lbAttrB.row(self.lbAttrB.selectedItems()[0])
186                var = self.varListB[ind]
187                self.varB = (var.name, var.varType)
188            else:
189                self.varB = None
190        else:
191            self.varB = None
192        self.sendData()
193
194
195    ############################################################################################################################################################
196    ## Utility functions
197    ############################################################################################################################################################
198
199    def _sp(self, l, capitalize=True):
200        """Input: list; returns tuple (str(len(l)), "s"/"")
201        """
202        n = len(l)
203        if n == 0:
204            if capitalize:
205                return "No", "s"
206            else:
207                return "no", "s"
208        elif n == 1:
209            return str(n), ''
210        else:
211            return str(n), 's'
212
213    def merge(self, dataA, dataB, varA, varB):
214        """ Merge two tables
215        """
216       
217        val2idx = dict([(e[varB].native(), i) for i, e in reversed(list(enumerate(dataB)))])
218       
219        for key in ["?", "~", ""]:
220            if key in val2idx:
221                val2idx.pop(key)
222                 
223        metasA = dataA.domain.getmetas().items()
224        metasB = dataB.domain.getmetas().items()
225       
226        includedAttsB = [attrB for attrB in dataB.domain if attrB not in dataA.domain]
227        includedMetaB = [(mid, meta) for mid, meta in metasB if (mid, meta) not in metasA]
228        includedClassVarB = dataB.domain.classVar and dataB.domain.classVar not in dataA.domain
229       
230        reducedDomainB = orange.Domain(includedAttsB, includedClassVarB)
231        reducedDomainB.addmetas(dict(includedMetaB))
232       
233       
234        mergingB = orange.ExampleTable(reducedDomainB)
235       
236        for ex in dataA:
237            ind = val2idx.get(ex[varA].native(), None)
238            if ind is not None:
239                mergingB.append(orange.Example(reducedDomainB, dataB[ind]))
240               
241            else:
242                mergingB.append(orange.Example(reducedDomainB, ["?"] * len(reducedDomainB)))
243               
244        return orange.ExampleTable([dataA, mergingB])
245   
246if __name__=="__main__":
247    """
248    import sys
249    import OWDataTable, orngSignalManager
250    signalManager = orngSignalManager.SignalManager(0)
251    #data = orange.ExampleTable('dicty_800_genes_from_table07.tab')
252##    data = orange.ExampleTable(r'..\..\doc\datasets\adult_sample.tab')
253##    dataA = orange.ExampleTable(r'c:\Documents and Settings\peterjuv\My Documents\STEROLTALK\Sterolgene v.0 mouse\sterolgene v.0 mouse probeRatios.tab')
254##    dataA = orange.ExampleTable(r'c:\Documents and Settings\peterjuv\My Documents\STEROLTALK\Sterolgene v.0 mouse\Copy of sterolgene v.0 mouse probeRatios.tab')
255##    dataB = orange.ExampleTable(r'c:\Documents and Settings\peterjuv\My Documents\STEROLTALK\Sterolgene v.0 mouse\sterolgene v.0 mouse probeRatios.tab')
256    dataA = orange.ExampleTable(r'c:\Documents and Settings\peterjuv\My Documents\et1.tab')
257    dataB = orange.ExampleTable(r'c:\Documents and Settings\peterjuv\My Documents\et2.tab')
258    a=QApplication(sys.argv)
259    ow=OWMergeData()
260    a.setMainWidget(ow)
261    ow.show()
262    ow.onDataAInput(dataA)
263    ow.onDataBInput(dataB)
264    # data table
265    dt = OWDataTable.OWDataTable(signalManager = signalManager)
266    signalManager.addWidget(ow)
267    signalManager.addWidget(dt)
268    signalManager.setFreeze(1)
269    signalManager.addLink(ow, dt, 'Merged Examples A+B', 'Examples', 1)
270    signalManager.addLink(ow, dt, 'Merged Examples B+A', 'Examples', 1)
271    signalManager.setFreeze(0)
272    dt.show()
273    a.exec_()
274    """
275    import sys
276    a=QApplication(sys.argv)
277    ow=OWMergeData()
278    ow.show()
279    data = orange.ExampleTable(r"E:\Development\Orange Datasets\UCI\iris.tab")
280    ow.onDataAInput(data)
281    a.exec_()
Note: See TracBrowser for help on using the repository browser.