Commits

Blaz Zupan  committed 25aa7c8 Merge

merge

  • Participants
  • Parent commits d5173ab, af723ff

Comments (0)

Files changed (273)

 84d3895ad2a55acb7e69173e22314a602c87a29f 2.6
 8adaabe1a15bdc88d3baa633042c02c1a7bc7fa9 2.6.1
 19b97395b702366db35cc58615e3d016ce1c9701 2.7
+b3d7eae5e2c49d74debd44c64a33490d330154c6 2.7.1
 Faculty of Computer and Information Science, University of Ljubljana,
 Slovenia, together with open source community.
 
+Orange is released under GNU GPL (see below). A subset (without GUI) is also
+available under the terms of BSD upon request (orange.biolab.si/contact).
+
+
 Orange is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
 Software Foundation; either version 3.0 of the License, or (at your

File Orange/OrangeCanvas/config.py

 """
 
 import os
+import sys
 import logging
 import cPickle as pickle
 import itertools
     return cachedir
 
 
+def log_dir():
+    """
+    Return the application log directory.
+    """
+    init()
+    if sys.platform == "darwin":
+        name = str(QCoreApplication.applicationName())
+        logdir = os.path.join(os.path.expanduser("~/Library/Logs"), name)
+    else:
+        logdir = data_dir()
+
+    if not os.path.exists(logdir):
+        os.makedirs(logdir)
+    return logdir
+
+
 def open_config():
     global rc
     app_dir = data_dir()

File Orange/OrangeCanvas/main.py

     # and write to the old file descriptors)
     fix_win_pythonw_std_stream()
 
-    logging.basicConfig(level=levels[options.log_level])
+    # File handler should always be at least INFO level so we need
+    # the application root level to be at least at INFO.
+    root_level = min(levels[options.log_level], logging.INFO)
+    rootlogger = logging.getLogger(OrangeCanvas.__name__)
+    rootlogger.setLevel(root_level)
+
+    # Standard output stream handler at the requested level
+    stream_hander = logging.StreamHandler()
+    stream_hander.setLevel(level=levels[options.log_level])
+    rootlogger.addHandler(stream_hander)
 
     log.info("Starting 'Orange Canvas' application.")
 
     log.debug("Starting CanvasApplicaiton with argv = %r.", qt_argv)
     app = CanvasApplication(qt_argv)
 
+    # NOTE: config.init() must be called after the QApplication constructor
+    config.init()
+
+    file_handler = logging.FileHandler(
+        filename=os.path.join(config.log_dir(), "canvas.log"),
+        mode="w"
+    )
+
+    file_handler.setLevel(root_level)
+    rootlogger.addHandler(file_handler)
+
     # intercept any QFileOpenEvent requests until the main window is
     # fully initialized.
     # NOTE: The QApplication must have the executable ($0) and filename
 
     app.fileOpenRequest.connect(onrequest)
 
-    # Note: config.init must be called after the QApplication constructor
-    config.init()
     settings = QSettings()
 
     stylesheet = options.stylesheet

File Orange/OrangeCanvas/orngDlgs.py

 
         self.busy(True)
         self.repaint()
+
+        addons = Orange.utils.addons.open_addons(flag="r")
+
         add, remove, upgrade = self.to_install(), self.to_remove(), self.to_upgrade
 
         def errormessage(title, message, details=None, exc_info=None):
 
             return box.exec_()
 
+        def subprocesswait(process):
+            output = []
+            while process.poll() is None:
+                try:
+                    line = process.stdout.readline()
+                except IOError as ex:
+                    if ex.errno != 4:
+                        raise
+                else:
+                    output.append(line)
+                    qApp.processEvents(QEventLoop.ExcludeUserInputEvents)
+                    print line,
+
+            if process.returncode:
+                output = "".join(output)
+                output += process.stdout.read()
+
+                errormessage("Error",
+                             "'easy_install' exited with error code %i" %
+                             process.returncode,
+                             details=output)
+            return process.returncode
+
+        def easy_install(req):
+            try:
+                process = Orange.utils.addons.easy_install_process([req])
+            except (OSError, IOError):
+                # TODO: Should show some usefull message (executable not
+                # found, permission error, ...
+                raise
+            else:
+                subprocesswait(process)
+
         for name in upgrade:
-            try:
-                self.busy("Upgrading %s ..." % name)
-                self.repaint()
-                Orange.utils.addons.upgrade(name, self.pcb)
-            except subprocess.CalledProcessError, ex:
-                errormessage("Error",
-                             "setup.py script exited with error code %i" \
-                             % ex.returncode,
-                             details=ex.output)
-            except Exception, e:
-                errormessage("Error",
-                             "Problem upgrading add-on %s: %s" % (name, e),
-                             exc_info=True)
+            req = "{0}=={1}".format(
+                name, addons[name.lower()].available_version)
+
+            self.busy("Upgrading %s ..." % name)
+            self.progress.setRange(0, 0)
+            self.repaint()
+
+            easy_install(req)
 
         for name in remove:
+            self.busy("Uninstalling %s ..." % name)
+            self.repaint()
             try:
-                self.busy("Uninstalling %s ..." % name)
-                self.repaint()
                 Orange.utils.addons.uninstall(name, self.pcb)
             except Exception, e:
                 errormessage("Error",
                              exc_info=True)
 
         for name in add:
-            try:
-                self.busy("Installing %s ..." % name)
-                self.repaint()
-                Orange.utils.addons.install(name, self.pcb)
-            except subprocess.CalledProcessError, ex:
-                errormessage("Error",
-                             "setup.py script exited with error code %i" \
-                             % ex.returncode,
-                             details=ex.output)
+            req = "{0}=={1}".format(
+                name, addons[name.lower()].available_version)
 
-            except Exception, e:
-                errormessage("Error",
-                             "Problem installing add-on %s: %s" % (name, e),
-                             exc_info=True)
+            self.busy("Installing %s ..." % name)
+            self.progress.setRange(0, 0)
+            self.repaint()
 
-        if len(upgrade) > 0:
-            QMessageBox.warning(self, "Restart Orange", "After upgrading add-ons, it is very important to restart Orange to make sure the changes have been applied.")
-        elif len(remove) > 0:  # Don't bother with this if there has already been one (more important) warning.
-            QMessageBox.warning(self, "Restart Orange", "After removal of add-ons, it is suggested that you restart Orange for the changes to become effective.")
+            easy_install(req)
+
+        if len(add) + len(upgrade) + len(remove) > 0:
+            QMessageBox.information(
+                self, "Restart Orange",
+                "Please restart Orange for changes to take effect.")
 
         QDialog.accept(self)
 

File Orange/OrangeCanvas/scheme/readwrite.py

             raise exc
 
     if desc.version == "1.0":
-        desc = resolve_1_0(desc, registry, error_handler)
+        desc = resolve_1_0(desc, registry)
 
     desc = resolve_replaced(desc, registry)
     nodes_not_found = []

File Orange/OrangeWidgets/Data/OWRank.py

 
 import Orange
 from Orange.feature import scoring
-from Orange.regression import earth
 from Orange.classification import svm
 from Orange.ensemble import forest
 
 
 
 MEASURE_PARAMS = {
-    earth.ScoreEarthImportance: [
-        {"name": "t",
-         "type": int,
-         "display_name": "Num. models.",
-         "range": (1, 20),
-         "default": 10,
-         "doc": "Number of models to train for feature scoring."},
-        {"name": "terms",
-         "type": int,
-         "display_name": "Max. num of terms",
-         "range": (3, 200),
-         "default": 10,
-         "doc": "Maximum number of terms in the forward pass"},
-        {"name": "degree",
-         "type": int,
-         "display_name": "Max. term degree",
-         "range": (1, 3),
-         "default": 2,
-         "doc": "Maximum degree of terms included in the model."}
-    ],
     scoring.Relief: [
         {"name": "k",
          "type": int,
     score_meta(
         "Random Forests", "RF", forest.ScoreFeature,
         params=MEASURE_PARAMS[forest.ScoreFeature]),
-    score_meta(
-        "Earth Importance", "Earth imp.", earth.ScoreEarthImportance,
-        params=MEASURE_PARAMS[earth.ScoreEarthImportance],
-    )
 ]
 
 _DEFAULT_SELECTED = set(m.name for m in SCORES[:6])

File Orange/OrangeWidgets/OWDatabasesUpdate.py

 import os
 import sys
 
-from datetime import datetime
+from datetime import datetime, timedelta
 from functools import partial
 from collections import namedtuple
 
 from OWConcurrent import Task, ThreadExecutor, methodinvoke
 
 import OWGUIEx
+import OWGUI
 
 
 #: Update file item states
     installClicked = Signal()
     #: Remove button was clicked.
     removeClicked = Signal()
-
+    
     def __init__(self, state=AVAILABLE, parent=None):
         QWidget.__init__(self, parent)
         layout = QHBoxLayout()
         layout.setSpacing(1)
         layout.setContentsMargins(1, 1, 1, 1)
-        self.installButton = UpdateOptionButton(self)
-        self.installButton.setIcon(icon("update.png"))
-        self.installButton.setToolTip("Download")
 
-        self.removeButton = UpdateOptionButton(self)
-        self.removeButton.setIcon(icon("delete.png"))
-        self.removeButton.setToolTip("Remove from system")
-
-        self.installButton.clicked.connect(self.installClicked)
-        self.removeButton.clicked.connect(self.removeClicked)
-
-        layout.addWidget(self.installButton)
-        layout.addWidget(self.removeButton)
+        self.checkButton = QCheckBox()
+         
+        layout.addWidget(self.checkButton)
         self.setLayout(layout)
-
+ 
         self.setMaximumHeight(30)
 
         self.state = -1
         self.setState(state)
-
+        
     def setState(self, state):
         """
         Set the current update state for the widget (AVAILABLE,
         if self.state != state:
             self.state = state
             self._update()
-
+ 
     def _update(self):
         if self.state == AVAILABLE:
-            self.installButton.setIcon(icon("update.png"))
-            self.installButton.setToolTip("Download")
-            self.installButton.setEnabled(True)
-            self.removeButton.setEnabled(False)
+            self.checkButton.setChecked(False)
         elif self.state == CURRENT:
-            self.installButton.setIcon(icon("update1.png"))
-            self.installButton.setToolTip("Update")
-            self.installButton.setEnabled(False)
-            self.removeButton.setEnabled(True)
+            self.checkButton.setChecked(True)
         elif self.state == OUTDATED:
-            self.installButton.setIcon(icon("update1.png"))
-            self.installButton.setToolTip("Update")
-            self.installButton.setEnabled(True)
-            self.removeButton.setEnabled(True)
+            self.checkButton.setChecked(True)
         elif self.state == DEPRECATED:
-            self.installButton.setIcon(icon("update.png"))
-            self.installButton.setToolTip("")
-            self.installButton.setEnabled(False)
-            self.removeButton.setEnabled(True)
+            self.checkButton.setChecked(True)
         else:
             raise ValueError("Invalid state %r" % self._state)
 
+        try:
+            self.checkButton.clicked.disconnect()   # Remove old signals if they exist
+        except:
+            pass
+
+        if not self.checkButton.isChecked():        # Switch signals if the file is present or not
+            self.checkButton.clicked.connect(self.installClicked)
+        else:
+            self.checkButton.clicked.connect(self.removeClicked)
+
 
 class UpdateTreeWidgetItem(QTreeWidgetItem):
     """
         self.setData(1, Qt.DisplayRole, item.title)
         self.setData(1, self.EditRole2, item.title)
 
-        self.setData(2, Qt.DisplayRole, sizeof_fmt(item.size))
-        self.setData(2, self.EditRole2, item.size)
+        self.setData(4, Qt.DisplayRole, sizeof_fmt(item.size))
+        self.setData(4, self.EditRole2, item.size)
 
-        if item.latest is not None:
-            self.setData(3, Qt.DisplayRole, item.latest.date().isoformat())
-            self.setData(3, self.EditRole2, item.latest)
+        if item.local is not None:
+            self.setData(3, Qt.DisplayRole, item.local.date().isoformat())
+            self.setData(3, self.EditRole2, item.local)
         else:
-            self.setData(3, Qt.DisplayRole, "N/A")
+            self.setData(3, Qt.DisplayRole, "")
             self.setData(3, self.EditRole2, datetime.now())
 
         self._updateToolTip()
 
     def _updateToolTip(self):
         state_str = self.STATE_STRINGS[self.item.state]
+        try:
+            diff_date = self.item.latest - self.item.local
+        except:
+            diff_date = None
+        
         tooltip = ("State: %s\nTags: %s" %
-                   (state_str,
-                    ", ".join(tag for tag in self.item.tags
-                              if not tag.startswith("#"))))
+                   (state_str, ", ".join(tag for tag in self.item.tags
+                    if not tag.startswith("#"))))
 
         if self.item.state in [CURRENT, OUTDATED, DEPRECATED]:
             tooltip += ("\nFile: %s" %
                         serverfiles.localpath(self.item.domain,
                                               self.item.filename))
+       
+        if self.item.state == 2 and diff_date:
+            tooltip += ("\nServer version: %s\nStatus: old (%d days)" % (self.item.latest, diff_date.days))
+        else:
+            tooltip += ("\nServer version: %s" % self.item.latest)
+
         for i in range(1, 4):
             self.setToolTip(i, tooltip)
 
 
         box = OWGUI.widgetBox(self.controlArea, "Files")
         self.filesView = QTreeWidget(self)
-        self.filesView.setHeaderLabels(["Options", "Title", "Size",
-                                        "Last Updated"])
+        self.filesView.setHeaderLabels(["", "Data Source", "Update",
+                                        "Last Updated", "Size"])
         self.filesView.setRootIsDecorated(False)
         self.filesView.setUniformRowHeights(True)
         self.filesView.setSelectionMode(QAbstractItemView.NoSelection)
         box.layout().addWidget(self.filesView)
 
         box = OWGUI.widgetBox(self.controlArea, orientation="horizontal")
-        OWGUI.button(box, self, "Update all local files",
+        self.updateButton = OWGUI.button(box, self, "Update all",
                      callback=self.UpdateAll,
-                     tooltip="Update all updatable files")
-        OWGUI.button(box, self, "Download filtered",
+                     tooltip="Update all updatable files",
+                     )
+        
+        self.downloadButton = OWGUI.button(box, self, "Download all",
                      callback=self.DownloadFiltered,
                      tooltip="Download all filtered files shown")
-        OWGUI.button(box, self, "Cancel", callback=self.Cancel,
+        self.cancelButton = OWGUI.button(box, self, "Cancel", callback=self.Cancel,
                      tooltip="Cancel scheduled downloads/updates.")
         OWGUI.rubber(box)
         OWGUI.lineEdit(box, self, "accessCode", "Access Code",
 
         self.setEnabled(False)
 
+
     def SetFilesList(self, serverInfo):
         """
         Set the files to show.
             options_widget = UpdateOptionsWidget(item.state)
             options_widget.item = item
 
-            # Connect the actions to the appropriate methods
             options_widget.installClicked.connect(
                 partial(self.SubmitDownloadTask, item.domain, item.filename)
             )
 
         for item, tree_item, options_widget in self.updateItems:
             self.filesView.setItemWidget(tree_item, 0, options_widget)
+            
+            # Add an update button if the file is updateable
+            if item.state == 2:
+                ButtonWidget = QPushButton("Update")
+                layout = QHBoxLayout()
+                layout.setSpacing(1)
+                layout.setContentsMargins(20, 30, 30, 30)
+
+                layout.addWidget(ButtonWidget)                 
+                ButtonWidget.setMaximumHeight(30)
+                ButtonWidget.setMaximumWidth(120)
+                ButtonWidget.setAutoDefault(False)
+
+                ButtonWidget.clicked.connect(partial(self.SubmitDownloadTask, item.domain, item.filename))
+
+                self.filesView.setItemWidget(tree_item, 2, ButtonWidget)
 
         self.progress.advance()
 
                                       if not hint.startswith("#")])
         self.SearchUpdate()
         self.UpdateInfoLabel()
+        self.toggleButtons()
+        self.cancelButton.setEnabled(False)
 
         self.progress.setRange(0, 0)
 
+    def buttonCheck(self, selected_items, state, button):
+        for item in selected_items:
+            if item.state != state:
+                button.setEnabled(False)
+            else:
+                button.setEnabled(True)
+                break
+
+    def toggleButtons(self):
+        selected_items = [item for item, tree_item, _ in self.updateItems if not tree_item.isHidden()]
+        self.buttonCheck(selected_items, OUTDATED, self.updateButton)
+        self.buttonCheck(selected_items, AVAILABLE, self.downloadButton)
+
     def HandleError(self, exception):
         if isinstance(exception, IOError):
             self.error(0,
         self.infoLabel.setText(text)
 
     def UpdateAll(self):
-        for item, _, _ in self.updateItems:
-            if item.state == OUTDATED:
+        for item, tree_item, _ in self.updateItems:
+            if item.state == OUTDATED and not tree_item.isHidden():
                 self.SubmitDownloadTask(item.domain, item.filename)
 
     def DownloadFiltered(self):
                            for string in strings)
             tree_item.setHidden(hide)
         self.UpdateInfoLabel()
+        self.toggleButtons()
 
     def SubmitDownloadTask(self, domain, filename):
         """
         Submit the (domain, filename) to be downloaded/updated.
         """
+        self.cancelButton.setEnabled(True)
+
         index = self.updateItemIndex(domain, filename)
         _, tree_item, opt_widget = self.updateItems[index]
 
             opt_widget.setState(new_item.state)
 
             self.UpdateInfoLabel()
+            self.cancelButton.setEnabled(False)
 
     def SubmitRemoveTask(self, domain, filename):
         serverfiles.remove(domain, filename)

File Orange/OrangeWidgets/Regression/OWEarth.py

-"""
-<name>Earth</name>
-<description>Multivariate Adaptive Regression Splines (MARS)</description>
-<category>Regression</category>
-<icon>icons/EarthMars.svg</icon>
-<priority>100</priority>
-<tags>MARS, Multivariate, Adaptive, Regression, Splines</tags>
-"""
-
-from OWWidget import *
-import OWGUI
-import Orange
-
-from Orange.regression import earth
-from orngWrap import PreprocessedLearner
-
-
-class OWEarth(OWWidget):
-    settingsList = ["name", "degree", "terms", "penalty"]
-
-    def __init__(self, parent=None, signalManager=None,
-                 title="Earth"):
-        OWWidget.__init__(self, parent, signalManager, title,
-                          wantMainArea=False)
-
-        self.inputs = [("Data", Orange.data.Table, self.set_data),
-                       ("Preprocessor", PreprocessedLearner,
-                        self.set_preprocessor)]
-
-        self.outputs = [("Learner", earth.EarthLearner, Default),
-                        ("Predictor", earth.EarthClassifier, Default),
-                        ("Basis Matrix", Orange.data.Table)]
-
-        self.name = "Earth Learner"
-        self.degree = 1
-        self.terms = 21
-        self.penalty = 2
-
-        self.loadSettings()
-
-        #####
-        # GUI
-        #####
-
-        OWGUI.lineEdit(self.controlArea, self, "name",
-                       box="Learner/Classifier Name",
-                       tooltip="Name for the learner/predictor")
-
-        box = OWGUI.widgetBox(self.controlArea, "Forward Pass", addSpace=True)
-        OWGUI.spin(box, self, "degree", 1, 3, step=1,
-                   label="Max. term degree",
-                   tooltip="Maximum degree of the terms derived "
-                           "(number of hinge functions).")
-        s = OWGUI.spin(box, self, "terms", 1, 200, step=1,
-                       label="Max. terms",
-                       tooltip="Maximum number of terms derived in the "
-                               "forward pass.")
-        s.control.setSpecialValueText("Automatic")
-
-        box = OWGUI.widgetBox(self.controlArea, "Pruning Pass", addSpace=True)
-        OWGUI.doubleSpin(box, self, "penalty", min=0.0, max=10.0, step=0.25,
-                   label="Knot penalty")
-
-        OWGUI.button(self.controlArea, self, "&Apply",
-                     callback=self.apply)
-
-        self.data = None
-        self.preprocessor = None
-        self.resize(300, 200)
-
-        self.apply()
-
-    def set_data(self, data=None):
-        self.data = data
-
-    def set_preprocessor(self, pproc=None):
-        self.preprocessor = pproc
-
-    def handleNewSignals(self):
-        self.apply()
-
-    def apply(self):
-        learner = earth.EarthLearner(
-            degree=self.degree,
-            terms=self.terms if self.terms >= 2 else None,
-            penalty=self.penalty,
-            name=self.name)
-
-        predictor = None
-        basis_matrix = None
-        if self.preprocessor:
-            learner = self.preprocessor.wrapLearner(learner)
-
-        self.error(0)
-        if self.data is not None:
-            try:
-                predictor = learner(self.data)
-                predictor.name = self.name
-            except Exception, ex:
-                self.error(0, "An error during learning: %r" % ex)
-
-            if predictor is not None:
-                base_features = predictor.base_features()
-                basis_domain = Orange.data.Domain(
-                    base_features,
-                    self.data.domain.class_var,
-                    self.data.domain.class_vars)
-                basis_domain.add_metas(self.data.domain.get_metas())
-                basis_matrix = Orange.data.Table(basis_domain, self.data)
-
-        self.send("Learner", learner)
-        self.send("Predictor", predictor)
-        self.send("Basis Matrix", basis_matrix)
-
-    def sendReport(self):
-        self.reportSettings(
-            "Learning parameters",
-            [("Degree", self.degree),
-             ("Terms", self.terms if self.terms >= 2 else "Automatic"),
-             ("Knot penalty", "%.2f" % self.penalty)
-             ])
-
-        self.reportData(self.data)
-
-if __name__ == "__main__":
-    app = QApplication(sys.argv)
-    w = OWEarth()
-    w.set_data(Orange.data.Table("auto-mpg"))
-    w.show()
-    app.exec_()
-    w.saveSettings()

File Orange/__init__.py

 
 _import("regression")
 _import("regression.base")
-_import("regression.earth")
 _import("regression.lasso")
 _import("regression.linear")
 _import("regression.mean")

File Orange/orng/ensemble.c

-/*
-    This file is part of Orange.
-
-    Orange is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    Orange is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Orange; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-    Authors: Janez Demsar, Blaz Zupan, 1996--2002
-    Contact: janez.demsar@fri.uni-lj.si
-*/
-
 /*  This file is a part of dynamic library that staticaly links with C4.5.
     To compile it, download C4.5 (R8) from http://www.cse.unsw.edu.au/~quinlan/,
     copy this file and buildc45.py to C45's directory R8/Src and run buildc45.py.

File Orange/orng/mathutil.py

-#	Mathematical utility routines
-#	Copyright (C) 1999, Wesley Phoa
-#
-#	Reference: Numerical Recipes in C
-
-# Although lacking the proper copyright notice, the author of this module
-# states on his site (http://www.margaretmorgan.com/wesley/python/)
-# that the code is released under GNU GPL
-
-from operator import *
-from Numeric import *
-
-class BracketingException(Exception):
-	pass
-
-class RootFindingException(Exception):
-	pass
-
-class MinimizationException(Exception):
-	pass
-
-GOLDEN = (1+5**.5)/2
-LITTLE = 1e-10
-SQPREC = 1e-4
-
-# 
-# MISCELLANEOUS
-#
-
-def sgn(x):
-	if x==0:
-		return 0
-	else:
-		return x/abs(x)
-
-def along(f, x, v):
-	"""\
-Given a multivariate function f, a point x and a vector v,
-return the univariate function t |-> f(x+tv).
-	"""
-	return lambda t,f=f,x=x,v=v: apply(f, add(x, multiply(t, v)))
-
-#
-# UNIVARIATE ROOT FINDING
-#
-
-def bracket_root(f, interval, max_iterations=50):
-	"""\
-Given a univariate function f and a tuple interval=(x1,x2),
-return a new tuple (bracket, fnvals) where bracket=(x1,x2)
-brackets a root of f and fnvals=(f(x1),f(x2)).
-	"""
-	x1, x2 = interval
-	if x1==x2:
-		raise BracketingException("initial interval has zero width")
-	elif x2<x1:
-		x1, x2 = x2, x1
-	f1, f2 = f(x1), f(x2)
-	for j in range(max_iterations):
-		while f1*f2 >= 0:  # not currently bracketed
-			if abs(f1)<abs(f2):
-				x1 = x1 + GOLDEN*(x1-x2)
-			else:
-				x2 = x2 + GOLDEN*(x2-x1)
-			f1, f2 = f(x1), f(x2)
-		return (x1, x2), (f1, f2)
-	raise BracketingException("too many iterations")
-
-def ridder_root(f, bracket, fnvals=None, accuracy=1e-6, max_iterations=50):
-	"""\
-Given a univariate function f and a tuple bracket=(x1,x2) bracketing a root,
-find a root x of f using Ridder's method. Parameter fnvals=(f(x1),f(x2)) is optional.
-	"""
-	x1, x2 = bracket
-	if fnvals==None:
-		f1, f2 = f(x1), f(x2)
-	else:
-		f1, f2 = fnvals
-	if f1==0:
-		return x1
-	elif f2==0:
-		return x2
-	elif f1*f2>=0:
-		raise BracketingException("initial interval does not bracket a root")
-	x4 = 123456789.
-	for j in range(max_iterations):
-		x3 = (x1+x2)/2
-		f3 = f(x3)
-		temp = f3*f3 - f1*f2
-		x4, x4old = x3 + (x3-x1)*sgn(f1-f2)*f3/temp**.5, x4
-		f4 = f(x4)
-		if f1*f4<0:  # x1 and x4 bracket root
-			x2, f2 = x4, f4
-		else:  # x4 and x2 bracket root
-			x1, f1 = x4, f4
-		if min(abs(x1-x2),abs(x4-x4old))<accuracy or temp==0:
-			return x4
-	raise RootFindingException("too many iterations")
-
-def root(f, interval=(0.,1.), accuracy=1e-4, max_iterations=50):
-	"""\
-Given a univariate function f and an optional interval (x1,x2),
-find a root of f using bracket_root and ridder_root.
-	"""
-	bracket, fnvals = bracket_root(f, interval, max_iterations)
-	return ridder_root(f, bracket, fnvals, accuracy, max_iterations)
-
-#
-# UNIVARIATE MINIMIZATION
-#
-
-def bracket_min(f, interval, max_iterations=50):
-	"""\
-Given a univariate function f and a tuple interval=(x1,x2),
-return a new tuple (bracket, fnval) where bracket=(x1,x2,x3)
-brackets a minimum of f and fnvals=(f(x1),f(x2),f(x3)).
-	"""
-	x1, x2 = interval
-	f1, f2 = f(x1), f(x2)
-	if f2>f1:  # ensure x1 --> x2 is downhill direction
-		x1, x2, f1, f2 = x2, x1, f2, f1
-	x3 = x2 + GOLDEN*(x2-x1)
-	f3 = f(x3)
-	for j in range(max_iterations):
-		if f2<f3:
-			if x1>x3:  # ensure x1<x2<x3
-				x1, x3, f1, f3 = x3, x1, f3, f1
-			return (x1, x2, x3), (f1, f2, f3)
-		else:
-			x1, x2, x3 = x1, x3, x3 + GOLDEN*(x3-x1)
-			f1, f2, f3 = f(x1), f(x2), f(x3)
-	raise BracketingException("too many iterations")
-
-def brent_min(f, bracket, fnvals=None, tolerance=1e-6, max_iterations=50):
-	"""\
-Given a univariate function f and a tuple bracket=(x1,x2,x3) bracketing a minimum,
-find a local minimum of f (with fn value) using Brent's method.
-Optionally pass in the tuple fnvals=(f(x1),f(x2),f(x3)) as a parameter.
-	"""
-	x1, x2, x3 = bracket
-	if fnvals==None:
-		f1, f2, f3 = f(x1), f(xx), f(x3)
-	else:
-		f1, f2, f3 = fnvals
-	if not f1>f2<f3:
-		raise MinimizationException("initial triple does not bracket a minimum")
-	if not x1<x3:  # ensure x1, x2, x3 in ascending order
-		x1, f1, x3, f3 = x3, f3, x1, f1
-	a, b = x1, x3
-
-	e = 0.
-	x = w = v = x2
-	fw = fv = fx = f(x)
-
-	for j in range(max_iterations):
-		xm = (a+b)/2
-		accuracy = tolerance*abs(x) + LITTLE
-		if abs(x-xm) < (2*accuracy - (b-a)/2):
-			return x, fx
-
-		if abs(e)>accuracy:
-			r = (x-w)*(fx-fv)
-			q = (x-v)*(fx-fw)
-			p = (x-v)*q - (x-w)*r
-			q = 2*(q-r)
-			if q>0:
-				p = -p
-			q = abs(q)
-			etemp = e
-			e = d
-			if abs(p)>=abs(q*etemp)/2 or p<=q*(a-x) or p>=q*(b-x):
-				if x>=xm:
-					e = a-x
-				else:
-					e = b-x
-				d = (2-GOLDEN)*e
-			else:  # accept parabolic fit
-				d = p/q
-				u = x+d
-				if u-a<2*accuracy or b-u<2*accuracy:
-					d = accuracy*sgn(xm-x)
-		else:
-			if x>=xm:
-				e = a-x
-			else:
-				e = b-x
-			d = (2-GOLDEN)*e
-
-		if abs(d)>=accuracy:
-			u = x+d
-		else:
-			u = x+accuracy*sgn(d)
-		fu = f(u)
-
-		if fu<=fx:
-			if u>=x:
-				a = x
-			else:
-				b = x
-			v, w, x = w, x, u
-			fv, fw, fx = fw, fx, fu
-		else:
-			if u<x:
-				a = u
-			else:
-				b = u
-			if fu<-fw or w==x:
-				v, w, fv, fw = w, u, fw, fu
-			elif fu<=fw or v==x or v==w:
-				v, fv = u, fu
-
-	raise MinimizationException("too many iterations")
-
-def minimum(f, interval=(0.,1.), tolerance=1e-4, max_iterations=50, return_fnval=0):
-	"""\
-Given a univariate function f and an optional interval (x1,x2),
-find a local minimum of f using bracket_min and brent_min.
-	"""
-	bracket, fnvals = bracket_min(f, interval, max_iterations)
-	min, fnval = brent_min(f, bracket, fnvals, tolerance, max_iterations)
-	if return_fnval:
-		return min, fnval
-	else:
-		return min
-
-#
-# MULTIVARIATE MINIMIZATION
-#
-
-def powell_min(f, p0, tolerance=1e-4, max_iterations=200, return_fnval=0):
-	"""\
-Given a multivariate function f and a starting point p0,
-find a local minimum of f using Powell's direction set method.
-	"""
-	p = p0
-	fp = apply(f, p)
-	n = len(p)
-	directions = identity(n).tolist()  # the n coordinate vectors
-
-	for j in range(max_iterations):
-		maxdrop_i = 0
-		maxdrop = 0.
-		pold=p
-		fpold=fp
-
-		for i in range(n):
-			fptemp = fp
-			v = directions[i]
-			t, fp = minimum(along(f, p, v), return_fnval=1)
-			p = add(p, multiply(t, v))
-			if fptemp-fp>maxdrop:
-				maxdrop_i = i
-				maxdrop = fpold-fp
-
-		totaldrop = fpold-fp
-		if 2*totaldrop<=tolerance*(abs(fp)+abs(fpold)):
-			if return_fnval:
-				return tuple(p), fp
-			else:
-				return tuple(p)
-
-		vnew = subtract(p, pold)
-		pex = add(pold, multiply(2, vnew))
-		fpex = apply(f, pex)
-		if fpex<fp:
-			temp = fpold-fp-maxdrop
-			if 2*(fpold-2*fp+fpex)*temp*temp<totaldrop*totaldrop*maxdrop:
-				t, fp = minimum(along(f, p, vnew), return_fnval=1)
-				p = add(p, multiply(t, vnew))
-				directions[maxdrop_i] = vnew
-
-	raise MinimizationException("too many iterations")
-
-if __name__=='__main__':
-
-	from math import *
-
-	print root(cos)
-	print minimum(sin)
-
-	def f(x, y):
-		return x*x + x*y + y*y + 2*x - y
-
-	print powell_min(f, (0., 0.))

File Orange/orng/orngDimRed.py

 #   - 2003/10/28: project initiated
 #   - 2003/11/20: returning the parameters of the transform
 
-import numpy, mathutil
+import numpy
 import numpy.linalg as LinearAlgebra
 
 # before running PCA, it is helpful to apply the transformation
         # kurtosis = numpy.average(numpy.power(cv,4))/numpy.power(numpy.average(numpy.power(cv,2)),2)-3
         return skewness**2
 
-def BoxCoxTransform(vector,lambd=None):
-    v = -min(vector)+1+vector
-    print "shifting by ",-min(vector)+1
-    if lambd==None:
-        # find the value of lambda that will minimize skew
-        lambd = mathutil.minimum(_BCskewness(v))
-        print "best-fitting lambda = ",lambd
-    return _BC(v,lambd)
+# def BoxCoxTransform(vector,lambd=None):
+#     v = -min(vector)+1+vector
+#     print "shifting by ",-min(vector)+1
+#     if lambd==None:
+#         # find the value of lambda that will minimize skew
+#         lambd = mathutil.minimum(_BCskewness(v))
+#         print "best-fitting lambda = ",lambd
+#     return _BC(v,lambd)
 
 def RankConversion(vector,reverse=0):
     assert(len(numpy.shape(vector))==1) # this must be a vector
     print MaxScaling(v)
     print "variance scaling"
     print VarianceScaling(v)
-    print "Box-Cox"
-    print BoxCoxTransform(v)
+#    print "Box-Cox"
+#    print BoxCoxTransform(v)

File Orange/regression/earth.py

-"""\
-====================================================
-Multivariate Adaptive Regression Splines (``earth``)
-====================================================
-
-.. index:: regression, linear model
-
-`Multivariate adaptive regression splines (MARS)`_ is a non-parametric
-regression method that extends a linear model with non-linear
-interactions.
-
-This module borrows the implementation of the technique from the `Earth R
-package`_ by Stephen Milborrow.
-
-.. _`Multivariate adaptive regression splines (MARS)`:
-        http://en.wikipedia.org/wiki/Multivariate_adaptive_regression_splines
-
-.. _`Earth R package`: http://cran.r-project.org/web/packages/earth/index.html
-
-Example ::
-
-    >>> import Orange
-    >>> data = Orange.data.Table("housing")
-    >>> c = Orange.regression.earth.EarthLearner(data, degree=2, terms=10)
-    >>> print c
-    MEDV =
-       23.587
-       +11.896 * max(0, RM - 6.431)
-       +1.142 * max(0, 6.431 - RM)
-       -0.612 * max(0, LSTAT - 6.120)
-       -228.795 * max(0, NOX - 0.647) * max(0, RM - 6.431)
-       +0.023 * max(0, TAX - 307.000) * max(0, 6.120 - LSTAT)
-       +0.029 * max(0, 307.000 - TAX) * max(0, 6.120 - LSTAT)
-
-
-.. autoclass:: EarthLearner
-    :members:
-
-.. autoclass:: EarthClassifier
-    :members:
-
-
-Utility functions
------------------
-
-.. autofunction:: gcv
-
-.. autofunction:: plot_evimp
-
-.. autofunction:: bagged_evimp
-
-.. autoclass:: ScoreEarthImportance
-
-"""
-
-import Orange
-from Orange.feature import Discrete, Continuous
-from Orange.data import Table, Domain
-from Orange.data.preprocess import DomainContinuizer
-
-import numpy
-
-
-def is_discrete(var):
-    return isinstance(var, Discrete)
-
-
-def is_continuous(var):
-    return isinstance(var, Continuous)
-
-
-def expand_discrete(var):
-    """ Expand a discrete variable ``var`` returning one continuous indicator
-    variable for each value of ``var`` (if the number of values is grater
-    then 2 else return only one indicator variable).
-
-    """
-    if len(var.values) > 2:
-        values = var.values
-    elif len(var.values) == 2:
-        values = var.values[-1:]
-    else:
-        values = var.values[:1]
-    new_vars = []
-    for value in values:
-        new = Continuous("{0}={1}".format(var.name, value))
-        new.get_value_from = cls = Orange.core.ClassifierFromVar(whichVar=var)
-        cls.transformer = Orange.core.Discrete2Continuous()
-        cls.transformer.value = int(Orange.core.Value(var, value))
-        new.source_variable = var
-        new_vars.append(new)
-    return new_vars
-
-
-def select_attrs(table, features, class_var=None,
-                 class_vars=None, metas=None):
-    """ Select only ``attributes`` from the ``table``.
-    """
-    if class_vars is None:
-        domain = Domain(features, class_var)
-    else:
-        domain = Domain(features, class_var, class_vars=class_vars)
-    if metas:
-        domain.add_metas(metas)
-    return Table(domain, table)
-
-
-class EarthLearner(Orange.regression.base.BaseRegressionLearner):
-    """Earth learner class. Supports both regression and classification
-    problems. For classification, class values are expanded into
-    continuous indicator columns (one for each value if the number of
-    values is grater then 2), and a multi response model is fit to these
-    new columns. The resulting classifier the computes response
-    values on new instances to select the final predicted class.
-
-    """
-    def __new__(cls, instances=None, weight_id=None, **kwargs):
-        self = Orange.regression.base.BaseRegressionLearner.__new__(cls)
-        if instances is not None:
-            self.__init__(**kwargs)
-            return self.__call__(instances, weight_id)
-        else:
-            return self
-
-    def __init__(self, degree=1, terms=21, penalty=None, thresh=1e-3,
-                 min_span=0, new_var_penalty=0, fast_k=20, fast_beta=1,
-                 pruned_terms=None, scale_resp=True, store_instances=True,
-                **kwds):
-        """Initialize the learner instance.
-
-        :param degree: Maximum degree (num. of hinge functions per term)
-            of the terms in the model (default: 1).
-        :type degree: int
-        :param terms: Maximum number of terms in the forward pass
-                (default: 21).  If set to ``None``, ``min(200, max(20, 2
-                * n_attributes)) + 1`` will be used, like the default
-                setting in earth R package.
-        :type terms: int
-        :param penalty: Penalty for hinges in the GCV computation (used
-            in the pruning pass). Default is 3.0 if ``degree`` is above 1,
-            and 2.0 otherwise.
-        :type penalty: float
-        :param thresh: Threshold for RSS decrease in the forward pass
-            (default: 0.001).
-        :type thresh: float
-        :param min_span: TODO.
-        :param new_var_penalty: Penalty for introducing a new variable
-            in the model during the forward pass (default: 0).
-        :type new_var_penalty: float
-        :param fast_k: Fast k.
-        :param fast_beta: Fast beta.
-        :param pruned_terms: Maximum number of terms in the model after
-            pruning (default: ``None``, no limit).
-        :type pruned_terms: int
-        :param scale_resp: Scale responses prior to forward pass (default:
-            ``True``); ignored for models with multiple responses.
-        :type scale_resp: bool
-        :param store_instances: Store training instances in the model
-            (default: ``True``).
-        :type store_instances: bool
-
-        .. todo:: min_span, prunning_method (need Leaps like functionality,
-            currently only eval_subsets_using_xtx is implemented).
-
-        """
-
-        super(EarthLearner, self).__init__()
-
-        self.degree = degree
-        self.terms = terms
-        if penalty is None:
-            penalty = 3 if degree > 1 else 2
-        self.penalty = penalty
-        self.thresh = thresh
-        self.min_span = min_span
-        self.new_var_penalty = new_var_penalty
-        self.fast_k = fast_k
-        self.fast_beta = fast_beta
-        self.pruned_terms = pruned_terms
-        self.scale_resp = scale_resp
-        self.store_instances = store_instances
-        self.__dict__.update(kwds)
-
-        self.continuizer.class_treatment = DomainContinuizer.Ignore
-
-    def __call__(self, instances, weight_id=None):
-        expanded_class = None
-        multitarget = False
-
-        if instances.domain.class_var:
-            instances = self.impute_table(instances)
-            instances = self.continuize_table(instances)
-
-            if is_discrete(instances.domain.class_var):
-                # Expand a discrete class with indicator columns
-                expanded_class = expand_discrete(instances.domain.class_var)
-                y_table = select_attrs(instances, expanded_class)
-                (y, ) = y_table.to_numpy_MA("A")
-                (x, ) = instances.to_numpy_MA("A")
-            elif is_continuous(instances.domain.class_var):
-                x, y, _ = instances.to_numpy_MA()
-                y = y.reshape((-1, 1))
-            else:
-                raise ValueError("Cannot handle the response.")
-        elif instances.domain.class_vars:
-            # Multi-target domain
-            if not all(map(is_continuous, instances.domain.class_vars)):
-                raise TypeError("Only continuous multi-target classes are supported.")
-            x_table = select_attrs(instances, instances.domain.attributes)
-            y_table = select_attrs(instances, instances.domain.class_vars)
-
-            # Impute and continuize only the x_table
-            x_table = self.impute_table(x_table)
-            x_table = self.continuize_table(x_table)
-            domain = Domain(x_table.domain.attributes,
-                            class_vars=instances.domain.class_vars)
-
-            (x, ) = x_table.to_numpy_MA("A")
-            (y, ) = y_table.to_numpy_MA("A")
-
-            multitarget = True
-        else:
-            raise ValueError("Class variable expected.")
-
-        # check for non-finite values in y.
-        if not numpy.isfinite(y).all():
-            raise ValueError("Non-finite values present in Y")
-
-        # mask non-finite values in x.
-        x = numpy.ma.masked_invalid(x, copy=False)
-
-        if self.scale_resp and y.shape[1] == 1:
-            sy = y - numpy.ma.mean(y, axis=0)
-            sy = sy / numpy.ma.std(sy, axis=0)
-        else:
-            sy = y
-
-        # replace masked values with means.
-        if numpy.ma.is_masked(sy):
-            mean_sy = numpy.ma.mean(sy, axis=0)
-            sy = numpy.where(sy.mask, mean_sy, sy)
-
-        if numpy.ma.is_masked(x):
-            mean_x = numpy.ma.mean(x, axis=0)
-            x = numpy.where(x.mask, mean_x, x)
-
-        terms = self.terms
-        if terms is None:
-            # Automatic maximum number of terms
-            terms = min(200, max(20, 2 * x.shape[1])) + 1
-
-        n_terms, used, bx, dirs, cuts = forward_pass(x, sy,
-            degree=self.degree, terms=terms, penalty=self.penalty,
-            thresh=self.thresh, fast_k=self.fast_k, fast_beta=self.fast_beta,
-            new_var_penalty=self.new_var_penalty)
-
-        # discard unused terms from bx, dirs, cuts
-        bx = bx[:, used]
-        dirs = dirs[used, :]
-        cuts = cuts[used, :]
-
-        # pruning
-        used, subsets, rss_per_subset, gcv_per_subset = \
-            pruning_pass(bx, y, self.penalty,
-                         pruned_terms=self.pruned_terms)
-
-        # Fit betas
-        bx_used = bx[:, used]
-        betas, res, rank, s = numpy.linalg.lstsq(bx_used, y)
-
-        return EarthClassifier(instances.domain, used, dirs, cuts, betas.T,
-                               subsets, rss_per_subset, gcv_per_subset,
-                               instances=instances if self.store_instances else None,
-                               multitarget=multitarget,
-                               expanded_class=expanded_class
-                               )
-
-
-def soft_max(values):
-    values = numpy.asarray(values)
-    return numpy.exp(values) / numpy.sum(numpy.exp(values))
-
-
-class EarthClassifier(Orange.core.ClassifierFD):
-    """ Earth classifier.
-    """
-    def __init__(self, domain, best_set, dirs, cuts, betas, subsets=None,
-                 rss_per_subset=None, gcv_per_subset=None, instances=None,
-                 multitarget=False, expanded_class=None,
-                 original_domain=None, **kwargs):
-        self.multitarget = multitarget
-        self.domain = domain
-        self.class_var = domain.class_var
-        if self.multitarget:
-            self.class_vars = domain.class_vars
-
-        self.best_set = best_set
-        self.dirs = dirs
-        self.cuts = cuts
-        self.betas = betas
-        self.subsets = subsets
-        self.rss_per_subset = rss_per_subset
-        self.gcv_per_subset = gcv_per_subset
-        self.instances = instances
-        self.expanded_class = expanded_class
-        self.original_domain = original_domain
-        self.__dict__.update(kwargs)
-
-    def __call__(self, instance, result_type=Orange.core.GetValue):
-        if self.multitarget and self.domain.class_vars:
-            resp_vars = list(self.domain.class_vars)
-        elif is_discrete(self.class_var):
-            resp_vars = self.expanded_class
-        else:
-            resp_vars = [self.class_var]
-
-        vals = self.predict(instance)
-        vals = [var(val) for var, val in zip(resp_vars, vals)]
-
-        from Orange.statistics.distribution import Distribution
-
-        if not self.multitarget and is_discrete(self.class_var):
-            dist = Distribution(self.class_var)
-            if len(self.class_var.values) == 2:
-                probs = [1 - float(vals[0]), float(vals[0])]
-            else:
-                probs = soft_max(map(float, vals))
-
-            for val, p in zip(self.class_var.values, probs):
-                dist[val] = p
-            value = dist.modus()
-            vals, probs = [value], [dist]
-        else:
-            probs = []
-            for var, val in zip(resp_vars, vals):
-                dist = Distribution(var)
-                dist[val] = 1.0
-                probs.append(dist)
-
-        if not self.multitarget:
-            vals, probs = vals[0], probs[0]
-
-        if result_type == Orange.core.GetValue:
-            return vals
-        elif result_type == Orange.core.GetBoth:
-            return vals, probs
-        else:
-            return probs
-
-    def base_matrix(self, instances=None):
-        """Return the base matrix (bx) of the Earth model for the table.
-        If table is not supplied, the base matrix of the training instances
-        is returned.
-        Base matrix is a len(instances) x num_terms matrix of computed values
-        of terms in the model (not multiplied by beta) for each instance.
-
-        :param instances: Input instances for the base matrix.
-        :type instances: :class:`Orange.data.Table`
-
-        """
-        if instances is None:
-            instances = self.instances
-        instances = select_attrs(instances, self.domain.attributes)
-        (data,) = instances.to_numpy_MA("A")
-        bx = base_matrix(data, self.best_set, self.dirs, self.cuts)
-        return bx
-
-    def base_features(self):
-        """Return a list of features for the included Earth terms.
-        The attributes can be used in Orange's domain translation
-        (i.e. they define the proper ``get_value_from`` functions).
-
-        """
-        terms = []
-        dirs = self.dirs[self.best_set]
-        cuts = self.cuts[self.best_set]
-        # For faster domain translation all the features share
-        # this _instance_cache.
-        _instance_cache = {}
-        for dir, cut in zip(dirs[1:], cuts[1:]):  # Drop the intercept (first column)
-            hinge = [_format_knot(self, attr.name, dir1, cut1) \
-                     for (attr, dir1, cut1) in \
-                     zip(self.domain.attributes, dir, cut) \
-                     if dir1 != 0.0]
-            term_name = " * ".join(hinge)
-            term = Orange.feature.Continuous(term_name)
-            term.get_value_from = term_computer(
-                term, self.domain, dir, cut,
-                _instance_cache=_instance_cache
-            )
-
-            terms.append(term)
-        return terms
-
-    def predict(self, instance):
-        """ Predict the response value(s)
-
-        :param instance: Data instance
-        :type instance: :class:`Orange.data.Instance`
-
-        """
-        data = Orange.data.Table(self.domain, [instance])
-        bx = self.base_matrix(data)
-        bx_used = bx[:, self.best_set]
-        vals = numpy.dot(bx_used, self.betas.T).ravel()
-        return vals
-
-    def used_attributes(self, term=None):
-        """Return the used terms for term (index). If no term is given,
-        return all attributes in the model.
-
-        :param term: term index
-        :type term: int
-
-        """
-        if term is None:
-            return reduce(set.union, [self.used_attributes(i) \
-                                      for i in range(self.best_set.size)],
-                          set())
-
-        attrs = self.domain.attributes
-
-        used_mask = self.dirs[term, :] != 0.0
-        return [a for a, u in zip(attrs, used_mask) if u]
-
-    def evimp(self, used_only=True):
-        """ Return the estimated variable importances.
-
-        :param used_only: if True return only used attributes
-
-        """
-        return evimp(self, used_only)
-
-    def __reduce__(self):
-        return (type(self), (self.domain, self.best_set, self.dirs,
-                            self.cuts, self.betas),
-                dict(self.__dict__))
-
-    def to_string(self, percision=3, indent=3):
-        """ Return a string representation of the model.
-        """
-        return format_model(self, percision, indent)
-
-    def __str__(self):
-        return self.to_string()
-
-"""
-Utility functions
------------------
-"""
-
-
-def base_matrix(data, best_set, dirs, cuts):
-    """ Return the base matrix for the earth model.
-
-    :param data: Input data
-    :type data: :class:`numpy.ndarray`
-
-    :param best_set: A array of booleans indicating used terms.
-    :type best_set: :class:`numpy.ndarray`
-
-    :param dirs: Earth model's dirs members
-    :type dirs: :class:`numpy.ndarray`
-
-    :param cuts: Earth model's cuts members
-    :type cuts: :class:`numpy.ndarray`
-
-    """
-    data = numpy.asarray(data)
-    best_set = numpy.asarray(best_set)
-    dirs = numpy.asarray(dirs)
-    cuts = numpy.asarray(cuts)
-
-    bx = numpy.zeros((data.shape[0], best_set.shape[0]))
-    bx[:, 0] = 1.0  # The intercept
-    for termi in range(1, best_set.shape[0]):
-        term_dirs = dirs[termi]
-        term_cuts = cuts[termi]
-
-        dir_p1 = numpy.where(term_dirs == 1)[0]
-        dir_m1 = numpy.where(term_dirs == -1)[0]
-        dir_2 = numpy.where(term_dirs == 2)[0]
-
-        x1 = data[:, dir_p1] - term_cuts[dir_p1]
-        x2 = term_cuts[dir_m1] - data[:, dir_m1]
-        x3 = data[:, dir_2]
-
-        x1 = numpy.where(x1 > 0.0, x1, 0.0)
-        x2 = numpy.where(x2 > 0.0, x2, 0.0)
-
-        X = numpy.hstack((x1, x2, x3))
-        X = numpy.cumprod(X, axis=1)
-        bx[:, termi] = X[:, -1] if X.size else 0.0
-
-    return bx
-
-
-def gcv(rss, n, n_effective_params):
-    """ Return the generalized cross validation.
-
-    .. math:: gcv = rss / (n * (1 - NumEffectiveParams / n) ^ 2)
-
-    :param rss: Residual sum of squares.
-    :param n: Number of training instances.
-    :param n_effective_params: Number of effective paramaters.
-
-    """
-    return  rss / (n * (1. - n_effective_params / n) ** 2)
-
-
-class term_computer(Orange.core.ClassifierFD):
-    """An utility class for computing basis terms. Can be used as
-    a :obj:`~Orange.feature.Descriptior.get_value_from` member.
-
-    """
-    def __init__(self, term_var=None, domain=None, dir=None, cut=None,
-                 _instance_cache=None):
-        self.class_var = term_var
-        self.domain = domain
-
-        self.dir = dir
-        self.cut = cut
-
-        if dir is not None:
-            self.mask = self.dir != 0
-            self.masked_dir = self.dir[self.mask]
-            self.masked_cut = self.cut[self.mask]
-        else:
-            # backcompat. with old pickled format.
-            self.mask = self.masked_dir = self.masked_cut = None
-
-        self._instance_cache = _instance_cache
-
-    def __call__(self, instance, return_what=Orange.core.GetValue):
-        instance = self._instance_as_masked_array(instance)
-
-        if self.mask is None:
-            self.mask = self.dir != 0
-            self.masked_dir = self.dir[self.mask]
-            self.masked_cut = self.cut[self.mask]
-
-        values = instance[self.mask]
-        if numpy.ma.is_masked(values):
-            # Can't compute the term.
-            return self.class_var("?")
-
-        # Works faster with plain arrays
-        values = numpy.array(values)
-        values -= self.masked_cut
-        values *= self.masked_dir
-
-        values[values < 0] = 0
-        value = numpy.prod(values)
-
-        return self.class_var(value)
-
-    def _instance_as_masked_array(self, instance):
-        array = None
-        if self._instance_cache is not None:
-            array = self._instance_cache.get(instance, None)
-
-        if array is None:
-            table = Orange.data.Table(self.domain, [instance])
-            (array,) = table.to_numpy_MA("A")
-            array = array[0]
-
-            if self._instance_cache is not None:
-                self._instance_cache.clear()
-                self._instance_cache[instance] = array
-        return array
-
-    def __reduce__(self):
-        return (type(self), (self.class_var, self.domain, self.dir, self.cut),
-                dict(self.__dict__.items()))
-
-
-"""
-Multi-label utility functions
-"""
-
-
-"""
-ctypes interface to ForwardPass and EvalSubsetsUsingXtx.
-"""
-
-import ctypes
-from numpy import ctypeslib
-import orange
-
-_c_orange_lib = ctypeslib.load_library(orange.__file__, "")
-_c_forward_pass_ = _c_orange_lib.EarthForwardPass
-
-_c_forward_pass_.argtypes = \
-    [ctypes.POINTER(ctypes.c_int),  # pnTerms:
-     ctypeslib.ndpointer(dtype=ctypes.c_bool, ndim=1),  # FullSet
-     ctypeslib.ndpointer(dtype=ctypes.c_double, ndim=2, flags="F_CONTIGUOUS"), # bx
-     ctypeslib.ndpointer(dtype=ctypes.c_int, ndim=2, flags="F_CONTIGUOUS"),    # Dirs
-     ctypeslib.ndpointer(dtype=ctypes.c_double, ndim=2, flags="F_CONTIGUOUS"), # Cuts
-     ctypeslib.ndpointer(dtype=ctypes.c_int, ndim=1),  # nFactorsInTerms
-     ctypeslib.ndpointer(dtype=ctypes.c_int, ndim=1),  # nUses
-     ctypeslib.ndpointer(dtype=ctypes.c_double, ndim=2, flags="F_CONTIGUOUS"), # x
-     ctypeslib.ndpointer(dtype=ctypes.c_double, ndim=2, flags="F_CONTIGUOUS"), # y
-     ctypeslib.ndpointer(dtype=ctypes.c_double, ndim=1), # Weights
-     ctypes.c_int,  # nCases
-     ctypes.c_int,  # nResp
-     ctypes.c_int,  # nPred
-     ctypes.c_int,  # nMaxDegree
-     ctypes.c_int,  # nMaxTerms
-     ctypes.c_double,   # Penalty
-     ctypes.c_double,   # Thresh
-     ctypes.c_int,  # nFastK
-     ctypes.c_double,   # FastBeta
-     ctypes.c_double,   # NewVarPenalty
-     ctypeslib.ndpointer(dtype=ctypes.c_int, ndim=1),  # LinPreds
-     ctypes.c_bool, # UseBetaCache
-     ctypes.c_char_p    # sPredNames
-     ]
-
-
-def forward_pass(x, y, degree=1, terms=21, penalty=None, thresh=0.001,
-                  fast_k=21, fast_beta=1, new_var_penalty=2):
-    """ Do earth forward pass.
-    """
-    x = numpy.asfortranarray(x, dtype=ctypes.c_double)
-    y = numpy.asfortranarray(y, dtype=ctypes.c_double)
-    if x.shape[0] != y.shape[0]:
-        raise ValueError("First dimensions of x and y must be the same.")
-    if y.ndim == 1:
-        y = y.reshape((-1, 1), order="F")
-    if penalty is None:
-        penalty = 2
-    n_cases = x.shape[0]
-    n_preds = x.shape[1]
-
-    n_resp = y.shape[1] if y.ndim == 2 else y.shape[0]
-
-    # Output variables
-    n_term = ctypes.c_int()
-    full_set = numpy.zeros((terms,), dtype=ctypes.c_bool, order="F")
-    bx = numpy.zeros((n_cases, terms), dtype=ctypes.c_double, order="F")
-    dirs = numpy.zeros((terms, n_preds), dtype=ctypes.c_int, order="F")
-    cuts = numpy.zeros((terms, n_preds), dtype=ctypes.c_double, order="F")
-    n_factors_in_terms = numpy.zeros((terms,), dtype=ctypes.c_int, order="F")
-    n_uses = numpy.zeros((n_preds,), dtype=ctypes.c_int, order="F")
-    weights = numpy.ones((n_cases,), dtype=ctypes.c_double, order="F")
-    lin_preds = numpy.zeros((n_preds,), dtype=ctypes.c_int, order="F")
-    use_beta_cache = True
-
-    # These tests are performed in ForwardPass, and if they fail the function
-    # calls exit. So we must check it here and raise a exception to avoid a
-    # process shutdown.
-    if n_cases < 8:
-        raise ValueError("Need at least 8 data instances.")