source: orange/Orange/OrangeWidgets/Prototypes/OWPCA.py @ 10798:93c951f4cc33

Revision 10798:93c951f4cc33, 9.6 KB checked in by Ales Erjavec <ales.erjavec@…>, 2 years ago (diff)

Added PCA widget.

Line 
1"""
2<name>PCA</name>
3
4"""
5import Orange
6import Orange.utils.addons
7
8from OWWidget import *
9import OWGUI
10
11import Orange
12import Orange.projection.linear as plinear
13
14import numpy as np
15
16import sys
17from Orange import orangeqt
18
19sys.modules["orangeqt"] = orangeqt
20
21from plot.owplot import OWPlot
22from plot.owcurve import OWCurve
23from plot import owaxis
24
25class ScreePlot(OWPlot):
26    def __init__(self, parent=None, name="Scree Plot"):
27        OWPlot.__init__(self, parent, name=name)
28
29class OWPCA(OWWidget):
30    settingsList = ["standardize", "max_components", "variance_covered",
31                    "use_generalized_eigenvectors"]
32    def __init__(self, parent=None, signalManager=None, title="PCA"):
33        OWWidget.__init__(self, parent, signalManager, title)
34
35        self.inputs = [("Data", Orange.data.Table, self.set_data)]
36        self.outputs = [("Projected Data", Orange.data.Table, Default),
37                        ("PCA Projector", Orange.projection.linear.PcaProjector),
38                        ("Principal Vectors", Orange.data.Table)
39                        ]
40
41        self.standardize = True
42        self.use_variance_covered = 0
43        self.max_components = 0
44        self.variance_covered = 100.0
45        self.use_generalized_eigenvectors = False
46
47        self.loadSettings()
48
49        self.data = None
50
51        #####
52        # GUI
53        #####
54        grid = QGridLayout()
55        box = OWGUI.widgetBox(self.controlArea, "Settings",
56                              orientation=grid)
57        cb = OWGUI.checkBox(box, self, "standardize", "Standardize",
58                            tooltip="Standardize all input features.",
59                            callback=self.on_change, 
60                            addToLayout=False
61                            )
62        grid.addWidget(cb, 0, 0)
63
64#        OWGUI.radioButtonsInBox(box, self, "use_variance_covered", [],
65#                                callback=self.on_update)
66#        rb1 = OWGUI.appendRadioButton(box, self, "use_variance_covered",
67#                                      "Max components",
68#                                      tooltip="Select max components",
69#                                      callback=self.on_update,
70#                                      addToLayout=False
71#                                      )
72#        grid.addWidget(rb1, 1, 0)
73        label1 = QLabel("Max components", box)
74        grid.addWidget(label1, 1, 0)
75
76        sb1 = OWGUI.spin(box, self, "max_components", 0, 1000,
77                         tooltip="Maximum number of components",
78                         callback=self.on_change,
79                         addToLayout=False,
80                         keyboardTracking=False
81                         )
82        sb1.control.setSpecialValueText("All")
83        grid.addWidget(sb1.control, 1, 1)
84
85#        rb2 = OWGUI.appendRadioButton(box, self, "use_variance_covered",
86#                                      "Variance covered",
87#                                      tooltip="Percent of variance covered.",
88#                                      callback=self.on_update,
89#                                      addToLayout=False
90#                                      )
91#        grid.addWidget(rb2, 2, 0)
92        label2 = QLabel("Variance covered", box)
93        grid.addWidget(label2, 2, 0)
94
95        sb2 = OWGUI.doubleSpin(box, self, "variance_covered", 1.0, 100.0, 5.0,
96                               tooltip="Percent of variance covered.",
97                               callback=self.on_change,
98                               decimals=1,
99                               addToLayout=False,
100                               keyboardTracking=False
101                               )
102        sb2.control.setSuffix("%")
103        grid.addWidget(sb2.control, 2, 1)
104
105        cb = OWGUI.checkBox(box, self, "use_generalized_eigenvectors",
106                            "Use generalized eigenvectors",
107                            callback=self.on_change,
108                            addToLayout=False,
109                            )
110        grid.addWidget(cb, 3, 0, 1, 2)
111
112        OWGUI.rubber(self.controlArea)
113
114        self.scree_plot = ScreePlot(self)
115#        self.scree_plot.set_main_title("Scree Plot")
116#        self.scree_plot.set_show_main_title(True)
117        self.scree_plot.set_axis_title(owaxis.xBottom, "Principal Components")
118        self.scree_plot.set_show_axis_title(owaxis.xBottom, 1)
119        self.scree_plot.set_axis_title(owaxis.yLeft, "Proportion of Variance")
120        self.scree_plot.set_show_axis_title(owaxis.yLeft, 1)
121       
122        self.mainArea.layout().addWidget(self.scree_plot)
123
124        self.components = None
125        self.variances = None
126        self.variances_sum = None
127        self.projector_full = None
128
129        self.resize(800, 600)
130
131    def clear(self):
132        """Clear widget state
133        """
134        self.data = None
135        self.clear_cached()
136       
137    def clear_cached(self):
138        """Clear cached components
139        """
140        self.components = None
141        self.variances = None
142        self.variances_sum = None
143        self.projector_full = None
144
145    def set_data(self, data=None):
146        """Set the widget input data.
147        """
148        self.clear()
149        if data is not None:
150            self.data = data
151            self.on_change()
152
153    def on_change(self):
154        if self.data is None:
155            return
156        self.clear_cached()
157        self.apply()
158
159    def on_update(self):
160        if self.data is None:
161            return
162        self.update_components()
163
164    def construct_pca_all_comp(self):
165        pca = plinear.PCA(standardize=self.standardize,
166                          max_components=0,
167                          variance_covered=1,
168                          use_generalized_eigenvectors=self.use_generalized_eigenvectors
169                          )
170        return pca
171
172    def construct_pca(self):
173        max_components = self.max_components #if not self.use_variance_covered else 0
174        variance_covered = self.variance_covered #if self.use_variance_covered else 0
175        pca = plinear.PCA(standardize=self.standardize,
176                          max_components=max_components,
177                          variance_covered=variance_covered / 100.0,
178                          use_generalized_eigenvectors=self.use_generalized_eigenvectors
179                          )
180        return pca
181
182    def apply(self):
183        """Apply PCA in input data, caching the full projection,
184        then updating the selected components.
185       
186        """
187        pca = self.construct_pca_all_comp()
188        self.projector_full = projector = pca(self.data)
189        self.update_scree_plot()
190        self.update_components()
191
192    def update_components(self):
193        scale = self.projector_full.scale
194        center = self.projector_full.center
195        components = self.projector_full.projection
196        input_domain = self.projector_full.input_domain
197        variances = self.projector_full.variances
198        variance_sum = self.projector_full.variance_sum
199
200        # Get selected components (based on max_components and
201        # variance_coverd)
202        pca = self.construct_pca()
203        variances, components, variance_sum = pca._select_components(variances, components)
204
205        projector = plinear.PcaProjector(input_domain=input_domain,
206                                         standardize=self.standardize,
207                                         scale=scale,
208                                         center=center,
209                                         projection=components,
210                                         variances=variances,
211                                         variance_sum=variance_sum)
212        projected_data = projector(self.data)
213        eigenvectors = self.eigenvectors_as_table(components)
214
215        self.send("Projected Data", projected_data)
216        self.send("PCA Projector", projector)
217        self.send("Principal Vectors", eigenvectors)
218
219    def eigenvectors_as_table(self, U):
220        features = [Orange.feature.Continuous("C%i" % i) \
221                    for i in range(1, U.shape[1] + 1)]
222        domain = Orange.data.Domain(features, False)
223        return Orange.data.Table(domain, [list(v) for v in U])
224
225    def update_scree_plot(self):
226        variances = self.projector_full.variances
227        s = np.sum(variances)
228        cv = variances / s
229        cs = np.cumsum(cv)
230        x_space = np.arange(0, len(variances))
231        self.scree_plot.set_axis_enabled(owaxis.xBottom, True)
232        self.scree_plot.set_axis_enabled(owaxis.yLeft, True)
233        self.scree_plot.set_axis_labels(owaxis.xBottom, 
234                                        ["PC" + str(i + 1) for i in x_space])
235
236        self.c = self.scree_plot.add_curve("Variance",
237                        Qt.red, Qt.red, 2, 
238                        xData=x_space,
239                        yData=cv,
240                        style=OWCurve.Lines,
241                        enableLegend=True,
242                        lineWidth=2,
243                        autoScale=1,
244                        x_axis_key=owaxis.xBottom,
245                        y_axis_key=owaxis.yLeft,
246                        )
247       
248        self.c = self.scree_plot.add_curve("Cumulative Variance",
249                        Qt.darkYellow, Qt.darkYellow, 2, 
250                        xData=x_space,
251                        yData=cs,
252                        style=OWCurve.Lines,
253                        enableLegend=True,
254                        lineWidth=2,
255                        autoScale=1,
256                        x_axis_key=owaxis.xBottom,
257                        y_axis_key=owaxis.yLeft,
258                        )
259
260if __name__ == "__main__":
261    app = QApplication(sys.argv)
262    w = OWPCA()
263    data = Orange.data.Table("iris")
264    w.set_data(data)
265    w.show()
266    app.exec_()
Note: See TracBrowser for help on using the repository browser.