source: orange/orange/OrangeWidgets/Unsupervised/OWSOMVisualizer.py @ 9546:2b6cc6f397fe

Revision 9546:2b6cc6f397fe, 51.1 KB checked in by ales_erjavec <ales.erjavec@…>, 2 years ago (diff)

Renamed widget channel names in line with the new naming rules/convention.
Added backwards compatibility in orngDoc loadDocument to enable loading of schemas saved before the change.

Line 
1"""
2<name>SOM Visualizer</name>
3<description>Visualizes a trained self organising maps.</description>
4<icon>icons/SOMVisualizer.png</icon>
5<contact>Ales Erjavec (ales.erjavec(@at@)fri.uni-lj.si)</contact> 
6<priority>5020</priority>
7"""
8import orange, orngSOM
9import math, numpy
10import OWGUI, OWColorPalette
11from OWWidget import *
12
13from OWDlgs import OWChooseImageSizeDlg
14
15DefColor=QColor(200, 200, 0)
16BaseColor=QColor(0, 0, 200)
17
18from orngMisc import ColorPalette
19from functools import partial
20
21class ColorPaletteBW(ColorPalette):
22    def __init__(self, *args, **kwargs):
23        ColorPalette.__init__(self, [(0, 0, 0,), (255, 255, 255)], *args, **kwargs)
24       
25class ColorPaletteHSV(ColorPalette):
26    def __init__(self, num_of_colors):
27        ColorPalette.__init__(self, OWColorPalette.defaultRGBColors)
28        self.num_of_colors = num_of_colors
29       
30    def get_rgb(self, value, gamma=None):
31        if isinstance(value, int):
32            return self.colors[value]
33        else:
34            raise ValueError("int expected for discrete color palette")
35       
36class ColorPaletteGR(ColorPalette):
37    def __init__(self, *args, **kwargs):
38        ColorPalette.__init__(self, [(0, 255, 0), (255, 0, 0)], *args, **kwargs)
39       
40class GraphicsSOMItem(QGraphicsPolygonItem):
41    startAngle=0
42    segment=6
43    def __init__(self, *args):
44        QGraphicsPolygonItem.__init__(self, *args)
45        self.node=None
46        self.labelText=""
47        self.textColor=Qt.black
48        self.defaultPen = QPen(Qt.black)
49        self.outlinePoints=None
50        self.histogramConstructor = None
51        self.histogramItem = None
52        self.setSize(10)
53        self.setZValue(0)
54
55    def areaPoints(self):
56        return self.outlinePoints
57   
58    def setSize(self, size):
59        self.outlinePoints = [QPointF(math.cos(2*math.pi/self.segments*i + self.startAngle)*size,
60                                      math.sin(2*math.pi/self.segments*i + self.startAngle)*size)
61                              for i in range(self.segments)]
62        self.setPolygon(QPolygonF(self.outlinePoints))
63
64    def setColor(self, color):
65        self.color = color
66        if color.value() < 100:
67            self.textColor = Qt.white
68        self.setBrush(QBrush(color))
69
70    def setNode(self, node):
71        self.node = node
72        self.hasNode = True
73        self.setFlags(QGraphicsItem.ItemIsSelectable if node else 0)
74        self.updateToolTips()
75
76    def advancement(self):
77        pass
78
79    def updateToolTips(self, showToolTips=True, includeCodebook=True):
80        if self.node and showToolTips:
81            node = self.node
82            text = "Items: %i" % len(self.node.examples)
83            if includeCodebook:
84                text += "<hr><b>Codebook vector:</b><br>" + "<br>".join(\
85                    [a.variable.name + ": " + str(a) for a in node.referenceExample \
86                     if a.variable != node.referenceExample.domain.classVar])
87           
88            if node.examples.domain.classVar and len(node.examples):
89                dist = orange.Distribution(node.examples.domain.classVar, node.examples)
90                if node.examples.domain.classVar.varType == orange.VarTypes.Continuous:
91                    text += "<hr>Avg " + node.examples.domain.classVar.name + ":" + ("%.3f" % dist.average())
92                else:
93                    colors = OWColorPalette.ColorPaletteHSV(len(node.examples.domain.classVar.values))
94                    text += "<hr>" + "<br>".join(["<span style=\"color:%s\">%s</span>" %(colors[i].name(), str(value) + ": " + str(dist[i])) \
95                                                 for i, value in enumerate(node.examples.domain.classVar.values)])
96            self.setToolTip(text)
97        else:
98            self.setToolTip("")
99       
100    def setComponentPlane(self, component):
101        colorSchema = self.colorSchema(component)
102        if component is not None:
103            val = self.node.referenceExample[component]
104            color = colorSchema(val)
105        else:
106            color = QColor(Qt.white)
107        self.setBrush(QBrush(color))
108       
109    @property
110    def colorSchema(self):
111        return self.parentItem().colorSchema
112   
113    @property
114    def histogramColorSchema(self):
115        return self.parentItem().histogramColorSchema
116   
117    def setHistogramConstructor(self, constructor):
118        if getattr(self, "histogramItem", None) is not None:
119            self.scene().removeItem(self.histogramItem)
120            self.histogramItem = None
121        self.histogramConstructor = constructor
122        self.updateHistogram()
123       
124    def updateHistogram(self):
125        if self.histogramItem is not None:
126            self.scene().removeItem(self.histogramItem)
127            self.histogramItem = None
128        if self.histogramConstructor and self.node and self.node.mappedExamples:
129            self.histogramItem = self.histogramConstructor(self.node.mappedExamples, self)
130            self.histogramItem.setParentItem(self)
131            # center the item
132            rect = self.histogramItem.boundingRect()
133           
134            self.histogramItem.setPos(-rect.center())
135           
136    def paint(self, painter, option, widget=0):
137        painter.setBrush(self.brush())
138        if self.isSelected():
139            painter.setPen(QPen(Qt.red, 2))
140        else:
141            painter.setPen(self.pen())
142        painter.drawPolygon(self.polygon())
143       
144    def itemChange(self, change, value):
145        if change == QGraphicsItem.ItemSelectedHasChanged:
146            self.setZValue(self.zValue() + (1 if value.toPyObject() else -1))
147        return QGraphicsPolygonItem.itemChange(self, change, value)
148                       
149class GraphicsSOMHexagon(GraphicsSOMItem):
150    startAngle = 0
151    segments = 6
152    def advancement(self):
153        width = self.outlinePoints[0].x() - self.outlinePoints[3].x()
154        line = self.outlinePoints[1].x() - self.outlinePoints[2].x()
155        x = width - (width-line)/2
156        y = self.outlinePoints[2].y() - self.outlinePoints[5].y()
157        return (x, y)
158
159class GraphicsSOMRectangle(GraphicsSOMItem):
160    startAngle = math.pi/4
161    segments = 4
162    def advancement(self):
163        x = self.outlinePoints[0].x() - self.outlinePoints[1].x()
164        y = self.outlinePoints[0].y() - self.outlinePoints[3].y()
165        return (x,y)
166   
167class PieChart(QGraphicsEllipseItem):
168    def __init__(self, attr, examples, *args, **kwargs):
169        QGraphicsEllipseItem.__init__(self, *args)
170        self.distribution = orange.Distribution(attr, examples)
171        self.setRect(0.0, 0.0, 20.0, 20.0)
172       
173    def paint(self, painter, option, widget=0):
174        start, stop = self.startAngle(), self.startAngle() + self.spanAngle()
175        span = stop - start
176        distsum = sum(self.distribution)
177        angle = start
178        dspan = span / distsum
179        colorSchema = self.colorSchema
180        for i, count in enumerate(self.distribution): 
181            color = colorSchema(i)
182            painter.setBrush(QBrush(color))
183            arcSpan = count * dspan
184            painter.drawPie(self.rect(), angle, arcSpan)
185            angle = angle + arcSpan
186           
187    @property
188    def colorSchema(self):
189        try:
190            self.no_attr
191            return self.parentItem().histogramColorSchema
192        except AttributeError:
193            return lambda val: OWColorPalette.ColorPaletteHSV(len(self.distribution))[val]
194       
195    @classmethod
196    def legendItemConstructor(cls, attr, examples, *args, **kwargs):
197        return DiscreteLegendItem(attr, examples, *args, **kwargs)
198       
199class MajorityChart(PieChart):
200    def __init__(self, *args, **kwargs):
201        PieChart.__init__(self, *args, **kwargs)
202        self.majorityValue = self.distribution.modus()
203        index = int(numpy.argmax(list(self.distribution)))
204        self.setBrush(QBrush(self.colorSchema(index)))
205       
206    def paint(self, painter, option, widget=0):
207        QGraphicsEllipseItem.paint(self, painter, option, widget)
208       
209class MajorityProbChart(MajorityChart):
210    def __init__(self, *args, **kwargs):
211        MajorityChart.__init__(self, *args, **kwargs)
212        index = int(numpy.argmax(list(self.distribution)))
213        val = self.distribution[self.majorityValue]
214        prob = float(val) / (sum(self.distribution) or 1)
215#        colorSchema = self.colorSchema(self.distribution.variable)
216        color = self.colorSchema(index)
217        color = color.lighter(200 - 100 * prob)
218        self.setBrush(QColor(color))
219       
220class ContChart(QGraphicsEllipseItem):
221    def __init__(self, attr, examples, *args, **kwargs):
222        QGraphicsEllipseItem.__init__(self, *args)
223        self.distribution = orange.Distribution(attr, examples)
224        self.setRect(0, 0, 20, 20)
225        self.setBrush(QBrush(DefColor))
226       
227    @property
228    def colorSchema(self):
229        try:
230            return self.parentItem().histogramColorSchema
231        except AttributeError, ex:
232            raise
233   
234    @classmethod
235    def legendItemConstructor(cls, attr, examples, *args, **kwargs):
236        return LegendItem(attr, examples, *args, **kwargs)
237       
238class AverageValue(ContChart):
239    def __init__(self, *args, **kwargs):
240        ContChart.__init__(self, *args, **kwargs)
241        try:
242            self.average = self.distribution.average()
243        except orange.KernelException:
244            self.average = None
245        colorSchema = self.colorSchema(self.distribution.variable)
246        color = lambda col: col if isinstance(col, QColor) else QColor(*col)
247        color = color(colorSchema(self.average)) if self.average is not None else Qt.white
248        self.setBrush(QBrush(color))
249       
250    @classmethod
251    def legendItemConstructor(cls, attr, examples, *args, **kwargs):
252        item = LegendItem(attr, examples, *args, **kwargs)
253        if examples:
254            dist = orange.Distribution(attr, examples)
255            minval, maxval = min(dist.keys()), max(dist.keys())
256            item.setScale((minval, maxval))
257        return item
258   
259class SOMMapItem(QAbstractGraphicsShapeItem):
260    """ A SOM graphics object
261    """
262    objSize = 15
263    def __init__(self, SOMMap=None, w=None, h=None, parent=None, scene=None):
264        QAbstractGraphicsShapeItem.__init__(self, parent, None)
265        self.histogramData = None
266        if map is not None:
267            self.setSOMMap(SOMMap)
268           
269        if scene is not None:
270            scene.addItem(self)
271           
272    def setSOMMap(self, map):
273        if map is not None:
274            if map.topology == orngSOM.HexagonalTopology:
275                self._setHexagonalMap(map)
276            elif map.topology == orngSOM.RectangularTopology:
277                self._setRectangularMap(map)
278        else:
279            self.map = None
280        self.prepareGeometryChange()
281       
282    def _setHexagonalMap(self, map):
283        self.map = map
284        xdim, ydim = map.map_shape
285        size = 2*self.objSize - 1
286        x, y = size, size*2
287        for n in self.map.map:
288            offset = 1 - abs(int(n.pos[0])%2 - 2)
289            h=GraphicsSOMHexagon(self)
290            h.setSize(size)
291            h.setNode(n)
292            (xa, ya) = h.advancement()
293            h.setPos(x + n.pos[0]*xa, y + n.pos[1]*ya + offset*ya/2)
294            h.show()
295       
296    def _setRectangularMap(self, map):
297        self.map = map
298        size=self.objSize*2-1
299        x,y=size, size
300        for n in self.map.map:
301            r=GraphicsSOMRectangle(self)
302            r.setSize(size)
303            r.setNode(n)
304            (xa,ya) = r.advancement()
305            r.setPos(x + n.pos[0]*xa, y + n.pos[1]*ya)
306            r.show()
307           
308    def boundingRect(self):
309        return self.childrenBoundingRect()
310       
311    def paint(self, painter, options, widget=0):
312        pass
313   
314    def setComponentPlane(self, component):
315        for node in self.nodes():
316            node.setComponentPlane(component)
317           
318    def setHistogram(self, attr):
319        for node in self.nodes():
320            node.setHistogram(attr)
321       
322    def nodes(self):
323        for item in self.childItems():
324            if isinstance(item, GraphicsSOMItem):
325                yield item
326               
327    def setColorSchema(self, schema):
328        self._colorSchema = schema
329       
330    @property
331    def colorSchema(self):
332        """ Color schema for component planes
333        """
334        if hasattr(self, "_colorSchema"):
335            return self._colorSchema
336        else:
337            def colorSchema(attr):
338                if attr is None:
339                    return lambda val: QColor(Qt.white)
340                elif type(attr) == int:
341                    attr = self.map.examples.domain[attr]
342                if attr.varType == orange.VarTypes.Discrete:
343                    index = self.map.examples.domain.index(attr)
344                    vals = [n.vector[index] for n in self.map.map]
345                    minval, maxval = min(vals), max(vals)
346                    return lambda val: OWColorPalette.ColorPaletteBW()[min(max(1 - (val - minval) / (maxval - minval or 1), 0.0), 1.0)]
347                else:
348                    index = self.map.examples.domain.index(attr)
349                    vals = [n.vector[index] for n in self.map.map]
350                    minval, maxval = min(vals), max(vals)
351                    return lambda val: OWColorPalette.ColorPaletteBW()[ 1 - (val - minval) / (maxval - minval or 1)] 
352            return colorSchema
353       
354    def setHistogramColorSchema(self, schema):
355        self._histogramColorSchema = schema
356       
357    @property
358    def histogramColorSchema(self):
359        """ Color schema for histograms
360        """
361        if hasattr(self, "_histogramColorSchema"):
362            def colorSchema(attr):
363                if attr is None:
364                    return lambda val: QColor(Qt.white)
365                elif type(attr) == int:
366                    attr = self.map.examples.domain[attr]
367                   
368                if attr.varType == orange.VarTypes.Discrete:
369                    return schema
370                else:
371                    index = self.map.examples.domain.index(attr)
372                    arr, c, w = self.histogramData.toNumpyMA()
373                    if index == arr.shape[1]:
374                        vals = c
375                    else:
376                        vals = arr[:,index]
377                    minval, maxval = numpy.min(vals), numpy.max(vals)
378                    def f(val):
379                        return self._histogramColorSchema((val - minval) / (maxval - minval or 1))
380                    return f
381            return colorSchema
382        else:
383            def colorSchema(attr):
384                if attr is None:
385                    return lambda val: QColor(Qt.white)
386                elif type(attr) == int:
387                    attr = self.map.examples.domain[attr]
388               
389                if attr.varType == orange.VarTypes.Discrete:
390                    return lambda val: OWColorPalette.ColorPaletteHSV(self.map.examples.domain[attr].values)[val]
391                else:
392                    index = self.map.examples.domain.index(attr)
393                    vals = [n.vector[index] for n in self.map.map]
394                    minval, maxval = min(vals), max(vals)
395                    return lambda val: OWColorPalette.ColorPaletteBW()[1 - (val - minval) / (maxval - minval or 1)] 
396            return colorSchema
397       
398    def componentRange(self, attr = None):
399        vectors = self.map.map.vectors()
400        range = zip(numpy.min(vectors, axis=0), numpy.max(vectors, axis=0))
401        if attr is not None:
402            return range[attr]
403        else:
404            return range
405       
406    def setNodePen(self, pen):
407        for node in self.nodes():
408            node.setPen(pen)
409           
410    def setHistogramData(self, data):
411        self.histogramData = data
412        for node in self.map:
413            node.mappedExamples = orange.ExampleTable(data.domain)
414        for example in data:
415            bmn = self.map.getBestMatchingNode(example)
416            bmn.mappedExamples.append(example)
417           
418        self.updateHistogram()
419
420    def setHistogramConstructor(self, constructor):
421        for item in self.nodes():
422            item.setHistogramConstructor(constructor)
423        self.updateHistogramSize()
424           
425    def updateHistogram(self):
426        for item in self.nodes():
427            item.updateHistogram()
428        self.updateHistogramSize()
429       
430    def updateHistogramSize(self):
431        if not self.histogramData:
432            return
433        maxmapped = max([len(getattr(node.node, "mappedExamples", [])) for node in self.nodes()])
434        for node in self.nodes():
435            if node.node is not None and node.node.mappedExamples and node.histogramItem is not None:
436                mapped = len(node.node.mappedExamples)
437                size = node.boundingRect().width() * 0.75
438                size = size  * mapped / maxmapped
439                rect = QRectF(0.0, 0.0, size, size)
440                rect.moveCenter(node.histogramItem.rect().center())
441                node.histogramItem.setRect(rect)
442               
443    def updateToolTips(self, *args, **kwargs):
444        for node in self.nodes():
445            node.updateToolTips(*args, **kwargs)
446         
447class SOMUMatrixItem(SOMMapItem):
448    """ A SOM U-Matrix graphics object
449    """
450    def setSOMMap(self, map):
451        self.map = map
452        self.somNodeMap = dict([(tuple(n.pos), n) for n in self.map])
453        if self.map.topology==orngSOM.HexagonalTopology:
454            self._setHexagonalUMat(map)
455        elif self.map.topology==orngSOM.RectangularTopology:
456            self._setRectangularUMat(map)
457        self.prepareGeometryChange()
458       
459    def _setHexagonalUMat(self, map):
460        self.map = map 
461        self.uMat = orngSOM.getUMat(map)
462        size=2*int(self.map.map_shape[0]*self.objSize/(2*self.map.map_shape[0]-1))-1
463        x,y=size, size
464        maxDist=max(reduce(numpy.maximum, [a for a in self.uMat]))
465        minDist=max(reduce(numpy.minimum, [a for a in self.uMat]))
466        for i in range(len(self.uMat)):
467            offset = 2 - abs(i % 4 - 2)
468            for j in range(len(self.uMat[i])):
469                h=GraphicsSOMHexagon(self)
470                h.setSize(size)
471                (xa,ya)=h.advancement()
472                h.setPos(x+i*xa, y+j*ya+offset*ya/2)
473                if i%2==0 and j%2==0:
474                    h.setNode(self.somNodeMap[(i/2,j/2)])
475                h.show()
476                val=255-min(max(255*(self.uMat[i][j]-minDist)/(maxDist-minDist),10),245)
477                h.setColor(QColor(val, val, val))
478               
479    def _setRectangularUMat(self, map):
480        self.map = map
481        self.uMat = orngSOM.getUMat(map)
482        size=2*int(self.map.map_shape[0]*self.objSize/(2*self.map.map_shape[0]-1))-1
483        x,y=size, size
484       
485        maxDist=max(reduce(numpy.maximum, [a for a in self.uMat]))
486        minDist=max(reduce(numpy.minimum, [a for a in self.uMat]))
487        for i in range(len(self.uMat)):
488            for j in range(len(self.uMat[i])):
489                r=GraphicsSOMRectangle(self)
490                r.setSize(size)
491                if i%2==0 and j%2==0:
492                    r.setNode(self.somNodeMap[(i/2,j/2)])
493                (xa,ya)=r.advancement()
494                r.setPos(x+i*xa, y+j*ya)
495                r.show()
496                val=255-min(max(255*(self.uMat[i][j]-minDist)/(maxDist-minDist),10),245)
497                r.setColor(QColor(val, val, val))
498
499class LayoutItemWrapper(QGraphicsWidget):
500    def __init__(self, item, parent=None):
501        self.item = item
502        QGraphicsWidget.__init__(self, parent)
503        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
504       
505    def setGeometry(self, rect):
506        self.item.setPos(rect.topLeft())
507        return QGraphicsWidget.setGeometry(self, rect)
508   
509    def sizeHint(self, which, constraint):
510        return self.item.boundingRect().size()
511       
512class AxisItem(QGraphicsWidget):
513    orientation = Qt.Horizontal
514    tickAlign = Qt.AlignBottom
515    textAlign = Qt.AlignHCenter | Qt.AlignBottom
516    axisScale = (0.0, 1.0)
517    def __init__(self, parent=None):
518        QGraphicsWidget.__init__(self, parent) 
519        self.tickCount = 5
520       
521    def setOrientation(self, orientation):
522        self.prepareGeometryChange()
523        self.orientation = orientation
524        if self.orientation == Qt.Horizontal:
525            self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
526        else:
527            self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)
528        self.updateGeometry()
529       
530    def ticks(self):
531        minval, maxval = self.axisScale
532        ticks = ["%.2f" % val for val in numpy.linspace(minval, maxval, self.tickCount)]
533        return ticks
534   
535    def paint(self, painter, option, widget=0):
536        painter.setFont(self.font())
537        size = self.geometry().size()
538        metrics = QFontMetrics(painter.font())
539        minval, maxval = self.axisScale
540        tickCount = 5
541       
542        if self.orientation == Qt.Horizontal:
543            spanx, spany = 0.0, size.width()
544            xadv, yadv =  spanx/ tickCount, 0.0
545            tick_w, tick_h = 0.0, 5.0
546            tickOffset = QPointF(0.0, 0.0)
547        else:
548            spanx, spany = 0.0, size.height()
549            xadv, yadv = 0.0, spany / tickCount
550            tick_w, tick_h = 5.0, 0.0
551            tickFunc = lambda : (y / spany)
552            tickOffset = QPointF(tick_w + 1.0, metrics.ascent()/2)
553           
554        ticks = self.ticks()
555           
556        xstart, ystart = 0.0, 0.0
557        painter.drawLine(xstart, ystart, xstart + tickCount*xadv, ystart + tickCount*yadv)
558       
559        linspacex = numpy.linspace(0.0, spanx, tickCount)
560        linspacey = numpy.linspace(0.0, spany, tickCount)
561       
562        for x, y, tick in zip(linspacex, linspacey, ticks):
563            painter.drawLine(x, y, x + tick_w, y + tick_h)
564            painter.drawText(QPointF(x, y) + tickOffset, tick)
565       
566    def setGeometry(self, rect):
567        self.prepareGeometryChange()
568        return QGraphicsWidget.setGeometry(self, rect)
569       
570    def sizeHint(self, which, *args):
571        minval, maxval = self.axisScale
572        ticks = self.ticks()
573        metrics = QFontMetrics(self.font())
574        if self.orientation == Qt.Horizontal:
575            h = metrics.height() + 5
576            w = 100 
577        else:
578            h = 100
579            w = max([metrics.width(t) for t in ticks]) + 5
580        return QSizeF(w, h)
581   
582    def boundingRect(self):
583        metrics = QFontMetrics(self.font())
584        return QRectF(QPointF(0.0, 0.0), self.sizeHint(None)).adjusted(0, -metrics.ascent(), 5, metrics.ascent())   
585   
586    def setAxisScale(self, min, max):
587        self.axisScale = (min, max)
588        self.updateGeometry()
589       
590    def setAxisTicks(self, ticks):
591        if isinstance(ticks, dict):
592            self.ticks = ticks
593        self.updateGeometry()
594           
595    def tickLayout(self):
596        min, max = getattr(self, "axisScale", (0.0, 1.0))
597        ticks = self.ticks
598        span = max - min
599        span_log = math.log10(span)
600        log_sign = -1 if log_sign < 0.0 else 1
601        span_log = math.floor(span_log)
602        majorTicks = [(x, 5.0, tick(i, span_log)) for i in range(5)]
603        minorTicks = [(x, 3.0, tick(i, span_log + log_sign))  for i in range(10)]
604        return [(i, major, label) for i, tick, label in majorTicks]
605       
606class LegendRect(QGraphicsWidget):
607    orientation = Qt.Horizontal
608    def __init__(self, parent=None):
609        QGraphicsWidget.__init__(self, parent)
610        self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum)
611        self.setColorSchema(lambda val: OWColorPalette.ColorPaletteBW()[1 - val])
612       
613    def setOrientation(self, orientation):
614        self.orientation = orientation
615        if orientation == Qt.Horizontal:
616            self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum)
617        else:
618            self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.MinimumExpanding)
619        self.setColorSchema(self.colorSchema)
620           
621
622    def paint(self, painter, option, widget=0):
623        size = self.geometry().size()
624        painter.setBrush(QBrush(self.gradient))
625        painter.drawRect(0.0, 0.0, size.width(), size.height())
626       
627    def setColorSchema(self, schema):
628        self.colorSchema = schema
629        self.gradient = QLinearGradient(0.0, 0.0, 0.0, 1.0)
630        space = numpy.linspace(0.0, 1.0, 10)
631        color = lambda col: col if isinstance(col, QColor) else QColor(*col)
632        self.gradient.setStops([(x, color(self.colorSchema(x))) for x in space])
633        if hasattr(self.gradient, "setCoordinateMode"):
634            self.gradient.setCoordinateMode(QGradient.ObjectBoundingMode)
635        else:
636            if self.orientation == Qt.Vertical:
637                self.gradient.setStart(self.geometry().topRight())
638                self.gradient.setFinalStop(self.geometry().bottomRight())
639            else:
640                self.gradient.setStart(self.geometry().topRight())
641                self.gradient.setFinalStop(self.geometry().topLeft())
642       
643    def sizeHint(self, which, *args):
644        if self.orientation == Qt.Horizontal:
645            return QSizeF(100, 20)
646        else:
647            return QSizeF(20, 100)
648   
649    def setGeometry(self, rect):
650        QGraphicsWidget.setGeometry(self, rect)
651        if getattr(self, "colorSchema", None) is not None:
652            self.setColorSchema(self.colorSchema) # reset the colorSchema for the new geometry
653       
654class LegendItem(QGraphicsWidget):
655    schema = ColorPalette([(255, 255, 255), (0, 0, 0)])
656    range = (0.0, 1.0)
657    orientation = Qt.Horizontal
658    textAlign = Qt.AlignVCenter | Qt.AlignTop
659    textFont = QFont()
660    def __init__(self, attr, examples=None, parent=None, **kwargs):
661        QGraphicsWidget.__init__(self, parent)
662        self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum)
663        self.setLayout(QGraphicsLinearLayout(Qt.Vertical))
664        self.layout().setSpacing(0)
665        self.rectItem = LegendRect(self)
666        self.rectItem.setOrientation(self.orientation)
667        self.layout().addItem(self.rectItem)
668        self.axisItem = AxisItem(self)
669        self.axisItem.setOrientation(self.orientation)
670        self.layout().addItem(self.axisItem)
671       
672    def setOrientation(self, orientation=Qt.Horizontal):
673        self.orientation = orientation
674        self.rectItem.setOrientation(orientation)
675        self.axisItem.setOrientation(orientation)
676        if orientation == Qt.Horizontal:
677            self.layout().setOrientation(Qt.Vertical)
678            self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum)
679        else:
680            self.layout().setOrientation(Qt.Horizontal)
681            self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.MinimumExpanding)
682        self.updateGeometry()
683       
684    def setTextAlign(self, align):
685        self.textAlign = align
686   
687    def setTextFont(self, font):
688        self.textFont = font
689       
690    def setColorSchema(self, schema):
691        self.schema = schema
692        self.rectItem.setColorSchema(schema)
693       
694    def setTicks(self, ticks):
695        self.ticks = ticks
696
697    def setScale(self, scale):
698        self.scale = scale
699        self.axisItem.setAxisScale(*scale)
700       
701    def setGeometry(self, rect):
702        QGraphicsWidget.setGeometry(self, rect)
703   
704class DiscreteLegendItem(QGraphicsWidget):
705    class _ItemPair(QGraphicsItemGroup):
706        def __init__(self, item1, item2, parent=None):
707            QGraphicsItemGroup.__init__(self, parent)
708            self.item1, self.item2 = item1, item2
709            self.addToGroup(item1)
710            self.addToGroup(item2)
711            self.item1.setPos(0, item1.boundingRect().height()/2)
712            self.item2.setPos(self.item1.boundingRect().width(), 0.0)
713           
714    orientation = Qt.Vertical
715    def __init__(self, attr, examples=None, colorSchema=None, parentItem=None, scene=None, itemShape=QGraphicsEllipseItem):
716        QGraphicsWidget.__init__(self, parentItem)
717        self.attr = attr
718        self.colorSchema = (lambda val: OWColorPalette.ColorPaletteHSV(len(attr.values))[val]) if colorSchema is None else colorSchema
719        self.itemShape = itemShape
720        self.setLayout(QGraphicsLinearLayout(self.orientation))
721       
722        self.legendItems = []
723        for i, value in enumerate(attr.values):
724            self.addLegendItem(attr, value, self.colorSchema(i))
725       
726    def addLegendItem(self, attr, value, color=None, size=10):
727        item = self.itemShape(self)
728        item.setBrush(QBrush(self.colorSchema(len(self.legendItems)) if color is None else color))
729        item.setRect(QRectF(QPointF(0, 0), QSizeF(size, size)))
730       
731        text = QGraphicsTextItem(value, self)
732        pair = self._ItemPair(item, text, self)
733#        pair.setToolTip(value)
734        self.layout().addItem(LayoutItemWrapper(pair))
735       
736    def setOrientation(self, orientation):
737        self.orientation = orientation
738        self.layout().setOrientation(orientation)
739       
740    def setGeometry(self, rect):
741        QGraphicsWidget.setGeometry(self, rect)
742
743
744class SOMGraphicsWidget(QGraphicsWidget):
745    """ Graphics widget containing a SOM map and optional legends
746    """
747    def __init__(self, parent=None):
748        QGraphicsWidget.__init__(self, parent)
749        self.setLayout(QGraphicsLinearLayout())
750        self.legendLayout = QGraphicsLinearLayout(Qt.Vertical)
751        self.layout().addItem(self.legendLayout)
752        self.componentLegendItem = None
753        self.histLegendItem = None
754       
755    def clear(self):
756        for i in range(self.layout().count()):
757            self.removeAt(i)
758           
759    def setSOM(self, som):
760        self.som = som
761        if getattr(self, "somItem", None) is not None:
762            self.layout().removeAt(0)
763            self.scene().removeItem(self.somItem)
764        self.somItem = SOMMapItem(som, parent=self)
765        self.layout().insertItem(0, LayoutItemWrapper(self.somItem))
766       
767    def setComponentPlane(self, attr):
768        self.componentPlane = attr
769        self.somItem.setComponentPlane(attr)
770        if self.componentLegendItem is not None:
771            self.scene().removeItem(self.componentLegendItem)
772            self.componentLegendItem = None
773        if attr is not None:
774            self.componentLegendItem = LegendItem(attr, parent=self)
775            self.componentLegendItem.setOrientation(Qt.Vertical)
776            self.legendLayout.insertItem(0, self.componentLegendItem)
777            self.componentLegendItem.setScale(self.somItem.componentRange(attr))
778       
779    def setHistogramData(self, data):
780        self.histogramData = data
781        self.somItem.setHistogramData(data)
782       
783    def setHistogramConstructor(self, constructor):
784        self.histogramConstructor = constructor
785        self.somItem.setHistogramConstructor(constructor)
786        if self.histLegendItem is not None:
787            self.scene().removeItem(self.histLegendItem)
788            self.histLegendItem = None
789        if constructor and getattr(constructor, "legendItemConstructor", None) is not None:
790            self.histLegendItem = constructor.legendItemConstructor(self.histogramData)
791            self.histLegendItem.setOrientation(Qt.Vertical)             
792           
793            self.legendLayout.insertItem(1, self.histLegendItem)
794           
795    def setHistogram(self, attr, type_):
796        def const(*args, **kwargs):
797            return type_(attr, self.data, *args, **kwargs)
798       
799        self.histogramConstructor = const
800        self.histogramConstructorType = type_
801        self.histogramAttr = attr
802        self.setHistogramConstructor(self.histogramConstructor)
803           
804    def setColorSchema(self, schema):
805        self.colorSchema = schema
806        self.update()
807       
808    def setHistogramColorSchema(self, schema):
809        self.histogramColorSchema = schema
810        self.somItem.setHistogramColorSchema(schema)
811        self.update()
812       
813    def paint(self, painter, option, widget=0):
814        painter.drawRect(self.boundingRect())
815       
816class SOMGraphicsUMatrix(SOMGraphicsWidget):
817    def setSOM(self, som):
818        self.som = som
819        if getattr(self, "somItem", None) is not None:
820            self.scene().removeItem(self.somItem)
821            self.somItem = None
822        self.somItem = SOMUMatrixItem(som, parent=self)
823        self.layout().insertItem(0, LayoutItemWrapper(self.somItem))
824       
825    def setComponentPlane(self, *args):
826        raise NotImplemented
827   
828baseColor = QColor(20,20,20)
829
830class SceneSelectionManager(QObject):
831    def __init__(self, scene):
832        QObject.__init__(self, scene)
833        self.scene = scene
834        self.selection = []
835       
836    def start(self, event):
837        pos = event.scenePos()
838        if event.modifiers() & Qt.ControlModifier:
839            self.selection.append((pos, pos + QPointF(1, 1)))
840        else:
841            self.selection = [(pos, pos)]
842        self.emit(SIGNAL("selectionGeometryChanged()"))
843       
844    def update(self, event):
845        pos = event.scenePos() + QPointF(2.0, 2.0)
846        self.selection[-1] = self.selection[-1][:-1] + (pos,)
847        self.emit(SIGNAL("selectionGeometryChanged()"))
848   
849    def end(self, event):
850        self.update(event)
851       
852    def testSelection(self, data):
853        data = numpy.asarray(data)
854        region = QPainterPath()
855        for p1, p2 in self.selection:
856            region.addRect(QRectF(p1, p2).normalized())
857        def test(point):
858            return region.contains(QPointF(point[0], point[1]))
859        test = numpy.apply_along_axis(test, 1, data)
860        return test
861   
862    def selectionArea(self):
863        region = QPainterPath()
864        for p1, p2 in self.selection:
865            region.addRect(QRectF(p1, p2).normalized())
866        return region
867   
868    def lastSelectionRect(self):
869        if self.selection:
870            return QRectF(*self.selection[-1]).normalized()
871        else:
872            return None
873           
874class SOMScene(QGraphicsScene):
875    def __init__(self, parent=None):
876        QGraphicsScene.__init__(self, parent)
877        self.histogramData = None
878        self.histogramConstructor = None
879        self.histogramColorSchema = None
880        self.componentColorSchema = None
881        self.componentPlane = None
882        self.somWidget = None
883       
884    def clear(self):
885        QGraphicsScene.clear(self)
886        self.histogramData = None
887        self.histogramConstructor = None
888        self.histogramColorSchema = None
889        self.componentColorSchema = None
890        self.componentPlane = None
891        self.somWidget = None
892       
893    def setSom(self, map):
894        self.clear()
895        self.map = map
896       
897        self.emit(SIGNAL("som_changed()"))
898       
899        self.selectionManager = SceneSelectionManager(self)
900        self.connect(self.selectionManager, SIGNAL("selectionGeometryChanged()"), self.onSelectionAreaChange)
901       
902    def setComponentPlane(self, attr):
903        if type(self.somWidget) != SOMGraphicsWidget:
904            self.clear()
905            self.somWidget = SOMGraphicsWidget(None)
906            self.somWidget.setSOM(self.map)
907            self.somWidget.setComponentPlane(attr)
908            self.addItem(self.somWidget)
909            self.componentPlane = None
910            self.histogramData = None
911            self.histogramConstructor = None
912           
913        if attr is not self.componentPlane:
914            self.componentPlane = attr
915            for item in self.somWidgets():
916                item.setComponentPlane(attr)
917               
918    def setUMatrix(self):
919        if type(self.somWidget) != SOMGraphicsUMatrix:
920            self.clear()
921            self.somWidget = SOMGraphicsUMatrix(None)
922            self.somWidget.setSOM(self.map)
923            self.addItem(self.somWidget)
924       
925    def somWidgets(self):
926        for item in self.items():
927            if isinstance(item, SOMGraphicsWidget):
928                yield item
929       
930    def setHistogramData(self, data):
931        if data is not self.histogramData:
932            self.histogramData = data
933            for item in self.somWidgets():
934                item.setHistogramData(data)
935               
936    def setHistogramConstructor(self, constructor):
937        if self.histogramConstructor is not constructor:
938            self.histogramConstructor = constructor
939            for item in self.somWidgets():
940                item.setHistogramConstructor(constructor)
941           
942    def setHistogramColorSchema(self, schema):
943        if schema is not self.histogramColorSchema:
944            self.histogramColorSchema = schema
945            for item in self.somWidgets():
946                item.setHistogramColorSchema(schema)
947           
948    def setComponentColorSchema(self, schema):
949        if schema is not self.componentColorSchema:
950            self.componentColorSchema = schema
951            for item in self.somWidgets():
952                item.setComponentColorSchema(schema)
953       
954    def setNodePen(self, pen):
955        for item in self.somWidgets():
956            item.somItem.setNodePen(pen)
957           
958    def mousePressEvent(self, event):
959        if event.button() == Qt.LeftButton:
960            self.selectionManager.start(event)
961            if getattr(self, "selectionRectItem", None) is not None:
962                self.removeItem(self.selectionRectItem)
963            self.selectionRectItem = self.addRect(self.selectionManager.lastSelectionRect())
964            self.selectionRectItem.setRect(self.selectionManager.lastSelectionRect())
965            self.selectionRectItem.show()
966       
967    def mouseMoveEvent(self, event):
968        if event.buttons() & Qt.LeftButton:
969            self.selectionManager.update(event)
970            if self.selectionRectItem:
971                self.selectionRectItem.setRect(self.selectionManager.lastSelectionRect())
972   
973    def mouseReleaseEvent(self, event):
974        if event.button() & Qt.LeftButton:
975            self.selectionManager.end(event)
976            if self.selectionRectItem:
977                self.selectionRectItem.hide()
978                self.removeItem(self.selectionRectItem)
979                self.selectionRectItem = None
980   
981    def mouseDoubleClickEvent(self, event):
982        return
983   
984    def onSelectionAreaChange(self):
985        self.setSelectionArea(self.selectionManager.selectionArea())
986       
987    def updateToolTips(self, *args, **kwargs):
988        for item in self.somWidgets():
989            item.somItem.updateToolTips(*args, **kwargs)
990   
991class OWSOMVisualizer(OWWidget):
992    settingsList = ["drawMode", "showNodeOutlines", "objSize","commitOnChange", "backgroundMode", "backgroundCheck", "includeCodebook", "showToolTips"]
993    contextHandlers = {"":DomainContextHandler("", [ContextField("attribute", DomainContextHandler.Optional),
994                                                  ContextField("discHistMode", DomainContextHandler.Optional),
995                                                  ContextField("contHistMode", DomainContextHandler.Optional),
996                                                  ContextField("targetValue", DomainContextHandler.Optional),
997                                                  ContextField("histogram", DomainContextHandler.Optional),
998                                                  ContextField("inputSet", DomainContextHandler.Optional),
999                                                  ContextField("component", DomainContextHandler.Optional),
1000                                                  ContextField("includeCodebook", DomainContextHandler.Optional)])}
1001   
1002    drawModes = ["None", "U-Matrix", "Component planes"]
1003   
1004    def __init__(self, parent=None, signalManager=None, name="SOM visualizer"):
1005        OWWidget.__init__(self, parent, signalManager, name, wantGraph=True)
1006        self.inputs = [("SOM", orngSOM.SOMMap, self.setSomMap), ("Data", ExampleTable, self.data)]
1007        self.outputs = [("Data", ExampleTable)]
1008       
1009        self.drawMode = 2
1010        self.objSize = 15
1011        self.component = 0
1012        self.labelNodes = 0
1013        self.commitOnChange = 0
1014        self.backgroundCheck = 1
1015        self.backgroundMode = 0
1016        self.includeCodebook = 0
1017        self.showToolTips = 0
1018        self.histogram = 1
1019        self.attribute = 0
1020        self.discHistMode = 0
1021        self.targetValue = 0
1022        self.contHistMode = 0
1023        self.inputSet = 0
1024        self.showNodeOutlines = 1
1025
1026        self.somMap = None
1027        self.examples = None
1028        self.selectionChanged = False
1029       
1030       
1031        self.scene = SOMScene(self)
1032        self.sceneView = QGraphicsView(self.scene, self.mainArea)
1033        self.sceneView.viewport().setMouseTracking(True)
1034        self.sceneView.setFocusPolicy(Qt.WheelFocus)
1035        self.sceneView.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing)
1036
1037        self.mainArea.layout().addWidget(self.sceneView)
1038        self.connect(self.scene, SIGNAL("selectionChanged()"), self.commitIf)
1039       
1040        self.loadSettings()
1041
1042        histTab = mainTab = self.controlArea
1043
1044        self.mainTab = mainTab
1045        self.histTab = histTab
1046
1047        self.backgroundBox = OWGUI.widgetBox(mainTab, "Background")
1048        b = OWGUI.radioButtonsInBox(self.backgroundBox, self, "drawMode", self.drawModes, callback=self.setBackground)
1049        b.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed))
1050        self.componentCombo=OWGUI.comboBox(OWGUI.indentedBox(b), self,"component", callback=self.setBackground)
1051        self.componentCombo.setEnabled(self.drawMode==2)
1052        OWGUI.checkBox(self.backgroundBox, self, "showNodeOutlines", "Show grid", callback=self.updateGrid)
1053       
1054        b = OWGUI.widgetBox(mainTab, "Histogram") 
1055        OWGUI.checkBox(b, self, "histogram", "Show histogram", callback=self.setHistogram)
1056        OWGUI.radioButtonsInBox(OWGUI.indentedBox(b), self, "inputSet", ["Use training set", "Use input subset"], callback=self.setHistogram)
1057       
1058        b1= OWGUI.widgetBox(mainTab) 
1059        b=OWGUI.hSlider(b1, self, "objSize","Plot size", 1, 100,step=10,ticks=10, callback=self.setZoom)
1060
1061        b1 = OWGUI.widgetBox(b1, "Tooltip Info")
1062        OWGUI.checkBox(b1, self, "showToolTips","Show tooltip", callback=self.updateToolTips)
1063        OWGUI.checkBox(b1, self, "includeCodebook", "Include codebook vector", callback=self.updateToolTips)
1064        b1.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed))
1065       
1066        self.histogramBox = OWGUI.widgetBox(histTab, "Coloring")
1067        self.attributeCombo = OWGUI.comboBox(self.histogramBox, self, "attribute", callback=self.onHistogramAttrSelection)
1068        self.coloringStackedLayout = QStackedLayout()
1069        indentedBox = OWGUI.indentedBox(self.histogramBox, orientation=self.coloringStackedLayout)
1070        self.discTab = OWGUI.widgetBox(indentedBox)
1071        OWGUI.radioButtonsInBox(self.discTab, self, "discHistMode", ["Pie chart", "Majority value", "Majority value prob."], box=0, callback=self.onHistogramAttrSelection)
1072        self.discTab.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed))
1073
1074        self.contTab = OWGUI.widgetBox(indentedBox)
1075        OWGUI.radioButtonsInBox(self.contTab, self, "contHistMode", ["Default", "Average value"], callback=self.onHistogramAttrSelection)
1076        self.contTab.layout().addStretch(10)
1077        self.contTab.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed))
1078 
1079        b = OWGUI.widgetBox(self.controlArea, "Selection")
1080        OWGUI.button(b, self, "&Invert selection", callback=self.invertSelection)
1081        button = OWGUI.button(b, self, "&Commit", callback=self.commit, default=True)
1082        check = OWGUI.checkBox(b, self, "commitOnChange", "Commit on change")
1083        OWGUI.setStopper(self, button, check, "selectionChanged", self.commit)
1084
1085        OWGUI.rubber(self.controlArea)
1086        self.connect(self.graphButton, SIGNAL("clicked()"), self.saveGraph)
1087       
1088        self.selectionList = []
1089        self.ctrlPressed = False
1090        self.resize(800,500)
1091
1092    def sendReport(self):
1093        self.reportSettings("Visual settings",
1094                           [("Background", self.drawModes[self.drawMode]+(" - "+self.componentCombo.currentText() if self.drawMode==2 else "")),
1095                            ("Histogram", ["data from training set", "data from input subset"][self.inputSet] if self.histogram else "none"),
1096                            ("Coloring",  "%s for %s" % (["pie chart", "majority value", "majority value probability"][self.discHistMode], self.attributeCombo.currentText()))
1097                           ])
1098        self.reportSection("Plot")
1099        self.reportImage(OWChooseImageSizeDlg(self.scene).saveImage)
1100       
1101    def setMode(self):
1102        self.componentCombo.setEnabled(self.drawMode == 2)
1103        if not self.somMap:
1104            return
1105        self.error(0)
1106        if self.drawMode == 0:
1107            self.scene.setComponentPlane(None)
1108        elif self.drawMode == 2:
1109            self.scene.setComponentPlane(self.component)
1110        elif self.drawMode == 1:
1111            self.scene.setUMatrix()
1112        if self.histogram:
1113            self.setHistogram()
1114        self.updateToolTips()
1115        self.updateGrid()
1116        self.setZoom()
1117
1118    def setBackground(self):
1119        self.setMode()
1120
1121    def setDiscCont(self):
1122        if self.somMap.examples.domain.variables[self.attribute].varType == orange.VarTypes.Discrete:
1123            self.coloringStackedLayout.setCurrentWidget(self.discTab)
1124        else:
1125            self.coloringStackedLayout.setCurrentWidget(self.contTab)
1126       
1127    def onHistogramAttrSelection(self):
1128        if not self.somMap:
1129            return 
1130        if self.somMap.examples.domain.variables[self.attribute].varType == orange.VarTypes.Discrete:
1131            self.coloringStackedLayout.setCurrentWidget(self.discTab)
1132        else:
1133            self.coloringStackedLayout.setCurrentWidget(self.contTab)
1134           
1135        self.setHistogram()
1136       
1137    def setHistogram(self):
1138        if self.somMap and self.histogram:
1139            if self.inputSet and self.examples is not None and self.examples.domain == self.somMap.examples.domain:
1140                self.scene.setHistogramData(self.examples)
1141                attr = self.examples.domain.variables[self.attribute]
1142            else:
1143                self.scene.setHistogramData(self.somMap.examples)
1144                attr = self.somMap.examples.domain.variables[self.attribute]
1145               
1146            if attr.varType == orange.VarTypes.Discrete:
1147                hist = [PieChart, MajorityChart, MajorityProbChart]
1148                hist = hist[self.discHistMode]
1149                visible, schema = True, None
1150            else:
1151                hist = [ContChart, AverageValue]
1152                hist = hist[self.contHistMode]
1153                visible = self.contHistMode == 1
1154                schema = ColorPalette([(0, 255, 0), (255, 0, 0)]) if self.contHistMode == 1 else None
1155               
1156            def partial__init__(self, *args, **kwargs):
1157                hist.__init__(self, attr, *args)
1158               
1159            def partialLegendItem(cls, *args, **kwargs):
1160                return hist.legendItemConstructor(attr, *args, **kwargs)
1161            hist_ = type(hist.__name__ + "Partial", (hist,), {"__init__":partial__init__, "legendItemConstructor":classmethod(partialLegendItem)})
1162            if schema:
1163                self.scene.setHistogramColorSchema(schema)
1164            self.scene.setHistogramConstructor(hist_)
1165           
1166            if visible and schema:
1167                self.scene.somWidget.histLegendItem.setColorSchema(schema)
1168            elif not visible:
1169                self.scene.somWidget.histLegendItem.hide()
1170        else:
1171            self.scene.setHistogramConstructor(None)
1172           
1173    def update(self):
1174        self.setMode()
1175
1176    def drawPies(self):
1177        return self.discHistMode == 0 and self.somMap.examples.domain.variables[self.attribute].varType == orange.VarTypes.Discrete
1178   
1179    def setZoom(self):
1180        self.sceneView.setTransform(QTransform().scale(float(self.objSize)/SOMMapItem.objSize, float(self.objSize)/SOMMapItem.objSize))
1181   
1182    def updateGrid(self):
1183        self.scene.setNodePen(QPen(Qt.black, 1) if self.showNodeOutlines else QPen(Qt.NoPen))
1184   
1185    def updateToolTips(self):
1186        self.scene.updateToolTips(showToolTips=self.showToolTips, includeCodebook=self.includeCodebook)
1187       
1188    def setSomMap(self, somMap=None):
1189        self.somType = "Map"
1190        self.setSom(somMap)
1191       
1192    def setSomClassifier(self, somMap=None):
1193        self.somType = "Classifier"
1194        self.setSom(somMap)
1195       
1196    def setSom(self, somMap=None):
1197        self.closeContext()
1198        self.somMap = somMap
1199        if not somMap:
1200            self.clear()
1201            return
1202        self.componentCombo.clear()
1203        self.attributeCombo.clear()
1204       
1205        self.targetValue = 0
1206        self.attribute = 0
1207        self.component = 0
1208        for v in somMap.examples.domain.attributes:
1209            self.componentCombo.addItem(v.name)
1210        for v in somMap.examples.domain.variables:
1211            self.attributeCombo.addItem(v.name)
1212           
1213        if somMap.examples.domain.classVar:
1214            self.attribute = len(somMap.examples.domain.variables) - 1
1215
1216        self.openContext("", somMap.examples)
1217        self.component = min(self.component, len(somMap.examples.domain.attributes) - 1)
1218        self.attribute = min(self.attribute, len(somMap.examples.domain.variables) - 1)
1219        self.setDiscCont()
1220        self.scene.setSom(somMap)
1221        self.update()
1222       
1223    def data(self, data=None):
1224        self.examples = data
1225        if data and self.somMap:
1226            for n in self.somMap.map:
1227                setattr(n,"mappedExamples", orange.ExampleTable(data.domain))
1228            for e in data:
1229                bmu = self.somMap.getBestMatchingNode(e)
1230                bmu.mappedExamples.append(e)
1231            if self.inputSet == 1:
1232                self.setHistogram()
1233        self.update()
1234   
1235    def clear(self):
1236        self.scene.clearSelection()
1237        self.componentCombo.clear()
1238        self.attributeCombo.clear()
1239        self.scene.component = 0
1240        self.scene.setSom(None)
1241        self.update()
1242        self.send("Data", None)
1243       
1244    def invertSelection(self):
1245        self._invertingSelection = True
1246        try:
1247            for widget in self.scene.somWidgets():
1248                for node in widget.somItem.nodes():
1249                    node.setSelected(not node.isSelected())
1250        finally:
1251            del self._invertingSelection
1252            self.commitIf()
1253   
1254    def updateSelection(self, nodeList):
1255        self.selectionList = nodeList
1256        self.commitIf()
1257       
1258    def commitIf(self):
1259        if self.commitOnChange and not getattr(self, "_invertingSelection", False):
1260            self.commit()
1261        else:
1262            self.selectionChanged = True
1263           
1264    def commit(self):
1265        if not self.somMap:
1266            return
1267        ex = orange.ExampleTable(self.somMap.examples.domain)
1268       
1269        for n in self.scene.selectedItems():
1270            if isinstance(n, GraphicsSOMItem) and n.node and hasattr(n.node, "mappedExamples"):
1271                ex.extend(n.node.mappedExamples)
1272        if len(ex):
1273            self.send("Data",ex)
1274        else:
1275            self.send("Data",None)
1276           
1277        self.selectionChanged = False
1278
1279    def saveGraph(self):
1280        sizeDlg = OWChooseImageSizeDlg(self.scene, parent=self)
1281        sizeDlg.exec_()
1282       
1283if __name__=="__main__":
1284    ap = QApplication(sys.argv)
1285    data = orange.ExampleTable("../../doc/datasets/housing.tab")
1286#    l=orngSOM.SOMLearner(batch_train=False)
1287    l = orngSOM.SOMLearner(batch_train=True, initialize=orngSOM.InitializeLinear)
1288#    l = orngSOM.SOMLearner(batch_train=True, initialize=orngSOM.InitializeRandom)
1289    l = l(data)
1290    l.data = data
1291#    import cPickle
1292#    cPickle.dump(l, open("iris.som", "wb"))
1293#    l = cPickle.load(open("iris.som", "rb"))
1294   
1295    w = OWSOMVisualizer()
1296##    ap.setMainWidget(w)
1297    w.setSomClassifier(l)
1298    w.data(orange.ExampleTable(data[:]))
1299    w.show()
1300    ap.exec_()
1301    w.saveSettings()
1302   
1303   
Note: See TracBrowser for help on using the repository browser.