Source code for plugins.datareader

#
##
##  SPDX-FileCopyrightText: © 2007-2023 Benedict Verhegghe <bverheg@gmail.com>
##  SPDX-License-Identifier: GPL-3.0-or-later
##
##  This file is part of pyFormex 3.4  (Thu Nov 16 18:07:39 CET 2023)
##  pyFormex is a tool for generating, manipulating and transforming 3D
##  geometrical models by sequences of mathematical operations.
##  Home page: https://pyformex.org
##  Project page: https://savannah.nongnu.org/projects/pyformex/
##  Development: https://gitlab.com/bverheg/pyformex
##  Distributed under the GNU General Public License version 3 or later.
##
##  This program 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 of the License, or
##  (at your option) any later version.
##
##  This program 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 this program.  If not, see http://www.gnu.org/licenses/.
##
#
"""Numerical data reader

"""

__all__ = ['splitFloat', 'readData']

import re

_re_float = re.compile(r'[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?')
_re_float_string = re.compile(r'(?P<float>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)(?P<string>.*)')


[docs]def splitFloat(s): """Match a floating point number at the beginning of a string. Parameters ---------- s: str A string that starts with a numerical part (float). Returns ------- tuple (float, str) | None: If the beginning of the string matches a floating point number, returns a tuple with the float and the remainder of the string. Else, return None Examples -------- >>> splitFloat('123e4rt345e6') [1230000.0, 'rt345e6'] """ m = _re_float_string.match(s) if m: return [float(m.group('float')), m.group('string')] return None
[docs]def convertData(s, to): """Convert numerical data to a requested type or units. Converts a string including a numerical value and optional units to the requested type or units. Parameters ---------- s: str A string containing a numerical value and an optional units string, optionally separated by whitespace. E.g. '10 kg'. to: str The units or type to which the value from the string ``s`` is to be converted. If ``s`` contains units, the specified ``to`` units should be compatible (i.e. for the same physical quantity). If a field has no units, the target ``to`` value can also be 'int' or 'float'. Returns ------- int | float The numerical value read from the string ``s``, converted to the specified units or type ``to``. Examples -------- >>> convertData('1 inch', 'mm') 25.4 >>> convertData('1', 'mm') 1.0 >>> convertData('14.5e3', 'kg') 14500.0 >>> convertData('12 inch', 'float') 12.0 >>> convertData('17.5', 'int') 17 Warning ------- You need to have the GNU ``units`` command installed for the unit conversion to work. """ # IMPORT HERE: LATE IMPORT from pyformex.plugins import units s = s.strip() m = _re_float.match(s) if not m: raise ValueError(f"No value found in '{s}'") if m.end() == len(s) or to in ['int', 'float']: # no units or no units conversion: handle ourself val = float(s[:m.end()]) if to == 'int': val = int(val) else: # let 'units' do the conversion val = float(units.convertUnits(s, to)) return val
[docs]def readData(s, to, strict=False): """Read values from a string matching the units/type specifications. This is a powerful function for reading, interpreting and converting numerical data from a string. Parameters ---------- s: str A string containing comma-separated fields. Each field is either a numerical value, or a numerical value followed by a units string. The value and the units can optionally be separated by whitespace. to: list of str A list of units to which the fields in the string ``s`` should be converted. The length of the list is usually equal to the number of fields in the string ``s``. If a field has units, the corresponding ``to`` should be a compatible unit (i.e. for the same physical quantity). If a field has no units, the target ``to`` value can also be 'int' or 'float'. strict: bool If True, the length of the ``to`` list should exactly match the number of fields in ``s``. The default (False) allows unequal lengths and will only proceed until the shortest is exhausted. Returns ------- list A list of int/float values read from the string s. With ``strict=True`` the list will have the same length as the number of fields in ``s`` and the number of items in ``to``. Else, it will have the shortest length of the two. Where the field contains a units designator, the value is converted to the requested ``to`` units. designator.Fields in the string s are separated by Examples -------- >>> readData('1 inch', ['mm']) [25.4] >>> readData('12, 13s, 14.5e3, 12 inch, 1hr, 31kg, 5MPa', ... ['int','float','kg','cm','s','g','kN/m**2']) [12, 13.0, 14500.0, 30.48, 3600.0, 31000.0, 5000.0] Warning ------- You need to have the GNU ``units`` command installed for the unit conversion to work. """ data = s.split(',') if strict and len(data) != len(to): raise ValueError(f"'{s} does not match {to}") return [convertData(si, ti) for ti, si in zip(to, data)]
# End