source: orange/orange/OrangeWidgets/Unsupervised/OWAttributeDistance.py @ 8042:ffcb93bc9028

Revision 8042:ffcb93bc9028, 6.3 KB checked in by markotoplak, 3 years ago (diff)

Hierarchical clustering: also catch RuntimeError when importing matplotlib (or the documentation could not be built on server).

Line 
1"""
2<name>Attribute Distance</name>
3<description>Computes attribute distance for given data set.</description>
4<icon>icons/AttributeDistance.png</icon>
5<contact>Blaz Zupan (blaz.zupan(@at@)fri.uni-lj.si)</contact>
6<priority>1400</priority>
7"""
8import orange, math
9import OWGUI
10from OWWidget import *
11import random
12import orngInteract
13import warnings
14warnings.filterwarnings("ignore", module="orngInteract")
15
16##############################################################################
17# main class
18
19class OWAttributeDistance(OWWidget):
20    settingsList = ["classInteractions"]
21
22    discMeasures = ("Pearson's chi-square", "2-way interactions - I(A;B)/H(A,B)", "3-way interactions - I(A;B;C)")
23    contMeasures = ("Pearson's correlation", "Spearman's correlation")
24    def __init__(self, parent=None, signalManager = None, name='AttributeDistance'):
25        self.callbackDeposit = [] # deposit for OWGUI callback functions
26        OWWidget.__init__(self, parent, signalManager, name, wantMainArea = 0, resizingEnabled = 0)
27
28        self.inputs = [("Examples", ExampleTable, self.dataset)]
29        self.outputs = [("Distance Matrix", orange.SymMatrix)]
30
31        self.data = None
32
33        self.classInteractions = 0
34        self.loadSettings()
35        rb = OWGUI.radioButtonsInBox(self.controlArea, self, "classInteractions", [], "Distance", callback=self.toggleClass)
36        OWGUI.widgetLabel(rb, "Measures on discrete attributes\n   (continuous attributes are discretized into five intervals)")
37        for b in self.discMeasures:
38            OWGUI.appendRadioButton(rb, self, "classInteractions", b)
39       
40        OWGUI.widgetLabel(rb, "\n"+"Measures on continuous attributes\n   (discrete attributes are treated as ordinal)")
41        for b in self.contMeasures:
42            OWGUI.appendRadioButton(rb, self, "classInteractions", b)
43           
44        OWGUI.rubber(self.controlArea)
45        self.resize(215,50)
46#        self.adjustSize()
47
48    def sendReport(self):
49        self.reportSettings("Settings", [("Distance measure", (self.discMeasures+self.contMeasures)[self.classInteractions])])
50        self.reportData(self.data)
51
52    def computeMatrix(self):
53        self.error(0)
54        if self.data:
55            atts = self.data.domain.attributes
56            if len(atts) < 2:
57                self.error(0, "Dataset must contain at least two attributes")
58                return None
59            matrix = orange.SymMatrix(len(atts))
60            matrix.setattr('items', atts)
61            if self.classInteractions < 3:
62                if self.data.domain.hasContinuousAttributes():
63                    if self.discretizedData is None:
64                        try:
65                            self.discretizedData = orange.Preprocessor_discretize(self.data, method=orange.EquiNDiscretization(numberOfIntervals=4))
66                        except orange.KernelException, ex:
67                            self.error(0, "An error ocured during data discretization: %s" % ex.message)
68                            return None
69                    data = self.discretizedData
70                else:
71                    data = self.data
72
73                # This is ugly, but: Aleks' code which computes Chi2 requires the class attribute because it prepares
74                # some common stuff for all measures. If we want to use his code, we need the class variable, so we
75                # prepare a fake one
76                if not data.domain.classVar:
77                    if self.classInteractions == 0:
78                        classedDomain = orange.Domain(data.domain.attributes, orange.EnumVariable("foo", values=["0", "1"]))
79                        data = orange.ExampleTable(classedDomain, data)
80                    else:
81                        self.error(0, "The selected distance measure requires a data set with a class attribute")
82                        return None
83
84                im = orngInteract.InteractionMatrix(data, dependencies_too=1)
85                off = 1
86                if self.classInteractions == 0:
87                    diss,labels = im.exportChi2Matrix()
88                    off = 0
89                elif self.classInteractions == 1:
90                    (diss,labels) = im.depExportDissimilarityMatrix(jaccard=1)  # 2-interactions
91                else:
92                    (diss,labels) = im.exportDissimilarityMatrix(jaccard=1)  # 3-interactions
93
94                for i in range(len(atts)-off):
95                    for j in range(i+1):
96                        matrix[i+off, j] = diss[i][j]
97
98            else:
99                if self.classInteractions == 3:
100                    for a1 in range(len(atts)):
101                        for a2 in range(a1):
102                            matrix[a1, a2] = (1.0 - orange.PearsonCorrelation(a1, a2, self.data, 0).r) / 2.0
103                else:
104                    if len(self.data) < 3:
105                        self.error(0, "The selected distance measure requires a data set with at least 3 instances")
106                        return None
107                    import numpy, statc
108                    m = self.data.toNumpyMA("A")[0]
109                    averages = numpy.ma.average(m, axis=0)
110                    filleds = [list(numpy.ma.filled(m[:,i], averages[i])) for i in range(len(atts))]
111                    for a1, f1 in enumerate(filleds):
112                        for a2 in range(a1):
113                            matrix[a1, a2] = (1.0 - statc.spearmanr(f1, filleds[a2])[0]) / 2.0
114               
115            return matrix
116        else:
117            return None
118
119    def toggleClass(self):
120        self.sendData()
121
122    def dataset(self, data):
123        self.data = data
124        self.discretizedData = None
125        self.sendData()
126
127    def sendData(self):
128        if self.data:
129            matrix = self.computeMatrix()
130        else:
131            matrix = None
132        self.send("Distance Matrix", matrix)
133
134
135##################################################################################################
136# test script
137
138if __name__=="__main__":
139    import os
140    if os.path.isfile(r'../../doc/datasets/voting.tab'):
141        data = orange.ExampleTable(r'../../doc/datasets/voting')
142    else:
143        #data = orange.ExampleTable('voting')
144        data = orange.ExampleTable(r"E:\Development\Orange Datasets\UCI\iris.tab")
145    a = QApplication(sys.argv)
146    ow = OWAttributeDistance()
147    ow.show()
148    ow.dataset(data)
149    a.exec_()
150    ow.saveSettings()
Note: See TracBrowser for help on using the repository browser.