tabulate: work around missing NumPy.
authorW. Trevor King <wking@tremily.us>
Sat, 20 Oct 2012 18:24:11 +0000 (14:24 -0400)
committerW. Trevor King <wking@tremily.us>
Sat, 20 Oct 2012 18:26:53 +0000 (14:26 -0400)
pygrader/tabulate.py

index 4e212797dc2b4ace5a7f498b3752e70665bf8dcf..d8a962d00615ac48ad4e28e6124993d25c285073 100644 (file)
 # You should have received a copy of the GNU General Public License along with
 # pygrader.  If not, see <http://www.gnu.org/licenses/>.
 
+import math as _math  # for numpy workarounds and testing
 import sys as _sys
 
+_numpy_import_error = None
 try:
     import numpy as _numpy
-except ImportError:
-    raise  # TODO work around
+except ImportError as e:
+    _numpy = None
+    _numpy_import_error = e
 
+from . import LOG as _LOG
 from .color import standard_colors as _standard_colors
 from .color import write_color as _write_color
 
 
+def _mean(iterable):  # missing-numpy workaround
+    """Return the mean of a list of items.
+
+    >>> print(_mean([0,1,2,3,4,5,6]))
+    3.0
+    """
+    length = len(iterable)
+    return sum(iterable) / float(length)
+
+def _std(iterable):  # missing-numpy workaround
+    """Return the standard deviation of a list of items.
+
+    >>> print(_std([0,1,2,3,4,5,6]))
+    2.0
+    """
+    length = len(iterable)
+    m = _mean(iterable)
+    return _math.sqrt(sum((x-m)**2 for x in iterable) / length)
+
+if _numpy is None:
+    _statistics_container = list
+else:
+    _statistics_container = _numpy.array
+
+def _statistic(iterabale, statistic):
+    """Calculate statistics on an list of numbers
+    """
+    global _numpy_import_error
+    if _numpy_import_error:
+        assert _numpy_import_error is not None
+        _LOG.warning('error importing numpy, falling back to workarounds')
+        _LOG.warning(str(_numpy_import_error))
+        _numpy_import_error = None
+    if stat == 'Mean':
+        if _numpy is None:  # work around missing numpy
+            return _mean(iterable)
+        else:
+            return gs.mean()
+    elif stat == 'Std. Dev.':
+        if _numpy is None:  # work around missing numpy
+            sval = _std(iterable)
+        else:
+            return gs.std()
+    else:
+        raise NotImplementedError(stat)
+
 def tabulate(course, statistics=False, stream=None, use_color=None, **kwargs):
     """Return a table of student's grades to date
     """
@@ -69,23 +119,13 @@ def tabulate(course, statistics=False, stream=None, use_color=None, **kwargs):
                 color = colors[(i+1)%len(colors)]
                 grades = [g for g in course.grades
                           if g.assignment == assignment]
-                gs = _numpy.array([g.points for g in grades])
-                if stat == 'Mean':
-                    sval = gs.mean()
-                elif stat == 'Std. Dev.':
-                    sval = gs.std()
-                else:
-                    raise NotImplementedError(stat)
+                gs = _statistics_container([g.points for g in grades])
+                sval = _statistic(gs, statistic=stat)
                 string = '\t{:.2f}'.format(sval)
                 _write_color(string=string, color=color, stream=stream)
             if len(assignments) == len(course.assignments):
-                gs = _numpy.array([course.total(s) for s in students])
-                if stat == 'Mean':
-                    sval = gs.mean()
-                elif stat == 'Std. Dev.':
-                    sval = gs.std()
-                else:
-                    raise NotImplementedError(stat)
+                gs = _statistics_container([course.total(s) for s in students])
+                sval = _statistic(gs, statistic=stat)
                 string = '\t{}'.format(sval)
                 color = colors[(i+2)%len(colors)]
                 _write_color(string=string, color=color, stream=stream)