source: orange/Orange/misc/__init__.py @ 9937:9b05ab0a87f3

Revision 9937:9b05ab0a87f3, 22.8 KB checked in by Jure Zbontar <jure.zbontar@…>, 2 years ago (diff)

Doctest initial commit

Line 
1"""
2.. index:: misc
3
4Module Orange.misc contains common functions and classes which are used in other modules.
5
6.. index: SymMatrix
7
8-----------------------
9SymMatrix
10-----------------------
11
12:obj:`SymMatrix` implements symmetric matrices of size fixed at
13construction time (and stored in :obj:`SymMatrix.dim`).
14
15.. class:: SymMatrix
16
17    .. attribute:: dim
18   
19        Matrix dimension.
20           
21    .. attribute:: matrix_type
22
23        Can be ``SymMatrix.Lower`` (0), ``SymMatrix.Upper`` (1),
24        ``SymMatrix.Symmetric`` (2, default), ``SymMatrix.Lower_Filled`` (3) or
25        ``SymMatrix.Upper_Filled`` (4).
26
27        If the matrix type is ``Lower`` or ``Upper``, indexing
28        above or below the diagonal, respectively, will fail.
29        With ``Lower_Filled`` and ``Upper_Filled``,
30        the elements upper or lower, respectively, still
31        exist and are set to zero, but they cannot be modified. The
32        default matrix type is ``Symmetric``, but can be changed
33        at any time.
34
35        If matrix type is ``Upper``, it is printed as:
36
37        >>> m.matrix_type = m.Upper
38        >>> print m
39        (( 1.000,  2.000,  3.000,  4.000),
40         (         4.000,  6.000,  8.000),
41         (                 9.000, 12.000),
42         (                        16.000))
43
44        Changing the type to ``Lower_Filled`` changes the printout to
45
46        >>> m.matrix_type = m.Lower_Filled
47        >>> print m
48        (( 1.000,  0.000,  0.000,  0.000),
49         ( 2.000,  4.000,  0.000,  0.000),
50         ( 3.000,  6.000,  9.000,  0.000),
51         ( 4.000,  8.000, 12.000, 16.000))
52   
53    .. method:: __init__(dim[, default_value])
54
55        Construct a symmetric matrix of the given dimension.
56
57        :param dim: matrix dimension
58        :type dim: int
59
60        :param default_value: default value (0 by default)
61        :type default_value: double
62       
63       
64    .. method:: __init__(instances)
65
66        Construct a new symmetric matrix containing the given data instances.
67        These can be given as Python list containing lists or tuples.
68
69        :param instances: data instances
70        :type instances: list of lists
71       
72        The following example fills a matrix created above with
73        data in a list::
74
75            import Orange
76            m = [[],
77                 [ 3],
78                 [ 2, 4],
79                 [17, 5, 4],
80                 [ 2, 8, 3, 8],
81                 [ 7, 5, 10, 11, 2],
82                 [ 8, 4, 1, 5, 11, 13],
83                 [ 4, 7, 12, 8, 10, 1, 5],
84                 [13, 9, 14, 15, 7, 8, 4, 6],
85                 [12, 10, 11, 15, 2, 5, 7, 3, 1]]
86                   
87            matrix = Orange.data.SymMatrix(m)
88
89        SymMatrix also stores diagonal elements. They are set
90        to zero, if they are not specified. The missing elements
91        (shorter lists) are set to zero as well. If a list
92        spreads over the diagonal, the constructor checks
93        for asymmetries. For instance, the matrix
94
95        ::
96
97            m = [[],
98                 [ 3,  0, f],
99                 [ 2,  4]]
100   
101        is only OK if f equals 2. Finally, no row can be longer
102        than matrix size. 
103
104    .. method:: get_values()
105   
106        Return all matrix values in a Python list.
107
108    .. method:: get_KNN(i, k)
109   
110        Return k columns with the lowest value in the i-th row.
111       
112        :param i: i-th row
113        :type i: int
114       
115        :param k: number of neighbors
116        :type k: int
117       
118    .. method:: avg_linkage(clusters)
119   
120        Return a symmetric matrix with average distances between given clusters. 
121     
122        :param clusters: list of clusters
123        :type clusters: list of lists
124       
125    .. method:: invert(type)
126   
127        Invert values in the symmetric matrix.
128       
129        :param type: 0 (-X), 1 (1 - X), 2 (max - X), 3 (1 / X)
130        :type type: int
131
132    .. method:: normalize(type)
133   
134        Normalize values in the symmetric matrix.
135       
136        :param type: 0 (normalize to [0, 1] interval), 1 (Sigmoid)
137        :type type: int
138       
139       
140-------------------
141Indexing
142-------------------
143
144For symmetric matrices the order of indices is not important:
145if ``m`` is a SymMatrix, then ``m[2, 4]`` addresses the same element as ``m[4, 2]``.
146
147.. literalinclude:: code/symmatrix.py
148    :lines: 1-6
149
150Although only the lower left half of the matrix was set explicitely,
151the whole matrix is constructed.
152
153>>> print m
154(( 1.000,  2.000,  3.000,  4.000),
155 ( 2.000,  4.000,  6.000,  8.000),
156 ( 3.000,  6.000,  9.000, 12.000),
157 ( 4.000,  8.000, 12.000, 16.000))
158 
159Entire rows are indexed with a single index. They can be iterated
160over in a for loop or sliced (with, for example, ``m[:3]``):
161
162>>> print m[1]
163(3.0, 6.0, 9.0, 0.0)
164>>> m.matrix_type = m.Lower
165>>> for row in m:
166...     print row
167(1.0,)
168(2.0, 4.0)
169(3.0, 6.0, 9.0)
170(4.0, 8.0, 12.0, 16.0)
171
172.. index: Random number generator
173
174-----------------------
175Random number generator
176-----------------------
177
178:obj:`Random` uses the
179`Mersenne twister <http://en.wikipedia.org/wiki/Mersenne_twister>`_ algorithm
180to generate random numbers.
181
182::
183
184    >>> import Orange
185    >>> rg = Orange.misc.Random(42)
186    >>> rg(10)
187    4
188    >>> rg(10)
189    7
190    >>> rg.uses  # We called rg two times.
191    2
192    >>> rg.reset()
193    >>> rg(10)
194    4
195    >>> rg(10)
196    7
197    >>> rg.uses
198    2
199
200
201.. class:: Random(initseed)
202
203    :param initseed: Seed used for initializing the random generator.
204    :type initseed: int
205
206    .. method:: __call__(n)
207
208        Return a random integer R such that 0 <= R < n.
209
210        :type n: int
211
212    .. method:: reset([initseed])
213
214        Reinitialize the random generator with `initseed`. If `initseed`
215        is not given use the existing value of attribute `initseed`.
216
217    .. attribute:: uses
218       
219        The number of times the generator was called after
220        initialization/reset.
221   
222    .. attribute:: initseed
223
224        Random seed.
225
226Two examples or random number generator uses found in the documentation
227are :obj:`Orange.evaluation.testing` and :obj:`Orange.data.Table`.
228
229.. automodule:: Orange.misc.counters
230  :members:
231
232.. automodule:: Orange.misc.render
233  :members:
234
235.. automodule:: Orange.misc.selection
236
237.. automodule:: Orange.misc.addons
238
239.. automodule:: Orange.misc.serverfiles
240
241.. automodule:: Orange.misc.environ
242
243.. automodule:: Orange.misc.r
244
245
246"""
247import environ
248import counters
249import render
250
251from Orange.core import RandomGenerator as Random
252from orange import SymMatrix
253
254# addons is intentionally not imported; if it were, add-ons' directories would
255# be added to the python path. If that sounds OK, this can be changed ...
256
257__all__ = ["counters", "selection", "render", "serverfiles",
258           "deprecated_members", "deprecated_keywords",
259           "deprecated_attribute", "deprecation_warning"]
260
261import random, types, sys
262import time
263
264def getobjectname(x, default=""):
265    if type(x)==types.StringType:
266        return x
267     
268    for i in ["name", "shortDescription", "description", "func_doc", "func_name"]:
269        if getattr(x, i, ""):
270            return getattr(x, i)
271
272    if hasattr(x, "__class__"):
273        r = repr(x.__class__)
274        if r[1:5]=="type":
275            return str(x.__class__)[7:-2]
276        elif r[1:6]=="class":
277            return str(x.__class__)[8:-2]
278    return default
279
280
281def demangle_examples(x):
282    if type(x)==types.TupleType:
283        return x
284    else:
285        return x, 0
286
287
288def frange(*argw):
289    """ Like builtin `range` but works with floats
290    """
291    start, stop, step = 0.0, 1.0, 0.1
292    if len(argw)==1:
293        start=step=argw[0]
294    elif len(argw)==2:
295        stop, step = argw
296    elif len(argw)==3:
297        start, stop, step = argw
298    elif len(argw)>3:
299        raise AttributeError, "1-3 arguments expected"
300
301    stop+=1e-10
302    i=0
303    res=[]
304    while 1:
305        f=start+i*step
306        if f>stop:
307            break
308        res.append(f)
309        i+=1
310    return res
311
312verbose = 0
313
314def print_verbose(text, *verb):
315    if len(verb) and verb[0] or verbose:
316        print text
317
318__doc__ += """\
319------------------
320Reporting progress
321------------------
322
323.. autoclass:: Orange.misc.ConsoleProgressBar
324    :members:
325
326"""
327
328class ConsoleProgressBar(object):
329    """ A class to for printing progress bar reports in the console.
330   
331    Example ::
332   
333        >>> import sys, time
334        >>> progress = ConsoleProgressBar("Example", output=sys.stdout)
335        >>> for i in range(100):
336        ...    progress.advance()
337        ...    # Or progress.set_state(i)
338        ...    time.sleep(0.01)
339        ...
340        ...
341        Example ===================================>100%
342       
343    """
344    def __init__(self, title="", charwidth=40, step=1, output=None):
345        """ Initialize the progress bar.
346       
347        :param title: The title for the progress bar.
348        :type title: str
349        :param charwidth: The maximum progress bar width in characters.
350       
351            .. todo:: Get the console width from the ``output`` if the
352                information can be retrieved.
353               
354        :type charwidth: int
355        :param step: A default step used if ``advance`` is called without
356            any  arguments
357       
358        :type step: int
359        :param output: The output file. If None (default) then ``sys.stderr``
360            is used.
361           
362        :type output: An file like object to print the progress report to.
363         
364        """
365        self.title = title + " "
366        self.charwidth = charwidth
367        self.step = step
368        self.currstring = ""
369        self.state = 0
370        if output is None:
371            output = sys.stderr
372        self.output = output
373
374    def clear(self, i=-1):
375        """ Clear the current progress line indicator string.
376        """
377        try:
378            if hasattr(self.output, "isatty") and self.output.isatty():
379                self.output.write("\b" * (i if i != -1 else len(self.currstring)))
380            else:
381                self.output.seek(-i if i != -1 else -len(self.currstring), 2)
382        except Exception: ## If for some reason we failed
383            self.output.write("\n")
384
385    def getstring(self):
386        """ Return the progress indicator string.
387        """
388        progchar = int(round(float(self.state) * (self.charwidth - 5) / 100.0))
389        return self.title + "=" * (progchar) + ">" + " " * (self.charwidth\
390            - 5 - progchar) + "%3i" % int(round(self.state)) + "%"
391
392    def printline(self, string):
393        """ Print the ``string`` to the output file.
394        """
395        try:
396            self.clear()
397            self.output.write(string)
398            self.output.flush()
399        except Exception:
400            pass
401        self.currstring = string
402
403    def __call__(self, newstate=None):
404        """ Set the ``newstate`` as the current state of the progress bar.
405        ``newstate`` must be in the interval [0, 100].
406       
407        .. note:: ``set_state`` is the prefered way to set a new steate.
408       
409        :param newstate: The new state of the progress bar.
410        :type newstate: float
411         
412        """
413        if newstate is None:
414            self.advance()
415        else:
416            self.set_state(newstate)
417           
418    def set_state(self, newstate):
419        """ Set the ``newstate`` as the current state of the progress bar.
420        ``newstate`` must be in the interval [0, 100].
421       
422        :param newstate: The new state of the progress bar.
423        :type newstate: float
424       
425        """
426        if int(newstate) != int(self.state):
427            self.state = newstate
428            self.printline(self.getstring())
429        else:
430            self.state = newstate
431           
432    def advance(self, step=None):
433        """ Advance the current state by ``step``. If ``step`` is None use
434        the default step as set at class initialization.
435         
436        """
437        if step is None:
438            step = self.step
439           
440        newstate = self.state + step
441        self.set_state(newstate)
442
443    def finish(self):
444        """ Finish the progress bar (i.e. set the state to 100 and
445        print the final newline to the ``output`` file).
446        """
447        self.__call__(100)
448        self.output.write("\n")
449
450def progress_bar_milestones(count, iterations=100):
451    return set([int(i*count/float(iterations)) for i in range(iterations)])
452
453def lru_cache(maxsize=100):
454    """ A least recently used cache function decorator.
455    (Similar to the functools.lru_cache in python 3.2)
456    """
457   
458    def decorating_function(func):
459        import functools
460        cache = {}
461       
462        @functools.wraps(func)
463        def wrapped(*args, **kwargs):
464            key = args + tuple(sorted(kwargs.items()))
465            if key not in cache:
466                res = func(*args, **kwargs)
467                cache[key] = (time.time(), res)
468                if len(cache) > maxsize:
469                    key, (_, _) = min(cache.iteritems(), key=lambda item: item[1][0])
470                    del cache[key]
471            else:
472                _, res = cache[key]
473                cache[key] = (time.time(), res) # update the time
474               
475            return res
476       
477        def clear():
478            cache.clear()
479       
480        wrapped.clear = clear
481        wrapped._cache = cache
482       
483        return wrapped
484    return decorating_function
485
486#from Orange.misc.render import contextmanager
487from contextlib import contextmanager
488
489
490@contextmanager
491def member_set(obj, name, val):
492    """ A context manager that sets member ``name`` on ``obj`` to ``val``
493    and restores the previous value on exit.
494    """
495    old_val = getattr(obj, name, val)
496    setattr(obj, name, val)
497    yield
498    setattr(obj, name, old_val)
499   
500   
501class recursion_limit(object):
502    """ A context manager that sets a new recursion limit.
503   
504    """
505    def __init__(self, limit=1000):
506        self.limit = limit
507       
508    def __enter__(self):
509        self.old_limit = sys.getrecursionlimit()
510        sys.setrecursionlimit(self.limit)
511   
512    def __exit__(self, exc_type, exc_val, exc_tb):
513        sys.setrecursionlimit(self.old_limit)
514
515
516__doc__ += """\
517-----------------------------
518Deprecation utility functions
519-----------------------------
520
521.. autofunction:: Orange.misc.deprecation_warning
522
523.. autofunction:: Orange.misc.deprecated_members
524
525.. autofunction:: Orange.misc.deprecated_keywords
526
527.. autofunction:: Orange.misc.deprecated_attribute
528
529.. autofunction:: Orange.misc.deprecated_function_name
530
531"""
532
533import warnings
534def deprecation_warning(old, new, stacklevel=-2):
535    """ Raise a deprecation warning of an obsolete attribute access.
536   
537    :param old: Old attribute name (used in warning message).
538    :param new: New attribute name (used in warning message).
539   
540    """
541    warnings.warn("'%s' is deprecated. Use '%s' instead!" % (old, new), DeprecationWarning, stacklevel=stacklevel)
542   
543# We need to get the instancemethod type
544class _Foo():
545    def bar(self):
546        pass
547instancemethod = type(_Foo.bar)
548del _Foo
549
550function = type(lambda: None)
551
552class universal_set(set):
553    """ A universal set, pretends it contains everything.
554    """
555    def __contains__(self, value):
556        return True
557   
558from functools import wraps
559
560def deprecated_members(name_map, wrap_methods="all", in_place=True):
561    """ Decorate a class with properties for accessing attributes, and methods
562    with deprecated names. In addition methods from the `wrap_methods` list
563    will be wrapped to receive mapped keyword arguments.
564   
565    :param name_map: A dictionary mapping old into new names.
566    :type name_map: dict
567   
568    :param wrap_methods: A list of method names to wrap. Wrapped methods will
569        be called with mapped keyword arguments (by default all methods will
570        be wrapped).
571    :type wrap_methods: list
572   
573    :param in_place: If True the class will be modified in place, otherwise
574        it will be subclassed (default True).
575    :type in_place: bool
576   
577    Example ::
578           
579        >>> class A(object):
580        ...     def __init__(self, foo_bar="bar"):
581        ...         self.set_foo_bar(foo_bar)
582        ...     
583        ...     def set_foo_bar(self, foo_bar="bar"):
584        ...         self.foo_bar = foo_bar
585        ...
586        ... A = deprecated_members(
587        ... {"fooBar": "foo_bar",
588        ...  "setFooBar":"set_foo_bar"},
589        ... wrap_methods=["set_foo_bar", "__init__"])(A)
590        ...
591        ...
592        >>> a = A(fooBar="foo")
593        __main__:1: DeprecationWarning: 'fooBar' is deprecated. Use 'foo_bar' instead!
594        >>> print a.fooBar, a.foo_bar
595        foo foo
596        >>> a.setFooBar("FooBar!")
597        __main__:1: DeprecationWarning: 'setFooBar' is deprecated. Use 'set_foo_bar' instead!
598       
599    .. note:: This decorator does nothing if \
600        :obj:`Orange.misc.environ.orange_no_deprecated_members` environment \
601        variable is set to `True`.
602       
603    """
604    if environ.orange_no_deprecated_members:
605        return lambda cls: cls
606   
607    def is_wrapped(method):
608        """ Is member method already wrapped.
609        """
610        if getattr(method, "_deprecate_members_wrapped", False):
611            return True
612        elif hasattr(method, "im_func"):
613            im_func = method.im_func
614            return getattr(im_func, "_deprecate_members_wrapped", False)
615        else:
616            return False
617       
618    if wrap_methods == "all":
619        wrap_methods = universal_set()
620    elif not wrap_methods:
621        wrap_methods = set()
622       
623    def wrapper(cls):
624        cls_names = {}
625        # Create properties for accessing deprecated members
626        for old_name, new_name in name_map.items():
627            cls_names[old_name] = deprecated_attribute(old_name, new_name)
628           
629        # wrap member methods to map keyword arguments
630        for key, value in cls.__dict__.items():
631            if isinstance(value, (instancemethod, function)) \
632                and not is_wrapped(value) and key in wrap_methods:
633               
634                wrapped = deprecated_keywords(name_map)(value)
635                wrapped._deprecate_members_wrapped = True # A flag indicating this function already maps keywords
636                cls_names[key] = wrapped
637        if in_place:
638            for key, val in cls_names.items():
639                setattr(cls, key, val)
640            return cls
641        else:
642            return type(cls.__name__, (cls,), cls_names)
643       
644    return wrapper
645
646def deprecated_keywords(name_map):
647    """ Deprecates the keyword arguments of the function.
648   
649    Example ::
650   
651        >>> @deprecated_keywords({"myArg": "my_arg"})
652        ... def my_func(my_arg=None):
653        ...     print my_arg
654        ...
655        ...
656        >>> my_func(myArg="Arg")
657        __main__:1: DeprecationWarning: 'myArg' is deprecated. Use 'my_arg' instead!
658        Arg
659       
660    .. note:: This decorator does nothing if \
661        :obj:`Orange.misc.environ.orange_no_deprecated_members` environment \
662        variable is set to `True`.
663       
664    """
665    if environ.orange_no_deprecated_members:
666        return lambda func: func
667   
668    def decorator(func):
669        @wraps(func)
670        def wrap_call(*args, **kwargs):
671            kwargs = dict(kwargs)
672            for name in name_map:
673                if name in kwargs:
674                    deprecation_warning(name, name_map[name], stacklevel=3)
675                    kwargs[name_map[name]] = kwargs[name]
676                    del kwargs[name]
677            return func(*args, **kwargs)
678        return wrap_call
679    return decorator
680
681def deprecated_attribute(old_name, new_name):
682    """ Return a property object that accesses an attribute named `new_name`
683    and raises a deprecation warning when doing so.
684   
685    Example ::
686   
687        >>> class A(object):
688        ...     def __init__(self):
689        ...         self.my_attr = "123"
690        ...     myAttr = deprecated_attribute("myAttr", "my_attr")
691        ...
692        ...
693        >>> a = A()
694        >>> print a.myAttr
695        __main__:1: DeprecationWarning: 'myAttr' is deprecated. Use 'my_attr' instead!
696        123
697       
698    .. note:: This decorator does nothing and returns None if \
699        :obj:`Orange.misc.environ.orange_no_deprecated_members` environment \
700        variable is set to `True`.
701       
702    """
703    if environ.orange_no_deprecated_members:
704        return None
705   
706    def fget(self):
707        deprecation_warning(old_name, new_name, stacklevel=3)
708        return getattr(self, new_name)
709   
710    def fset(self, value):
711        deprecation_warning(old_name, new_name, stacklevel=3)
712        setattr(self, new_name, value)
713   
714    def fdel(self):
715        deprecation_warning(old_name, new_name, stacklevel=3)
716        delattr(self, new_name)
717   
718    prop = property(fget, fset, fdel,
719                    doc="A deprecated member '%s'. Use '%s' instead." % (old_name, new_name))
720    return prop
721
722
723def deprecated_function_name(func):
724    """ Return a wrapped function that raises an deprecation warning when
725    called. This should be used for deprecation of module level function names.
726   
727    Example ::
728   
729        >>> def func_a(arg):
730        ...    print "This is func_a  (used to be named funcA) called with", arg
731        ...
732        ...
733        >>> funcA = deprecated_function_name(func_a)
734        >>> funcA(None)
735         
736   
737    .. note:: This decorator does nothing and if \
738        :obj:`Orange.misc.environ.orange_no_deprecated_members` environment \
739        variable is set to `True`.
740       
741    """
742    if environ.orange_no_deprecated_members:
743        return func
744   
745    @wraps(func)
746    def wrapped(*args, **kwargs):
747        warnings.warn("Deprecated function name. Use %r instead!" % func.__name__,
748                      DeprecationWarning, stacklevel=2)
749        return func(*args, **kwargs)
750    return wrapped
751   
752
753"""
754Some utility functions common to Orange classes.
755 
756"""
757
758def _orange__new__(base=None):
759    """ Return an orange 'schizofrenic' __new__ class method.
760   
761    :param base: base orange class (default orange.Learner)
762    :type base: type
763         
764    Example::
765        class NewOrangeLearner(orange.Learner):
766            __new__ = _orange__new(orange.Learner)
767       
768    """
769    if base is None:
770        import Orange
771        base = Orange.core.Learner
772       
773    @wraps(base.__new__)
774    def _orange__new_wrapped(cls, data=None, **kwargs):
775        self = base.__new__(cls, **kwargs)
776        if data:
777            self.__init__(**kwargs)
778            return self.__call__(data)
779        else:
780            return self
781    return _orange__new_wrapped
782
783
784def _orange__reduce__(self):
785    """ A default __reduce__ method for orange types. Assumes the object
786    can be reconstructed with the call `constructor(__dict__)` where __dict__
787    if the stored (pickled) __dict__ attribute.
788   
789    Example::
790        class NewOrangeType(orange.Learner):
791            __reduce__ = _orange__reduce()
792    """ 
793    return type(self), (), dict(self.__dict__)
794
795
796demangleExamples = deprecated_function_name(demangle_examples)
797progressBarMilestones = deprecated_function_name(progress_bar_milestones)
798printVerbose = deprecated_function_name(print_verbose)
799
800# Must be imported after deprecation function definitions
801import selection
Note: See TracBrowser for help on using the repository browser.