Commits

Georg Brandl committed 595ef82

data loader: allow a numor string to load multiple datas at once

  • Participants
  • Parent commits 4925ec8

Comments (0)

Files changed (4)

 GUI
 ===
 
-* numor list extended syntax
+* remove/merge already existing sets
 * add'l parameters
 * multiple sets: new data/same model, other model
 * keyboard shortcuts in plot

File ufit/data/__init__.py

 
 """Data loading and treatment for ufit."""
 
-from numpy import array
-
 from ufit import UFitError
 from ufit.data import ill, nicos
-from ufit.data.dataset import Dataset, DataList
+from ufit.data.loader import Loader
+from ufit.data.dataset import Dataset
 
 data_formats = {
     'ill': ill,
            'read_data', 'as_data']
 
 
-# XXX standardize column names (or select a few to standardize)
-
-class Loader(object):
-    def __init__(self):
-        self.format = 'auto'
-        self.template = '%d'
-        self.sets = DataList()
-
-    def _get_reader(self, filename, fobj):
-        if self.format == 'auto':
-            for n, m in data_formats.iteritems():
-                if m.check_data(fobj):
-                    return m
-            raise UFitError('File %s not recognized')
-        return data_formats[self.format]
-
-    def load(self, n, xcol, ycol, mcol=None, mscale=1):
-        filename = self.template % n
-        fobj = open(filename, 'rb')
-        colnames, coldata, meta = \
-            self._get_reader(filename, fobj).read_data(filename, fobj)
-        dset = Dataset(colnames, coldata, meta, xcol, ycol, mcol, mscale)
-        self.sets[n] = dset
-        return dset
-
-    def guess_cols(self, n):
-        filename = self.template % n
-        fobj = open(filename, 'rb')
-        colnames, coldata, meta = \
-            self._get_reader(filename, fobj).read_data(filename, fobj)
-        xguess, yguess, mguess = None, None, None
-        if colnames[0].lower() in ('h', 'qh'):
-            deviations = array([(cs.max()-cs.min()) for cs in coldata.T[:4]])
-            xguess = colnames[deviations.argmax()]
-        else:
-            xguess = colnames[0]
-        maxcts = 0
-        maxmon = 0
-        nmon = 0
-        for i, colname in enumerate(colnames):
-            if colname.lower().startswith(('ctr', 'cnts', 'det')):
-                if coldata[:,i].sum() > maxcts:
-                    yguess = colname
-                    maxcts = coldata[:,i].sum()
-            if colname.startswith('mon') or colname.startswith('M'):
-                if coldata[:,i].sum() > maxmon:
-                    mguess = colname
-                    maxmon = coldata[:,i].sum()
-                    # use average monitor counts for normalization, but
-                    # round to 2 significant digits
-                    nmon = int(float('%.2g' % coldata[:,i].mean()))
-        return colnames, xguess, yguess, mguess, nmon
-
-
 # simplified interface for usage in noninteractive scripts
 
 global_loader = Loader()

File ufit/data/loader.py

+#  -*- coding: utf-8 -*-
+# *****************************************************************************
+# ufit, a universal scattering fitting suite
+#
+# Copyright (c) 2013, Georg Brandl.  All rights reserved.
+# Licensed under a 2-clause BSD license, see LICENSE.
+# *****************************************************************************
+
+"""Data loader object."""
+
+from numpy import array
+
+from ufit import UFitError
+from ufit.data.dataset import Dataset, DataList
+
+
+# XXX standardize column names (or select a few to standardize)
+
+
+class Loader(object):
+    def __init__(self):
+        self.format = 'auto'
+        self.template = '%d'
+        self.sets = DataList()
+
+    def _get_reader(self, filename, fobj):
+        from ufit.data import data_formats
+        if self.format == 'auto':
+            for n, m in data_formats.iteritems():
+                if m.check_data(fobj):
+                    return m
+            raise UFitError('File %s not recognized')
+        return data_formats[self.format]
+
+    def load(self, n, xcol, ycol, mcol=None, mscale=1):
+        filename = self.template % n
+        fobj = open(filename, 'rb')
+        colnames, coldata, meta = \
+            self._get_reader(filename, fobj).read_data(filename, fobj)
+        dset = Dataset(colnames, coldata, meta, xcol, ycol, mcol, mscale)
+        self.sets[n] = dset
+        return dset
+
+    def guess_cols(self, n):
+        filename = self.template % n
+        fobj = open(filename, 'rb')
+        colnames, coldata, meta = \
+            self._get_reader(filename, fobj).read_data(filename, fobj)
+        xguess, yguess, mguess = None, None, None
+        if colnames[0].lower() in ('h', 'qh'):
+            deviations = array([(cs.max()-cs.min()) for cs in coldata.T[:4]])
+            xguess = colnames[deviations.argmax()]
+        else:
+            xguess = colnames[0]
+        maxcts = 0
+        maxmon = 0
+        nmon = 0
+        for i, colname in enumerate(colnames):
+            if colname.lower().startswith(('ctr', 'cnts', 'det')):
+                if coldata[:,i].sum() > maxcts:
+                    yguess = colname
+                    maxcts = coldata[:,i].sum()
+            if colname.startswith('mon') or colname.startswith('M'):
+                if coldata[:,i].sum() > maxmon:
+                    mguess = colname
+                    maxmon = coldata[:,i].sum()
+                    # use average monitor counts for normalization, but
+                    # round to 2 significant digits
+                    nmon = int(float('%.2g' % coldata[:,i].mean()))
+        return colnames, xguess, yguess, mguess, nmon
+
+    def load_numors(self, nstring, binsize, xcol, ycol, mcol=None, mscale=1):
+        """Load a number of data files and merge them according to numor
+        list operations:
+
+        * ``,`` - put single files in individual data sets
+        * ``-`` - put sequential files in individual data sets
+        * ``+`` - merge single files
+        * ``>`` - merge sequential files
+        """
+        def toint(a):
+            try:
+                return int(a)
+            except ValueError:
+                raise UFitError('invalid file number: %r' % a)
+        # operator "precedence": ',' has lowest, then '+',
+        # then '-' and '>' (equal)
+        parts1 = nstring.split(',')
+        datasets = []
+        for part1 in parts1:
+            if '-' in part1:
+                a, b = map(toint, part1.split('-'))
+                datasets.extend(self.load(n, xcol, ycol, mcol, mscale)
+                                for n in range(a, b+1))
+            else:
+                parts2 = part1.split('+')
+                inner = []
+                for part2 in parts2:
+                    if '>' in part2:
+                        a, b = map(toint, part2.split('>'))
+                        ds = [self.load(n, xcol, ycol, mcol, mscale)
+                              for n in range(a, b+1)]
+                        inner.append(ds[0].merge(binsize, *ds[1:]))
+                    else:
+                        inner.append(
+                            self.load(toint(part2), xcol, ycol, mcol, mscale))
+                datasets.append(inner[0].merge(binsize, *inner[1:]))
+        return datasets

File ufit/gui/dataloader.py

         except Exception:
             QMessageBox.information(self, 'Error', 'Monitor scale must be integer.')
             return
+        numors = str(self.numors.text())
         try:
-            numors = map(int, str(self.numors.text()).split(','))
-        except Exception:
-            QMessageBox.information(self, 'Error',
-                                    'Numor list must be n1,n2,n3 etc.')
-            return
-        try:
-            datas = [self.loader.load(numor, xcol, ycol, mcol, mscale)
-                     for numor in numors]
+            datas = self.loader.load_numors(numors, prec,
+                                            xcol, ycol, mcol, mscale)
         except Exception, e:
             QMessageBox.information(self, 'Error', 'Could not read data: %s' % e)
             return
-        if len(datas) == 1:
-            data = datas[0]
-        else:
-            data = datas[0].merge(prec, *datas[1:])
         if final:
-            self.last_data = data
-            self.emit(SIGNAL('newData'), data)
+            self.last_data = datas[-1]
+            for data in datas:
+                self.emit(SIGNAL('newData'), data)
             self.emit(SIGNAL('closeRequest'))
         else:
             self.plotter.reset()
-            self.plotter.plot_data(data)
+            for data in datas:
+                self.plotter.plot_data(data)
             self.plotter.draw()
 
     def initialize(self):