source: orange/Orange/projection/linear.py @ 9916:b31e223d233b

Revision 9916:b31e223d233b, 58.2 KB checked in by lanumek, 2 years ago (diff)

Changed Orange.core.SymMatrix -> Orange.misc.SymMatrix

Line 
1'''
2##############################
3Linear projection (``linear``)
4##############################
5
6.. index:: linear projection
7
8.. index::
9   single: projection; linear
10
11This module contains the FreeViz algorithm
12`(Demsar et al, 2005) <http://www.ailab.si/idamap/idamap2005/papers/12%20Demsar%20CR.pdf>`_
13[1], which finds a good two-dimensional projection of the given data, where the
14quality is defined by a separation of the data from different classes and the
15proximity of the instances from the same class. FreeViz would normally be used
16through a widget since it is primarily a method for graphical exploration of
17the data. About the only case where one would like to use this module directly
18is to tests the classification aspects of the method, that is, to verify the
19accuracy of the resulting kNN-like classifiers on a set of benchmark data sets.
20
21Description of the method itself is far beyond the scope of this page. See the
22above paper for the original version of the method; at the moment of writing
23the method has been largely extended and not published yet, though the basic
24principles are the same.
25
26[1] Janez Demsar, Gregor Leban, Blaz Zupan: FreeViz - An Intelligent
27Visualization Approach for Class-Labeled Multidimensional Data Sets,
28Proceedings of IDAMAP 2005, Edinburgh.
29
30***********************
31Projection Optimization
32***********************
33
34.. autoclass:: Orange.projection.linear.FreeViz
35   :members:
36   :show-inheritance:
37   :exclude-members: attractG, attractG, autoSetParameters, cancelOptimization,
38      classPermutationList, classPermutationList, findProjection,
39      forceBalancing, forceSigma, getShownAttributeList, mirrorSymmetry,
40      optimizeSeparation, optimize_FAST_Separation, optimize_LDA_Separation,
41      optimize_SLOW_Separation, radialAnchors, randomAnchors, repelG,
42      s2nMixAnchors, s2nMixData, s2nPlaceAttributes, s2nSpread,
43      setStatusBarText, showAllAttributes, stepsBeforeUpdate,
44      useGeneralizedEigenvectors
45
46**********************
47Learner and Classifier
48**********************
49
50.. autoclass:: Orange.projection.linear.FreeVizLearner
51   :members:
52   :show-inheritance:
53
54.. autoclass:: Orange.projection.linear.FreeVizClassifier
55   :members:
56   :show-inheritance:
57
58.. autoclass:: Orange.projection.linear.S2NHeuristicLearner
59   :members:
60   :show-inheritance:
61
62'''
63
64
65import Orange
66from Orange import orangeom
67import math
68import random
69import numpy
70
71from numpy.linalg import inv, pinv, eig      # matrix inverse and eigenvectors
72from Orange.preprocess.scaling import ScaleLinProjData
73from Orange.orng import orngVisFuncts as visfuncts
74from Orange.misc import deprecated_keywords
75from Orange.misc import deprecated_members
76
77try:
78    import numpy.ma as MA
79except:
80    import numpy.core.ma as MA
81
82#implementation
83FAST_IMPLEMENTATION = 0
84SLOW_IMPLEMENTATION = 1
85LDA_IMPLEMENTATION = 2
86
87LAW_LINEAR = 0
88LAW_SQUARE = 1
89LAW_GAUSSIAN = 2
90LAW_KNN = 3
91LAW_LINEAR_PLUS = 4
92
93DR_PCA = 0
94DR_SPCA = 1
95DR_PLS = 2
96
97def normalize(x):
98    return x / numpy.linalg.norm(x)
99
100def center(matrix):
101    '''centers all variables, i.e. subtracts averages in colomns
102    and divides them by their standard deviations'''
103    n,m = numpy.shape(matrix)
104    return (matrix - numpy.multiply(matrix.mean(axis = 0),
105                                    numpy.ones((n,m))))/numpy.std(matrix,
106                                                                  axis = 0)
107
108
109class FreeViz:
110    """
111    Contains an easy-to-use interface to the core of the method, which is
112    written in C++.
113   
114    .. attribute:: attract_g
115   
116    Coefficient for the attractive forces. By increasing or decreasing the ratio
117    between :obj:`attract_g` and :obj:`repel_g`, you can make one kind of the
118    forces stronger. Default: 1.
119   
120    .. attribute:: repel_g
121   
122    Coefficient for the repulsive forces. By increasing or decreasing the ratio
123    between :obj:`attract_g` and :obj:`repel_g`, you can make one kind of the
124    forces stronger. Default: 1.
125   
126    .. attribute:: force_balancing
127   
128    If set (default is False), the forces are balanced so that the total sum of
129    the attractive equals the total of repulsive, before they are multiplied by
130    the above factors. (By our experience, this gives bad results so you may
131    want to leave this alone.)
132   
133    .. attribute:: law
134   
135    Can be LAW_LINEAR, LAW_SQUARE, LAW_GAUSSIAN, LAW_KNN or LAW_LINEAR_PLUS.
136    Default is LAW_LINEAR, which means that the attractive forces increase
137    linearly by the distance and the repulsive forces are inversely
138    proportional to the distance. LAW_SQUARE would make them rise or fall with
139    the square of the distance, LAW_GAUSSIAN is based on a kind of
140    log-likelihood estimation, LAW_KNN tries to directly optimize the
141    classification accuracy of the kNN classifier in the projection space, and
142    in LAW_LINEAR_PLUS both forces rise with the square of the distance,
143    yielding a method that is somewhat similar to PCA. We found the first law
144    perform the best, with the second to not far behind.
145   
146    .. attribute:: force_sigma
147   
148    The sigma to be used in LAW_GAUSSIAN and LAW_KNN.
149   
150    .. attribute:: mirror_symmetry
151   
152    If enabled, it keeps the projection of the second attribute on the upper
153    side of the graph (the first is always on the right-hand x-axis). This is
154    useful when comparing whether two projections are the same, but has no
155    effect on the projection's clarity or its classification accuracy.
156
157    There are some more, undescribed, methods of a more internal nature.
158   
159    """
160   
161    def __init__(self, graph = None):
162        if not graph:
163            graph = ScaleLinProjData()
164        self.graph = graph
165
166        self.implementation = 0
167        self.attract_g = 1.0
168        self.repel_g = 1.0
169        self.law = LAW_LINEAR
170        self.restrain = 0
171        self.force_balancing = 0
172        self.force_sigma = 1.0
173        self.mirror_symmetry = 1
174        self.use_generalized_eigenvectors = 1
175
176        # s2n heuristics parameters
177        self.steps_before_update = 10
178        self.s2n_spread = 5
179        self.s2n_place_attributes = 50
180        self.s2n_mix_data = None
181        self.auto_set_parameters = 1
182        self.class_permutation_list = None
183        self.attrs_num = [5, 10, 20, 30, 50, 70, 100, 150, 200, 300, 500, 750,
184                          1000]
185
186    def clear_data(self):
187        self.s2n_mix_data = None
188        self.class_permutation_list = None
189       
190    clearData = clear_data
191
192    def set_statusbar_text(self, *args):
193        pass
194   
195    setStatusBarText = set_statusbar_text
196
197    def show_all_attributes(self):
198        self.graph.anchorData = [(0,0, a.name)
199                                 for a in self.graph.dataDomain.attributes]
200        self.radial_anchors()
201       
202    showAllAttributes = show_all_attributes
203
204    def get_shown_attribute_list(self):
205        return [anchor[2] for anchor in self.graph.anchorData]
206
207    getShownAttributeList = get_shown_attribute_list
208
209    def radial_anchors(self):
210        """
211        Reset the projection so that the anchors (projections of attributes)
212        are placed evenly around the circle.
213       
214        """
215        attr_list = self.get_shown_attribute_list()
216        if not attr_list:
217            return
218        if "3d" in self.parentName.lower():
219            self.graph.anchor_data = self.graph.create_anchors(len(attr_list), attr_list)
220            return
221        phi = 2*math.pi/len(attr_list)
222        self.graph.anchorData = [(math.cos(i*phi), math.sin(i*phi), a)
223                                 for i, a in enumerate(attr_list)]
224
225    radialAnchors = radial_anchors
226
227    def random_anchors(self):
228        """
229        Set the projection to a random one.
230       
231        """
232        if not self.graph.haveData:
233            return
234        attr_list = self.get_shown_attribute_list()
235        if not attr_list:
236            return
237        if "3d" in self.parentName.lower():
238            if self.restrain == 0:
239                def ranch(i, label):
240                    r = 0.3+0.7*random.random()
241                    phi = 2*math.pi*random.random()
242                    theta = math.pi*random.random()
243                    return (r*math.sin(theta)*math.cos(phi),
244                            r*math.sin(theta)*math.sin(phi),
245                            r*math.cos(theta),
246                            label)
247            elif self.restrain == 1:
248                def ranch(i, label):
249                    phi = 2*math.pi*random.random()
250                    theta = math.pi*random.random()
251                    r = 1.
252                    return (r*math.sin(theta)*math.cos(phi),
253                            r*math.sin(theta)*math.sin(phi),
254                            r*math.cos(theta),
255                            label)
256            else:
257                self.graph.anchor_data = self.graph.create_anchors(len(attr_list), attr_list)
258                def ranch(i, label):
259                    r = 0.3+0.7*random.random()
260                    return (r*self.graph.anchor_data[i][0],
261                            r*self.graph.anchor_data[i][1],
262                            r*self.graph.anchor_data[i][2],
263                            label)
264
265            anchors = [ranch(*a) for a in enumerate(attr_list)]
266
267            if not self.restrain == 1:
268                maxdist = math.sqrt(max([x[0]**2+x[1]**2+x[2]**2 for x in anchors]))
269                anchors = [(x[0]/maxdist, x[1]/maxdist, x[2]/maxdist, x[3]) for x in anchors]
270
271            self.graph.anchorData = anchors
272            return
273
274        if self.restrain == 0:
275            def ranch(i, label):
276                r = 0.3+0.7*random.random()
277                phi = 2*math.pi*random.random()
278                return (r*math.cos(phi), r*math.sin(phi), label)
279
280        elif self.restrain == 1:
281            def ranch(i, label):
282                phi = 2*math.pi*random.random()
283                return (math.cos(phi), math.sin(phi), label)
284
285        else:
286            def ranch(i, label):
287                r = 0.3+0.7*random.random()
288                phi = 2*math.pi * i / max(1, len(attr_list))
289                return (r*math.cos(phi), r*math.sin(phi), label)
290
291        anchors = [ranch(*a) for a in enumerate(attr_list)]
292
293        if not self.restrain == 1:
294            maxdist = math.sqrt(max([x[0]**2+x[1]**2 for x in anchors]))
295            anchors = [(x[0]/maxdist, x[1]/maxdist, x[2]) for x in anchors]
296
297        if not self.restrain == 2 and self.mirror_symmetry:
298            #### Need to rotate and mirror here
299            pass
300
301        self.graph.anchorData = anchors
302
303    randomAnchors = random_anchors
304
305    @deprecated_keywords({"singleStep": "single_step"})
306    def optimize_separation(self, steps = 10, single_step = False, distances=None):
307        """
308        Optimize the class separation. If you did not change any of the settings
309        which are not documented above, it will call a fast C++ routine which
310        will make :obj:`steps` optimization steps at a time, after which the
311        graph (if one is given) is updated. If :obj:`single_step` is True, it
312        will do that only once,
313        otherwise it calls it on and on, and compares the current positions of
314        the anchors with those 50 calls ago. If no anchor moved for more than
315        1e-3, it stops. In Orange Canvas the optimization is also stopped if
316        someone outside (namely, the stop button) manages to set the FreeViz's
317        flag attribute
318        :obj:`Orange.projection.linear.FreeViz.cancel_optimization`.
319        """
320        # check if we have data and a discrete class
321        if (not self.graph.haveData or len(self.graph.rawData) == 0
322            or not (self.graph.dataHasClass or distances)):
323            return
324        ai = self.graph.attributeNameIndex
325        attr_indices = [ai[label] for label in self.get_shown_attribute_list()]
326        if not attr_indices: return
327
328        if self.implementation == FAST_IMPLEMENTATION and not hasattr(self, '_use_3D'): # TODO
329            return self.optimize_fast_separation(steps, single_step, distances)
330
331        if self.__class__ != FreeViz: from PyQt4.QtGui import qApp
332        if single_step: steps = 1
333        if self.implementation == SLOW_IMPLEMENTATION:
334            impl = self.optimize_slow_separation
335        elif self.implementation == LDA_IMPLEMENTATION:
336            impl = self.optimize_lda_separation
337        xanchors = None
338        yanchors = None
339        zanchors = None
340
341        if hasattr(self, '_use_3D'):
342            if self.implementation == SLOW_IMPLEMENTATION:
343                impl = self.optimize_slow_separation_3D
344            elif self.implementation == LDA_IMPLEMENTATION:
345                impl = self.optimize_lda_separation_3D
346            else:
347                print('Unimplemented method!')
348                return
349
350            for c in range((single_step and 1) or 50):
351                for i in range(steps):
352                    if self.__class__ != FreeViz and self.cancel_optimization == 1:
353                        return
354                    self.graph.anchorData, (xanchors, yanchors, zanchors) = impl(attr_indices,
355                                                                                 self.graph.anchorData,
356                                                                                 xanchors,
357                                                                                 yanchors,
358                                                                                 zanchors)
359                if self.__class__ != FreeViz: qApp.processEvents()
360                if hasattr(self.graph, "updateGraph"): self.graph.updateData()
361        else:
362            for c in range((single_step and 1) or 50):
363                for i in range(steps):
364                    if self.__class__ != FreeViz and self.cancel_optimization == 1:
365                        return
366                    self.graph.anchorData, (xanchors, yanchors) = impl(attr_indices,
367                                                                       self.graph.anchorData,
368                                                                       xanchors,
369                                                                       yanchors)
370                if self.__class__ != FreeViz: qApp.processEvents()
371                if hasattr(self.graph, "updateGraph"): self.graph.updateData()
372
373    optimizeSeparation = optimize_separation
374
375    @deprecated_keywords({"singleStep": "single_step"})
376    def optimize_fast_separation(self, steps = 10, single_step = False, distances=None):
377        optimizer = [orangeom.optimizeAnchors, orangeom.optimizeAnchorsRadial,
378                     orangeom.optimizeAnchorsR][self.restrain]
379        ai = self.graph.attributeNameIndex
380        attr_indices = [ai[label] for label in self.get_shown_attribute_list()]
381        if not attr_indices: return
382
383        # repeat until less than 1% energy decrease in 5 consecutive iterations*steps steps
384        positions = [numpy.array([x[:2] for x in self.graph.anchorData])]
385        needed_steps = 0
386
387        valid_data = self.graph.getValidList(attr_indices)
388        n_valid = sum(valid_data) 
389        if not n_valid:
390            return 0
391
392        data = numpy.compress(valid_data, self.graph.noJitteringScaledData,
393                              axis=1)
394        data = numpy.transpose(data).tolist()
395        if self.__class__ != FreeViz: from PyQt4.QtGui import qApp
396
397        if distances:
398            if n_valid != len(valid_data):
399                classes = Orange.misc.SymMatrix(n_valid)
400                r = 0
401                for ro, vr in enumerate(valid_data):
402                    if not vr:
403                        continue
404                    c = 0
405                    for co, vr in enumerate(valid_data):
406                        if vr:
407                            classes[r, c] = distances[ro, co]
408                            c += 1
409                    r += 1 
410            else:
411                classes = distances
412        else:
413            classes = numpy.compress(valid_data,
414                                     self.graph.originalData[self.graph.dataClassIndex]).tolist()
415        while 1:
416            self.graph.anchorData = optimizer(data, classes,
417                                              self.graph.anchorData,
418                                              attr_indices,
419                                              attractG = self.attract_g,
420                                              repelG = self.repel_g,
421                                              law = self.law,
422                                              sigma2 = self.force_sigma,
423                                              dynamicBalancing = self.force_balancing,
424                                              steps = steps,
425                                              normalizeExamples = self.graph.normalizeExamples,
426                                              contClass = 2 if distances
427                                              else self.graph.dataHasContinuousClass,
428                                              mirrorSymmetry = self.mirror_symmetry)
429            needed_steps += steps
430
431            if self.__class__ != FreeViz:
432                qApp.processEvents()
433
434            if hasattr(self.graph, "updateData"):
435                self.graph.potentialsBmp = None
436                self.graph.updateData()
437
438            positions = positions[-49:]+[numpy.array([x[:2] for x
439                                                      in self.graph.anchorData])]
440            if len(positions)==50:
441                m = max(numpy.sum((positions[0]-positions[49])**2), 0)
442                if m < 1e-3: break
443            if single_step or (self.__class__ != FreeViz
444                               and self.cancel_optimization):
445                break
446        return needed_steps
447
448    optimize_FAST_Separation = optimize_fast_separation
449
450    @deprecated_keywords({"attrIndices": "attr_indices",
451                          "anchorData": "anchor_data",
452                          "XAnchors": "xanchors",
453                          "YAnchors": "yanchors"})
454    def optimize_lda_separation(self, attr_indices, anchor_data, xanchors = None, yanchors = None):
455        if (not self.graph.haveData or len(self.graph.rawData) == 0
456            or not self.graph.dataHasDiscreteClass): 
457            return anchor_data, (xanchors, yanchors)
458        class_count = len(self.graph.dataDomain.classVar.values)
459        valid_data = self.graph.getValidList(attr_indices)
460        selected_data = numpy.compress(valid_data,
461                                       numpy.take(self.graph.noJitteringScaledData,
462                                                  attr_indices, axis = 0),
463                                       axis = 1)
464
465        if xanchors == None:
466            xanchors = numpy.array([a[0] for a in anchor_data], numpy.float)
467        if yanchors == None:
468            yanchors = numpy.array([a[1] for a in anchor_data], numpy.float)
469
470        trans_proj_data = self.graph.createProjectionAsNumericArray(attr_indices,
471                                                                    valid_data = valid_data,
472                                                                    xanchors = xanchors,
473                                                                    yanchors = yanchors,
474                                                                    scaleFactor = self.graph.scaleFactor,
475                                                                    normalize = self.graph.normalizeExamples,
476                                                                    useAnchorData = 1)
477        if trans_proj_data == None:
478            return anchor_data, (xanchors, yanchors)
479
480        proj_data = numpy.transpose(trans_proj_data)
481        x_positions, y_positions, classData = (proj_data[0], proj_data[1],
482                                               proj_data[2])
483
484        averages = []
485        for i in range(class_count):
486            ind = classData == i
487            xpos = numpy.compress(ind, x_positions)
488            ypos = numpy.compress(ind, y_positions)
489            xave = numpy.sum(xpos)/len(xpos)
490            yave = numpy.sum(ypos)/len(ypos)
491            averages.append((xave, yave))
492
493        # compute the positions of all the points. we will try to move all points so that the center will be in the (0,0)
494        x_center_vector = -numpy.sum(x_positions) / len(x_positions)
495        y_center_vector = -numpy.sum(y_positions) / len(y_positions)
496        center_vector_length = math.sqrt(x_center_vector*x_center_vector +
497                                         y_center_vector*y_center_vector)
498
499        mean_destination_vectors = []
500
501        for i in range(class_count):
502            xdir = 0.0; ydir = 0.0; rs = 0.0
503            for j in range(class_count):
504                if i==j: continue
505                r = math.sqrt((averages[i][0] - averages[j][0])**2 +
506                              (averages[i][1] - averages[j][1])**2)
507                if r == 0.0:
508                    xdir += math.cos((i/float(class_count))*2*math.pi)
509                    ydir += math.sin((i/float(class_count))*2*math.pi)
510                    r = 0.0001
511                else:
512                    xdir += (1/r**3) * ((averages[i][0] - averages[j][0]))
513                    ydir += (1/r**3) * ((averages[i][1] - averages[j][1]))
514                #rs += 1/r
515            #actualDirAmpl = math.sqrt(xDir**2 + yDir**2)
516            #s = abs(xDir)+abs(yDir)
517            #xDir = rs * (xDir/s)
518            #yDir = rs * (yDir/s)
519            mean_destination_vectors.append((xdir, ydir))
520
521
522        maxlength = math.sqrt(max([x**2 + y**2 for (x,y)
523                                   in mean_destination_vectors]))
524        mean_destination_vectors = [(x/(2*maxlength), y/(2*maxlength)) for (x,y)
525                                    in mean_destination_vectors]     # normalize destination vectors to some normal values
526        mean_destination_vectors = [(mean_destination_vectors[i][0]+averages[i][0],
527                                     mean_destination_vectors[i][1]+averages[i][1])
528                                    for i in range(len(mean_destination_vectors))]    # add destination vectors to the class averages
529        #mean_destination_vectors = [(x + x_center_vector/5, y + y_center_vector/5) for (x,y) in mean_destination_vectors]   # center mean values
530        mean_destination_vectors = [(x + x_center_vector, y + y_center_vector)
531                                    for (x,y) in mean_destination_vectors]   # center mean values
532
533        fxs = numpy.zeros(len(x_positions), numpy.float)        # forces
534        fys = numpy.zeros(len(x_positions), numpy.float)
535
536        for c in range(class_count):
537            ind = (classData == c)
538            numpy.putmask(fxs, ind, mean_destination_vectors[c][0]-x_positions)
539            numpy.putmask(fys, ind, mean_destination_vectors[c][1]-y_positions)
540
541        # compute gradient for all anchors
542        gxs = numpy.array([sum(fxs * selected_data[i])
543                           for i in range(len(anchor_data))], numpy.float)
544        gys = numpy.array([sum(fys * selected_data[i])
545                           for i in range(len(anchor_data))], numpy.float)
546
547        m = max(max(abs(gxs)), max(abs(gys)))
548        gxs /= (20*m); gys /= (20*m)
549
550        newxanchors = xanchors + gxs
551        newyanchors = yanchors + gys
552
553        # normalize so that the anchor most far away will lie on the circle
554        m = math.sqrt(max(newxanchors**2 + newyanchors**2))
555        newxanchors /= m
556        newyanchors /= m
557
558        #self.parentWidget.updateGraph()
559
560        """
561        for a in range(len(anchor_data)):
562            x = anchor_data[a][0]; y = anchor_data[a][1];
563            self.parentWidget.graph.addCurve("lll%i" % i, QColor(0, 0, 0), QColor(0, 0, 0), 10, style = QwtPlotCurve.Lines, symbol = QwtSymbol.NoSymbol, xData = [x, x+gxs[a]], yData = [y, y+gys[a]], forceFilledSymbols = 1, lineWidth=3)
564
565        for i in range(class_count):
566            self.parentWidget.graph.addCurve("lll%i" % i, QColor(0, 0, 0), QColor(0, 0, 0), 10, style = QwtPlotCurve.Lines, symbol = QwtSymbol.NoSymbol, xData = [averages[i][0], mean_destination_vectors[i][0]], yData = [averages[i][1], mean_destination_vectors[i][1]], forceFilledSymbols = 1, lineWidth=3)
567            self.parentWidget.graph.addCurve("lll%i" % i, QColor(0, 0, 0), QColor(0, 0, 0), 10, style = QwtPlotCurve.Lines, xData = [averages[i][0], averages[i][0]], yData = [averages[i][1], averages[i][1]], forceFilledSymbols = 1, lineWidth=5)
568        """
569        #self.parentWidget.graph.repaint()
570        #self.graph.anchor_data = [(newxanchors[i], newyanchors[i], anchor_data[i][2]) for i in range(len(anchor_data))]
571        #self.graph.updateData(attrs, 0)
572        return [(newxanchors[i], newyanchors[i], anchor_data[i][2])
573                for i in range(len(anchor_data))], (newxanchors, newyanchors)
574
575    optimize_LDA_Separation = optimize_lda_separation
576
577    @deprecated_keywords({"attrIndices": "attr_indices",
578                          "anchorData": "anchor_data",
579                          "XAnchors": "xanchors",
580                          "YAnchors": "yanchors"})
581    def optimize_slow_separation(self, attr_indices, anchor_data, xanchors = None, yanchors = None):
582        if (not self.graph.haveData or len(self.graph.rawData) == 0
583            or not self.graph.dataHasDiscreteClass): 
584            return anchor_data, (xanchors, yanchors)
585        valid_data = self.graph.getValidList(attr_indices)
586        selected_data = numpy.compress(valid_data, numpy.take(self.graph.noJitteringScaledData,
587                                                              attr_indices,
588                                                              axis = 0),
589                                       axis = 1)
590
591        if xanchors == None:
592            xanchors = numpy.array([a[0] for a in anchor_data], numpy.float)
593        if yanchors == None:
594            yanchors = numpy.array([a[1] for a in anchor_data], numpy.float)
595
596        trans_proj_data = self.graph.createProjectionAsNumericArray(attr_indices,
597                                                                    valid_data = valid_data,
598                                                                    xanchors = xanchors,
599                                                                    yanchors = yanchors,
600                                                                    scaleFactor = self.graph.scaleFactor,
601                                                                    normalize = self.graph.normalizeExamples,
602                                                                    useAnchorData = 1)
603        if trans_proj_data == None:
604            return anchor_data, (xanchors, yanchors)
605
606        proj_data = numpy.transpose(trans_proj_data)
607        x_positions = proj_data[0]; x_positions2 = numpy.array(x_positions)
608        y_positions = proj_data[1]; y_positions2 = numpy.array(y_positions)
609        class_data = proj_data[2]  ; class_data2 = numpy.array(class_data)
610
611        fxs = numpy.zeros(len(x_positions), numpy.float)        # forces
612        fys = numpy.zeros(len(x_positions), numpy.float)
613        gxs = numpy.zeros(len(anchor_data), numpy.float)        # gradients
614        gys = numpy.zeros(len(anchor_data), numpy.float)
615
616        rotate_array = range(len(x_positions))
617        rotate_array = rotate_array[1:] + [0]
618        for i in range(len(x_positions)-1):
619            x_positions2 = numpy.take(x_positions2, rotate_array)
620            y_positions2 = numpy.take(y_positions2, rotate_array)
621            class_data2 = numpy.take(class_data2, rotate_array)
622            dx = x_positions2 - x_positions
623            dy = y_positions2 - y_positions
624            rs2 = dx**2 + dy**2
625            rs2 += numpy.where(rs2 == 0.0, 0.0001, 0.0)    # replace zeros to avoid divisions by zero
626            rs = numpy.sqrt(rs2)
627
628            F = numpy.zeros(len(x_positions), numpy.float)
629            classDiff = numpy.where(class_data == class_data2, 1, 0)
630            numpy.putmask(F, classDiff, 150*self.attract_g*rs2)
631            numpy.putmask(F, 1-classDiff, -self.repel_g/rs2)
632            fxs += F * dx / rs
633            fys += F * dy / rs
634
635        # compute gradient for all anchors
636        gxs = numpy.array([sum(fxs * selected_data[i])
637                           for i in range(len(anchor_data))], numpy.float)
638        gys = numpy.array([sum(fys * selected_data[i])
639                           for i in range(len(anchor_data))], numpy.float)
640
641        m = max(max(abs(gxs)), max(abs(gys)))
642        gxs /= (20*m); gys /= (20*m)
643
644        newxanchors = xanchors + gxs
645        newyanchors = yanchors + gys
646
647        # normalize so that the anchor most far away will lie on the circle
648        m = math.sqrt(max(newxanchors**2 + newyanchors**2))
649        newxanchors /= m
650        newyanchors /= m
651        return [(newxanchors[i], newyanchors[i], anchor_data[i][2])
652                for i in range(len(anchor_data))], (newxanchors, newyanchors)
653
654    optimize_SLOW_Separation = optimize_slow_separation
655
656
657    @deprecated_keywords({"attrIndices": "attr_indices",
658                          "anchorData": "anchor_data",
659                          "XAnchors": "xanchors",
660                          "YAnchors": "yanchors"})
661    def optimize_lda_separation_3D(self, attr_indices, anchor_data, xanchors = None, yanchors = None, zanchors = None):
662        if (not self.graph.haveData or len(self.graph.rawData) == 0
663            or not self.graph.dataHasDiscreteClass): 
664            return anchor_data, (xanchors, yanchors, zanchors)
665        class_count = len(self.graph.dataDomain.classVar.values)
666        valid_data = self.graph.getValidList(attr_indices)
667        selected_data = numpy.compress(valid_data,
668                                       numpy.take(self.graph.noJitteringScaledData,
669                                                  attr_indices, axis = 0),
670                                       axis = 1)
671
672        if xanchors == None:
673            xanchors = numpy.array([a[0] for a in anchor_data], numpy.float)
674        if yanchors == None:
675            yanchors = numpy.array([a[1] for a in anchor_data], numpy.float)
676        if zanchors == None:
677            zanchors = numpy.array([a[2] for a in anchor_data], numpy.float)
678
679        trans_proj_data = self.graph.createProjectionAsNumericArray(attr_indices,
680                                                                    valid_data = valid_data,
681                                                                    xanchors = xanchors,
682                                                                    yanchors = yanchors,
683                                                                    zanchors = zanchors,
684                                                                    scaleFactor = self.graph.scaleFactor,
685                                                                    normalize = self.graph.normalizeExamples,
686                                                                    useAnchorData = 1)
687        if trans_proj_data == None:
688            return anchor_data, (xanchors, yanchors, zanchors)
689
690        proj_data = numpy.transpose(trans_proj_data)
691        x_positions, y_positions, z_positions, classData = (proj_data[0],
692                                                            proj_data[1],
693                                                            proj_data[2],
694                                                            proj_data[3])
695
696        averages = []
697        for i in range(class_count):
698            ind = classData == i
699            xpos = numpy.compress(ind, x_positions)
700            ypos = numpy.compress(ind, y_positions)
701            zpos = numpy.compress(ind, z_positions)
702            xave = numpy.sum(xpos)/len(xpos)
703            yave = numpy.sum(ypos)/len(ypos)
704            zave = numpy.sum(zpos)/len(zpos)
705            averages.append((xave, yave, zave))
706
707        # compute the positions of all the points. we will try to move all points so that the center will be in the (0,0)
708        x_center_vector = -numpy.sum(x_positions) / len(x_positions)
709        y_center_vector = -numpy.sum(y_positions) / len(y_positions)
710        z_center_vector = -numpy.sum(z_positions) / len(z_positions)
711        center_vector_length = math.sqrt(x_center_vector*x_center_vector +
712                                         y_center_vector*y_center_vector +
713                                         z_center_vector*z_center_vector)
714
715        mean_destination_vectors = []
716
717        for i in range(class_count):
718            xdir = 0.0; ydir = 0.0; zdir = 0.0; rs = 0.0
719            for j in range(class_count):
720                if i==j: continue
721                r = math.sqrt((averages[i][0] - averages[j][0])**2 +
722                              (averages[i][1] - averages[j][1])**2)
723                if r == 0.0:
724                    xdir += math.cos((i/float(class_count))*2*math.pi)
725                    ydir += math.sin((i/float(class_count))*2*math.pi)
726                    r = 0.0001
727                else:
728                    xdir += (1/r**3) * ((averages[i][0] - averages[j][0]))
729                    ydir += (1/r**3) * ((averages[i][1] - averages[j][1]))
730                #rs += 1/r
731            #actualDirAmpl = math.sqrt(xDir**2 + yDir**2)
732            #s = abs(xDir)+abs(yDir)
733            #xDir = rs * (xDir/s)
734            #yDir = rs * (yDir/s)
735            mean_destination_vectors.append((xdir, ydir))
736
737
738        maxlength = math.sqrt(max([x**2 + y**2 for (x,y)
739                                   in mean_destination_vectors]))
740        mean_destination_vectors = [(x/(2*maxlength), y/(2*maxlength)) for (x,y)
741                                    in mean_destination_vectors]     # normalize destination vectors to some normal values
742        mean_destination_vectors = [(mean_destination_vectors[i][0]+averages[i][0],
743                                     mean_destination_vectors[i][1]+averages[i][1])
744                                    for i in range(len(mean_destination_vectors))]    # add destination vectors to the class averages
745        #mean_destination_vectors = [(x + x_center_vector/5, y + y_center_vector/5) for (x,y) in mean_destination_vectors]   # center mean values
746        mean_destination_vectors = [(x + x_center_vector, y + y_center_vector)
747                                    for (x,y) in mean_destination_vectors]   # center mean values
748
749        fxs = numpy.zeros(len(x_positions), numpy.float)        # forces
750        fys = numpy.zeros(len(x_positions), numpy.float)
751
752        for c in range(class_count):
753            ind = (classData == c)
754            numpy.putmask(fxs, ind, mean_destination_vectors[c][0]-x_positions)
755            numpy.putmask(fys, ind, mean_destination_vectors[c][1]-y_positions)
756
757        # compute gradient for all anchors
758        gxs = numpy.array([sum(fxs * selected_data[i])
759                           for i in range(len(anchor_data))], numpy.float)
760        gys = numpy.array([sum(fys * selected_data[i])
761                           for i in range(len(anchor_data))], numpy.float)
762
763        m = max(max(abs(gxs)), max(abs(gys)))
764        gxs /= (20*m); gys /= (20*m)
765
766        newxanchors = xanchors + gxs
767        newyanchors = yanchors + gys
768
769        # normalize so that the anchor most far away will lie on the circle
770        m = math.sqrt(max(newxanchors**2 + newyanchors**2))
771        newxanchors /= m
772        newyanchors /= m
773
774        #self.parentWidget.updateGraph()
775
776        """
777        for a in range(len(anchor_data)):
778            x = anchor_data[a][0]; y = anchor_data[a][1];
779            self.parentWidget.graph.addCurve("lll%i" % i, QColor(0, 0, 0), QColor(0, 0, 0), 10, style = QwtPlotCurve.Lines, symbol = QwtSymbol.NoSymbol, xData = [x, x+gxs[a]], yData = [y, y+gys[a]], forceFilledSymbols = 1, lineWidth=3)
780
781        for i in range(class_count):
782            self.parentWidget.graph.addCurve("lll%i" % i, QColor(0, 0, 0), QColor(0, 0, 0), 10, style = QwtPlotCurve.Lines, symbol = QwtSymbol.NoSymbol, xData = [averages[i][0], mean_destination_vectors[i][0]], yData = [averages[i][1], mean_destination_vectors[i][1]], forceFilledSymbols = 1, lineWidth=3)
783            self.parentWidget.graph.addCurve("lll%i" % i, QColor(0, 0, 0), QColor(0, 0, 0), 10, style = QwtPlotCurve.Lines, xData = [averages[i][0], averages[i][0]], yData = [averages[i][1], averages[i][1]], forceFilledSymbols = 1, lineWidth=5)
784        """
785        #self.parentWidget.graph.repaint()
786        #self.graph.anchor_data = [(newxanchors[i], newyanchors[i], anchor_data[i][2]) for i in range(len(anchor_data))]
787        #self.graph.updateData(attrs, 0)
788        return [(newxanchors[i], newyanchors[i], anchor_data[i][2])
789                for i in range(len(anchor_data))], (newxanchors, newyanchors)
790
791    optimize_LDA_Separation_3D = optimize_lda_separation_3D
792
793    @deprecated_keywords({"attrIndices": "attr_indices",
794                          "anchorData": "anchor_data",
795                          "XAnchors": "xanchors",
796                          "YAnchors": "yanchors"})
797    def optimize_slow_separation_3D(self, attr_indices, anchor_data, xanchors = None, yanchors = None, zanchors = None):
798        if (not self.graph.haveData or len(self.graph.rawData) == 0
799            or not self.graph.dataHasDiscreteClass): 
800            return anchor_data, (xanchors, yanchors, zanchors)
801        valid_data = self.graph.getValidList(attr_indices)
802        selected_data = numpy.compress(valid_data, numpy.take(self.graph.noJitteringScaledData,
803                                                              attr_indices,
804                                                              axis = 0),
805                                       axis = 1)
806
807        if xanchors == None:
808            xanchors = numpy.array([a[0] for a in anchor_data], numpy.float)
809        if yanchors == None:
810            yanchors = numpy.array([a[1] for a in anchor_data], numpy.float)
811        if zanchors == None:
812            zanchors = numpy.array([a[2] for a in anchor_data], numpy.float)
813
814        trans_proj_data = self.graph.createProjectionAsNumericArray(attr_indices,
815                                                                    valid_data = valid_data,
816                                                                    XAnchors = xanchors,
817                                                                    YAnchors = yanchors,
818                                                                    ZAnchors = zanchors,
819                                                                    scaleFactor = self.graph.scaleFactor,
820                                                                    normalize = self.graph.normalizeExamples,
821                                                                    useAnchorData = 1)
822        if trans_proj_data == None:
823            return anchor_data, (xanchors, yanchors, zanchors)
824
825        proj_data = numpy.transpose(trans_proj_data)
826        x_positions = proj_data[0]; x_positions2 = numpy.array(x_positions)
827        y_positions = proj_data[1]; y_positions2 = numpy.array(y_positions)
828        z_positions = proj_data[2]; z_positions2 = numpy.array(z_positions)
829        class_data = proj_data[3];  class_data2 = numpy.array(class_data)
830
831        fxs = numpy.zeros(len(x_positions), numpy.float)        # forces
832        fys = numpy.zeros(len(x_positions), numpy.float)
833        fzs = numpy.zeros(len(x_positions), numpy.float)
834        gxs = numpy.zeros(len(anchor_data), numpy.float)        # gradients
835        gys = numpy.zeros(len(anchor_data), numpy.float)
836        gzs = numpy.zeros(len(anchor_data), numpy.float)
837
838        rotate_array = range(len(x_positions))
839        rotate_array = rotate_array[1:] + [0]
840        for i in range(len(x_positions)-1):
841            x_positions2 = numpy.take(x_positions2, rotate_array)
842            y_positions2 = numpy.take(y_positions2, rotate_array)
843            z_positions2 = numpy.take(z_positions2, rotate_array)
844            class_data2 = numpy.take(class_data2, rotate_array)
845            dx = x_positions2 - x_positions
846            dy = y_positions2 - y_positions
847            dz = z_positions2 - z_positions
848            rs2 = dx**2 + dy**2 + dz**2
849            rs2 += numpy.where(rs2 == 0.0, 0.0001, 0.0)    # replace zeros to avoid divisions by zero
850            rs = numpy.sqrt(rs2)
851
852            F = numpy.zeros(len(x_positions), numpy.float)
853            classDiff = numpy.where(class_data == class_data2, 1, 0)
854            numpy.putmask(F, classDiff, 150*self.attract_g*rs2)
855            numpy.putmask(F, 1-classDiff, -self.repel_g/rs2)
856            fxs += F * dx / rs
857            fys += F * dy / rs
858            fzs += F * dz / rs
859
860        # compute gradient for all anchors
861        gxs = numpy.array([sum(fxs * selected_data[i])
862                           for i in range(len(anchor_data))], numpy.float)
863        gys = numpy.array([sum(fys * selected_data[i])
864                           for i in range(len(anchor_data))], numpy.float)
865        gzs = numpy.array([sum(fzs * selected_data[i])
866                           for i in range(len(anchor_data))], numpy.float)
867
868        m = max(max(abs(gxs)), max(abs(gys)), max(abs(gzs)))
869        gxs /= (20*m)
870        gys /= (20*m)
871        gzs /= (20*m)
872
873        newxanchors = xanchors + gxs
874        newyanchors = yanchors + gys
875        newzanchors = zanchors + gzs
876
877        # normalize so that the anchor most far away will lie on the circle
878        m = math.sqrt(max(newxanchors**2 + newyanchors**2 + newzanchors**2))
879        newxanchors /= m
880        newyanchors /= m
881        newzanchors /= m
882        return [(newxanchors[i], newyanchors[i], newzanchors[i], anchor_data[i][3])
883                for i in range(len(anchor_data))], (newxanchors, newyanchors, newzanchors)
884
885    optimize_SLOW_Separation_3D = optimize_slow_separation_3D
886
887
888
889    # ###############################################################
890    # S2N HEURISTIC FUNCTIONS
891    # ###############################################################
892
893
894
895    # place a subset of attributes around the circle. this subset must contain "good" attributes for each of the class values
896    @deprecated_keywords({"setAttributeListInRadviz":
897                          "set_attribute_list_in_radviz"})
898    def s2n_mix_anchors(self, set_attribute_list_in_radviz = 1):
899        # check if we have data and a discrete class
900        if (not self.graph.haveData or len(self.graph.rawData) == 0
901            or not self.graph.dataHasDiscreteClass): 
902            self.set_statusbar_text("S2N only works on data with a discrete class value")
903            return
904
905        # compute the quality of attributes only once
906        if self.s2n_mix_data == None:
907            ranked_attrs, ranked_attrs_by_class = visfuncts.findAttributeGroupsForRadviz(self.graph.rawData,
908                                                                                         visfuncts.S2NMeasureMix())
909            self.s2n_mix_data = (ranked_attrs, ranked_attrs_by_class)
910            class_count = len(ranked_attrs_by_class)
911            attrs = ranked_attrs[:(self.s2n_place_attributes/class_count)*
912                                 class_count]    # select appropriate number of attributes
913        else:
914            class_count = len(self.s2n_mix_data[1])
915            attrs = self.s2n_mix_data[0][:(self.s2n_place_attributes/class_count)*
916                                         class_count]
917
918        if len(attrs) == 0:
919            self.set_statusbar_text("No discrete attributes found")
920            return 0
921
922        arr = [0]       # array that will tell where to put the next attribute
923        for i in range(1,len(attrs)/2): arr += [i,-i]
924
925        phi = (2*math.pi*self.s2n_spread)/(len(attrs)*10.0)
926        anchor_data = []; start = []
927        arr2 = arr[:(len(attrs)/class_count)+1]
928        for cls in range(class_count):
929            start_pos = (2*math.pi*cls)/class_count
930            if self.class_permutation_list: cls = self.class_permutation_list[cls]
931            attrs_cls = attrs[cls::class_count]
932            temp_data = [(arr2[i], math.cos(start_pos + arr2[i]*phi),
933                          math.sin(start_pos + arr2[i]*phi),
934                          attrs_cls[i]) for i in
935                          range(min(len(arr2), len(attrs_cls)))]
936            start.append(len(anchor_data) + len(arr2)/2) # starting indices for each class value
937            temp_data.sort()
938            anchor_data += [(x, y, name) for (i, x, y, name) in temp_data]
939
940        anchor_data = anchor_data[(len(attrs)/(2*class_count)):] + anchor_data[:(len(attrs)/(2*class_count))]
941        self.graph.anchorData = anchor_data
942        attrNames = [anchor[2] for anchor in anchor_data]
943
944        if self.__class__ != FreeViz:
945            if set_attribute_list_in_radviz:
946                self.parentWidget.setShownAttributeList(attrNames)
947            self.graph.updateData(attrNames)
948            self.graph.repaint()
949        return 1
950
951    s2nMixAnchors = s2n_mix_anchors
952
953    # find interesting linear projection using PCA, SPCA, or PLS
954    @deprecated_keywords({"attrIndices": "attr_indices",
955                          "setAnchors": "set_anchors",
956                          "percentDataUsed": "percent_data_used"})
957    def find_projection(self, method, attr_indices = None, set_anchors = 0, percent_data_used = 100):
958        if not self.graph.haveData: return
959        ai = self.graph.attributeNameIndex
960        if attr_indices == None:
961            attributes = self.get_shown_attribute_list()
962            attr_indices = [ai[label] for label in attributes]
963        if len(attr_indices) == 0: return None
964
965        valid_data = self.graph.getValidList(attr_indices)
966        if sum(valid_data) == 0: return None
967
968        data_matrix = numpy.compress(valid_data, numpy.take(self.graph.noJitteringScaledData,
969                                                            attr_indices,
970                                                            axis = 0),
971                                     axis = 1)
972        if self.graph.dataHasClass:
973            class_array = numpy.compress(valid_data,
974                                         self.graph.noJitteringScaledData[self.graph.dataClassIndex])
975
976        if percent_data_used != 100:
977            indices = Orange.data.sample.SubsetIndices2(self.graph.rawData,
978                                                1.0-(float(percent_data_used)/100.0))
979            try:
980                data_matrix = numpy.compress(indices, data_matrix, axis = 1)
981            except:
982                pass
983            if self.graph.dataHasClass:
984                class_array = numpy.compress(indices, class_array)
985
986        ncomps = 3 if hasattr(self, '_use_3D') else 2
987        vectors = None
988        if method == DR_PCA:
989            vals, vectors = create_pca_projection(data_matrix, ncomps = ncomps,
990                                                  use_generalized_eigenvectors = self.use_generalized_eigenvectors)
991        elif method == DR_SPCA and self.graph.dataHasClass:
992            vals, vectors = create_pca_projection(data_matrix, class_array,
993                                                  ncomps = ncomps,
994                                                  use_generalized_eigenvectors = self.use_generalized_eigenvectors)
995        elif method == DR_PLS and self.graph.dataHasClass:
996            data_matrix = data_matrix.transpose()
997            class_matrix = numpy.transpose(numpy.matrix(class_array))
998            vectors = create_pls_projection(data_matrix, class_matrix, ncomps)
999            vectors = vectors.T
1000
1001        # test if all values are 0, if there is an invalid number in the array and if there are complex numbers in the array
1002        if (vectors == None or not vectors.any() or
1003            False in numpy.isfinite(vectors) or False in numpy.isreal(vectors)):
1004            self.set_statusbar_text("Unable to compute anchor positions for the selected attributes") 
1005            return None
1006
1007        xanchors = vectors[0]
1008        yanchors = vectors[1]
1009       
1010        if ncomps == 3:
1011            zanchors = vectors[2]
1012            m = math.sqrt(max(xanchors**2 + yanchors**2 + zanchors**2))
1013            zanchors /= m
1014        else:
1015            m = math.sqrt(max(xanchors**2 + yanchors**2))
1016
1017        xanchors /= m
1018        yanchors /= m
1019        names = self.graph.attributeNames
1020        attributes = [names[attr_indices[i]] for i in range(len(attr_indices))]
1021
1022        if set_anchors:
1023            if ncomps == 3:
1024                self.graph.setAnchors(list(xanchors), list(yanchors), list(zanchors), attributes)
1025            else:
1026                self.graph.setAnchors(list(xanchors), list(yanchors), attributes)
1027            self.graph.updateData()
1028            self.graph.repaint()
1029
1030        if ncomps == 3:
1031            return xanchors, yanchors, zanchors, (attributes, attr_indices)
1032        else:
1033            return xanchors, yanchors, (attributes, attr_indices)
1034
1035    findProjection = find_projection
1036
1037
1038FreeViz = deprecated_members({"attractG": "attract_g",
1039                              "repelG": "repel_g",
1040                              "forceBalancing": "force_balancing",
1041                              "forceSigma": "force_sigma",
1042                              "mirrorSymmetry": "mirror_symmetry",
1043                              "useGeneralizedEigenvectors": "use_generalized_eigenvectors",
1044                              "stepsBeforeUpdate": "steps_before_update",
1045                              "s2nSpread": "s2n_spread",
1046                              "s2nPlaceAttributes": "s2n_place_attributes",
1047                              "s2nMixData": "s2n_mix_data",
1048                              "autoSetParameters": "auto_set_parameters",
1049                              "classPermutationList": "class_permutation_list",
1050                              "attrsNum": "attrs_num",
1051                              "cancelOptimization": "cancel_optimization"})(FreeViz)
1052
1053
1054@deprecated_keywords({"X": "x", "Y": "y", "Ncomp": "ncomp"})
1055def create_pls_projection(x,y, ncomp = 2):
1056    '''Predict y from x using first ncomp principal components'''
1057
1058    # data dimensions
1059    n, mx = numpy.shape(x)
1060    my = numpy.shape(y)[1]
1061
1062    # Z-scores of original matrices
1063    ymean = y.mean()
1064    x,y = center(x), center(y)
1065
1066    p = numpy.empty((mx,ncomp))
1067    w = numpy.empty((mx,ncomp))
1068    c = numpy.empty((my,ncomp))
1069    t = numpy.empty((n,ncomp))
1070    u = numpy.empty((n,ncomp))
1071    b = numpy.zeros((ncomp,ncomp))
1072
1073    e,f = x,y
1074
1075    # main algorithm
1076    for i in range(ncomp):
1077
1078        u = numpy.random.random_sample((n,1))
1079        w = normalize(numpy.dot(e.T,u))
1080        t = normalize(numpy.dot(e,w))
1081        c = normalize(numpy.dot(f.T,t))
1082
1083        dif = t
1084        # iterations for loading vector t
1085        while numpy.linalg.norm(dif) > 10e-16:
1086            c = normalize(numpy.dot(f.T,t))
1087            u = numpy.dot(f,c)
1088            w = normalize(numpy.dot(e.T,u))
1089            t0 = normalize(numpy.dot(e,w))
1090            dif = t - t0
1091            t = t0
1092
1093        t[:,i] = t.T
1094        u[:,i] = u.T
1095        c[:,i] = c.T
1096        w[:,i] = w.T
1097
1098        b = numpy.dot(t.T,u)[0,0]
1099        b[i][i] = b
1100        p = numpy.dot(e.T,t)
1101        p[:,i] = p.T
1102        e = e - numpy.dot(t,p.T)
1103        xx = b * numpy.dot(t,c.T)
1104        f = f - xx
1105
1106    # esimated y
1107    #YE = numpy.dot(numpy.dot(t,b),c.t)*numpy.std(y, axis = 0) + ymean
1108    #y = y*numpy.std(y, axis = 0)+ ymean
1109    #BPls = numpy.dot(numpy.dot(numpy.linalg.pinv(p.t),b),c.t)
1110
1111    return w
1112
1113createPLSProjection = create_pls_projection
1114
1115# if no class data is provided we create PCA projection
1116# if there is class data then create SPCA projection
1117@deprecated_keywords({"dataMatrix": "data_matrix",
1118                      "classArray": "class_array",
1119                      "NComps": "ncomps",
1120                      "useGeneralizedEigenvectors": "use_generalized_eigenvectors"})
1121def create_pca_projection(data_matrix, class_array = None, ncomps = -1, use_generalized_eigenvectors = 1):
1122    if type(data_matrix) == numpy.ma.core.MaskedArray:
1123        data_matrix = numpy.array(data_matrix)
1124    if class_array != None and type(class_array) == numpy.ma.core.MaskedArray:
1125        class_array = numpy.array(class_array)
1126       
1127    data_matrix = numpy.transpose(data_matrix)
1128
1129    s = numpy.sum(data_matrix, axis=0)/float(len(data_matrix))
1130    data_matrix -= s       # substract average value to get zero mean
1131
1132    if class_array != None and use_generalized_eigenvectors:
1133        covarMatrix = numpy.dot(numpy.transpose(data_matrix), data_matrix)
1134        try:
1135            matrix = inv(covarMatrix)
1136        except:
1137            return None, None
1138        matrix = numpy.dot(matrix, numpy.transpose(data_matrix))
1139    else:
1140        matrix = numpy.transpose(data_matrix)
1141
1142    # compute dataMatrixT * L * dataMatrix
1143    if class_array != None:
1144        # define the Laplacian matrix
1145        l = numpy.zeros((len(data_matrix), len(data_matrix)))
1146        for i in range(len(data_matrix)):
1147            for j in range(i+1, len(data_matrix)):
1148                l[i,j] = -int(class_array[i] != class_array[j])
1149                l[j,i] = -int(class_array[i] != class_array[j])
1150
1151        s = numpy.sum(l, axis=0)      # doesn't matter which axis since the matrix l is symmetrical
1152        for i in range(len(data_matrix)):
1153            l[i,i] = -s[i]
1154
1155        matrix = numpy.dot(matrix, l)
1156
1157    matrix = numpy.dot(matrix, data_matrix)
1158
1159    vals, vectors = eig(matrix)
1160    if vals.dtype.kind == "c":       # if eigenvalues are complex numbers then do nothing
1161         return None, None
1162    vals = list(vals)
1163   
1164    if ncomps == -1:
1165        ncomps = len(vals)
1166    ncomps = min(ncomps, len(vals))
1167   
1168    ret_vals = []
1169    ret_indices = []
1170    for i in range(ncomps):
1171        ret_vals.append(max(vals))
1172        bestind = vals.index(max(vals))
1173        ret_indices.append(bestind)
1174        vals[bestind] = -1
1175   
1176    return ret_vals, numpy.take(vectors.T, ret_indices, axis = 0)         # i-th eigenvector is the i-th column in vectors so we have to transpose the array
1177
1178createPCAProjection = create_pca_projection
1179
1180
1181# #############################################################################
1182# class that represents freeviz classifier
1183class FreeVizClassifier(Orange.classification.Classifier):
1184    """
1185    A kNN classifier on the 2D projection of the data, optimized by FreeViz.
1186   
1187    Usually the learner
1188    (:class:`Orange.projection.linear.FreeVizLearner`) is used to construct the
1189    classifier.
1190   
1191    When constructing the classifier manually, the following parameters can
1192    be passed:
1193   
1194    :param data: table of data instances to project to a 2D plane and use for
1195        classification.
1196    :type data: :class:`Orange.data.Table`
1197   
1198    :param freeviz: the FreeViz algorithm instance to use to optimize the 2D
1199        projection.
1200    :type freeviz: :class:`Orange.projection.linear.FreeViz`
1201   
1202    """
1203   
1204    def __init__(self, data, freeviz):
1205        self.freeviz = freeviz
1206
1207        if self.freeviz.__class__ != FreeViz:
1208            self.freeviz.parentWidget.setData(data)
1209            self.freeviz.parentWidget.show_all_attributes = 1
1210        else:
1211            self.freeviz.graph.setData(data)
1212            self.freeviz.show_all_attributes()
1213
1214        #self.FreeViz.randomAnchors()
1215        self.freeviz.radial_anchors()
1216        self.freeviz.optimize_separation()
1217
1218        graph = self.freeviz.graph
1219        ai = graph.attributeNameIndex
1220        labels = [a[2] for a in graph.anchorData]
1221        indices = [ai[label] for label in labels]
1222
1223        valid_data = graph.getValidList(indices)
1224        domain = Orange.data.Domain([graph.dataDomain[i].name for i in indices]+
1225                               [graph.dataDomain.classVar.name],
1226                               graph.dataDomain)
1227        offsets = [graph.attrValues[graph.attributeNames[i]][0]
1228                   for i in indices]
1229        normalizers = [graph.getMinMaxVal(i) for i in indices]
1230        selected_data = numpy.take(graph.originalData, indices, axis = 0)
1231        averages = numpy.average(numpy.compress(valid_data, selected_data,
1232                                                axis=1), 1)
1233        class_data = numpy.compress(valid_data,
1234                                    graph.originalData[graph.dataClassIndex])
1235
1236        graph.createProjectionAsNumericArray(indices, useAnchorData = 1,
1237                                             removeMissingData = 0,
1238                                             valid_data = valid_data,
1239                                             jitterSize = -1)
1240        self.classifier = Orange.classification.knn.P2NN(domain,
1241                                      numpy.transpose(numpy.array([numpy.compress(valid_data,
1242                                                                                  graph.unscaled_x_positions),
1243                                                                   numpy.compress(valid_data,
1244                                                                                  graph.unscaled_y_positions),
1245                                                                   class_data])),
1246                                      graph.anchorData, offsets, normalizers,
1247                                      averages, graph.normalizeExamples, law=1)
1248
1249    # for a given instance run argumentation and find out to which class it most often fall
1250    @deprecated_keywords({"example": "instance", "returnType": "return_type"})
1251    def __call__(self, instance, return_type=Orange.classification.Classifier.GetValue):
1252        #instance.setclass(0)
1253        return self.classifier(instance, return_type)
1254
1255FreeVizClassifier = deprecated_members({"FreeViz":"freeviz"})(FreeVizClassifier)
1256
1257class FreeVizLearner(Orange.classification.Learner):
1258    """
1259    A learner that builds a :class:`FreeVizClassifier` on given data. An
1260    instance of :class:`FreeViz` can be passed to the constructor as a
1261    keyword argument :obj:`freeviz`.   
1262
1263    If data instances are provided to the constructor, the learning algorithm
1264    is called and the resulting classifier is returned instead of the learner.
1265   
1266    """
1267    def __new__(cls, freeviz = None, instances = None, weight_id = 0, **argkw):
1268        self = Orange.classification.Learner.__new__(cls, **argkw)
1269        if instances:
1270            self.__init__(freeviz, **argkw)
1271            return self.__call__(instances, weight_id)
1272        else:
1273            return self
1274
1275    def __init__(self, freeviz = None):
1276        if not freeviz:
1277            freeviz = FreeViz()
1278        self.freeviz = freeviz
1279        self.name = "freeviz Learner"
1280
1281    @deprecated_keywords({"examples": "instances", "weightID": "weight_id"})
1282    def __call__(self, instances, weight_id = 0):
1283        return FreeVizClassifier(instances, self.freeviz)
1284
1285FreeVizLearner = deprecated_members({"FreeViz":"freeviz"})(FreeVizLearner)
1286
1287
1288class S2NHeuristicLearner(Orange.classification.Learner):
1289    """
1290    This class is not documented yet.
1291   
1292    """
1293    def __new__(cls, freeviz = None, instances = None, weight_id = 0, **argkw):
1294        self = Orange.classification.Learner.__new__(cls, **argkw)
1295        if instances:
1296            self.__init__(freeviz, **argkw)
1297            return self.__call__(instances, weight_id)
1298        else:
1299            return self
1300
1301    def __init__(self, freeviz = None):
1302        if not freeviz:
1303            freeviz = FreeViz()
1304        self.freeviz = freeviz
1305        self.name = "S2N Feature Selection Learner"
1306
1307    @deprecated_keywords({"examples": "instances", "weightID": "weight_id"})
1308    def __call__(self, instances, weight_id = 0):
1309        return S2NHeuristicClassifier(instances, self.freeviz)
1310
1311S2NHeuristicLearner = deprecated_members({"FreeViz":
1312                                          "freeviz"})(S2NHeuristicLearner)
Note: See TracBrowser for help on using the repository browser.