Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeWidgets/Prototypes/OWPCA.py

    r10801 r10809  
    4848            x, _  = self.map_from_graph(pos) 
    4949            xmin, xmax = self.x_scale() 
    50             if x >= xmin and x <= xmax: 
     50            if x >= xmin - 0.1 and x <= xmax + 0.1: 
     51                x = min(max(x, xmin), xmax) 
    5152                self.cutoff_curve.set_data([x, x], [0.0, 1.0]) 
    5253                self.emit_cutoff_moved(x) 
     
    5859            x, _ = self.map_from_graph(pos) 
    5960            xmin, xmax = self.x_scale() 
    60             if x >= xmin and x <= xmax: 
     61            if x >= xmin - 0.5 and x <= xmax + 0.5: 
     62                x = min(max(x, xmin), xmax) 
    6163                self.cutoff_curve.set_data([x, x], [0.0, 1.0]) 
    6264                self.emit_cutoff_moved(x) 
     
    6971        ax = self.axes[owaxis.xBottom] 
    7072        if ax.labels: 
    71             return 0, len(ax.labels) - 0.5 
     73            return 0, len(ax.labels) - 1 
    7274        elif ax.scale: 
    73             return ax.scale[0], ax.scale[1] + 0.5 
     75            return ax.scale[0], ax.scale[1] 
    7476        else: 
    7577            raise ValueError 
     
    9092class OWPCA(OWWidget): 
    9193    settingsList = ["standardize", "max_components", "variance_covered", 
    92                     "use_generalized_eigenvectors"] 
     94                    "use_generalized_eigenvectors", "auto_commit"] 
    9395    def __init__(self, parent=None, signalManager=None, title="PCA"): 
    94         OWWidget.__init__(self, parent, signalManager, title) 
     96        OWWidget.__init__(self, parent, signalManager, title, wantGraph=True) 
    9597 
    9698        self.inputs = [("Input Data", Orange.data.Table, self.set_data)] 
     
    102104        self.variance_covered = 100.0 
    103105        self.use_generalized_eigenvectors = False 
     106        self.auto_commit = False 
    104107 
    105108        self.loadSettings() 
    106109 
    107110        self.data = None 
     111        self.changed_flag = False 
    108112 
    109113        ##### 
     
    111115        ##### 
    112116        grid = QGridLayout() 
    113         box = OWGUI.widgetBox(self.controlArea, "Settings", 
     117        box = OWGUI.widgetBox(self.controlArea, "Components Selection", 
    114118                              orientation=grid) 
    115119 
     
    140144 
    141145        OWGUI.rubber(self.controlArea) 
     146 
     147        box = OWGUI.widgetBox(self.controlArea, "Commit") 
     148        cb = OWGUI.checkBox(box, self, "auto_commit", "Commit on any change") 
     149        b = OWGUI.button(box, self, "Commit", 
     150                         callback=self.update_components) 
     151        OWGUI.setStopper(self, b, cb, "changed_flag", self.update_components) 
    142152 
    143153        self.scree_plot = ScreePlot(self) 
     
    180190                     self.on_cutoff_moved 
    181191                     ) 
     192 
     193        self.connect(self.graphButton, 
     194                     SIGNAL("clicked()"), 
     195                     self.scree_plot.save_to_file) 
     196 
    182197        self.components = None 
    183198        self.variances = None 
    184199        self.variances_sum = None 
    185200        self.projector_full = None 
     201        self.currently_selected = 0 
    186202 
    187203        self.resize(800, 400) 
     
    203219        self.variances_cumsum = None 
    204220        self.projector_full = None 
     221        self.currently_selected = 0 
    205222 
    206223    def set_data(self, data=None): 
     
    211228            self.data = data 
    212229            self.on_change() 
     230        else: 
     231            self.send("Transformed Data", None) 
     232            self.send("Eigen Vectors", None) 
    213233 
    214234    def on_change(self): 
     235        """Data has changed and we need to recompute the projection. 
     236        """ 
    215237        if self.data is None: 
    216238            return 
     
    219241 
    220242    def on_update(self): 
     243        """Component selection was changed by the user. 
     244        """ 
    221245        if self.data is None: 
    222246            return 
    223247        self.update_cutoff_curve() 
    224         self.update_components() 
     248        if self.currently_selected != self.number_of_selected_components(): 
     249            self.update_components_if() 
    225250 
    226251    def construct_pca_all_comp(self): 
     
    243268 
    244269    def apply(self): 
    245         """Apply PCA in input data, caching the full projection, 
     270        """Apply PCA on input data, caching the full projection, 
    246271        then updating the selected components. 
    247272         
     
    256281        self.max_components_spin.setRange(1, len(self.variances)) 
    257282        self.update_scree_plot() 
    258         self.update_components() 
    259  
     283        self.update_cutoff_curve() 
     284        self.update_components_if() 
     285 
     286    def update_components_if(self): 
     287        if self.auto_commit: 
     288            self.update_components() 
     289        else: 
     290            self.changed_flag = True 
     291         
    260292    def update_components(self): 
     293        """Update the output components. 
     294        """ 
     295        if self.data is None: 
     296            return  
     297 
    261298        scale = self.projector_full.scale 
    262299        center = self.projector_full.center 
     
    281318        eigenvectors = self.eigenvectors_as_table(components) 
    282319 
     320        self.currently_selected = self.number_of_selected_components() 
     321 
    283322        self.send("Transformed Data", projected_data) 
    284323        self.send("Eigen Vectors", eigenvectors) 
     324 
     325        self.changed_flag = False 
    285326 
    286327    def eigenvectors_as_table(self, U): 
     
    291332 
    292333    def update_scree_plot(self): 
    293         variances = self.projector_full.variances 
    294         s = np.sum(variances) 
    295         cv = variances / s 
    296         cs = np.cumsum(cv) 
    297         x_space = np.arange(0, len(variances)) 
     334        x_space = np.arange(0, len(self.variances)) 
    298335        self.scree_plot.set_axis_enabled(owaxis.xBottom, True) 
    299336        self.scree_plot.set_axis_enabled(owaxis.yLeft, True) 
     
    301338                                        ["PC" + str(i + 1) for i in x_space]) 
    302339 
    303         self.variance_curve.set_data(x_space, cv) 
    304         self.cumulative_variance_curve.set_data(x_space, cs) 
     340        self.variance_curve.set_data(x_space, self.variances) 
     341        self.cumulative_variance_curve.set_data(x_space, self.variances_cumsum) 
    305342        self.variance_curve.setVisible(True) 
    306343        self.cumulative_variance_curve.setVisible(True) 
    307344 
    308345        self.scree_plot.set_cutoff_curve_enabled(True) 
     346        self.scree_plot.replot() 
    309347 
    310348    def on_cutoff_moved(self, value): 
     349        """Cutoff curve was moved by the user. 
     350        """ 
    311351        components = int(np.floor(value)) + 1 
    312         if components != self.max_components: 
    313             self.max_components = int(np.floor(value)) + 1 
    314             self.variance_covered = self.variances_cumsum[self.max_components - 1] * 100 
    315             self.update_components() 
     352        # Did the number of components actually change 
     353        self.max_components = components 
     354        self.variance_covered = self.variances_cumsum[components - 1] * 100 
     355        if self.currently_selected != self.number_of_selected_components(): 
     356            self.update_components_if() 
    316357 
    317358    def update_cutoff_curve(self): 
    318         """Update cutoff line from gui control elements. 
     359        """Update cutoff curve from 'Components Selection' control box. 
    319360        """ 
    320361        variance = self.variances_cumsum[self.max_components - 1] * 100.0 
     
    326367        self.scree_plot.set_cutoff_value(cutoff + 0.5) 
    327368 
     369    def number_of_selected_components(self): 
     370        """How many components are selected. 
     371        """ 
     372        if self.data is None: 
     373            return 0 
     374 
     375        variance_components = np.searchsorted(self.variances_cumsum, 
     376                                              self.variance_covered / 100.0) 
     377        return min(variance_components + 1, self.max_components) 
     378 
     379    def sendReport(self): 
     380        self.reportSettings("PCA Settings", 
     381                            [("Max. components", self.max_components), 
     382                             ("Variance covered", "%i%%" % self.variance_covered), 
     383                             ]) 
     384        if self.data is not None and self.projector_full: 
     385            output_domain = self.projector_full.output_domain 
     386            st_dev = np.sqrt(self.projector_full.variances) 
     387            summary = [[""] + [a.name for a in output_domain.attributes], 
     388                       ["Std. deviation"] + ["%.3f" % sd for sd in st_dev], 
     389                       ["Proportion Var"] + ["%.3f" % v for v in self.variances * 100.0], 
     390                       ["Cumulative Var"] + ["%.3f" % v for v in self.variances_cumsum * 100.0] 
     391                       ] 
     392 
     393            th = "<th>%s</th>".__mod__ 
     394            header = "".join(map(th, summary[0])) 
     395            td = "<td>%s</td>".__mod__ 
     396            summary = ["".join(map(td, row)) for row in summary[1:]] 
     397            tr = "<tr>%s</tr>".__mod__ 
     398            summary = "\n".join(map(tr, [header] + summary)) 
     399            summary = "<table>\n%s\n</table>" % summary 
     400 
     401            self.reportSection("Summary") 
     402            self.reportRaw(summary) 
     403 
     404            self.reportSection("Scree Plot") 
     405            self.reportImage(self.scree_plot.save_to_file_direct) 
    328406 
    329407if __name__ == "__main__": 
Note: See TracChangeset for help on using the changeset viewer.