python.observation.util

Created on Fri Jul 29 12:48:16 2022

@author: philippe@loco-labs.io

  1# -*- coding: utf-8 -*-
  2"""
  3Created on Fri Jul 29 12:48:16 2022
  4
  5@author: philippe@loco-labs.io
  6"""
  7
  8from collections import Counter
  9from itertools import product
 10import datetime
 11import re
 12import numpy as np
 13import math
 14
 15from observation.timeslot import TimeInterval
 16from observation.esconstante import ES, _classval
 17from observation.esvalue_base import ESValue
 18
 19
 20def identity(*args, **kwargs):
 21    '''return the same value as args or kwargs'''
 22    if len(args) > 0:
 23        return args[0]
 24    if len(kwargs) > 0:
 25        return kwargs[list(kwargs.keys())[0]]
 26    return None
 27
 28
 29class util:
 30    ''' common functions for Field and Dataset class'''
 31# %% util
 32    c1 = re.compile('\d+\.?\d*[,\-_ ;:]')
 33    c2 = re.compile('[,\-_ ;:]\d+\.?\d*')
 34
 35    @staticmethod
 36    def canonorder(lenidx):
 37        '''return a list of crossed keys from a list of number of values'''
 38        listrange = [range(lidx) for lidx in lenidx]
 39        return util.transpose(util.list(list(product(*listrange))))
 40
 41    """@staticmethod
 42    def cast(val, dtype=None, string=True, default=None, maxlen=None):
 43        ''' convert val in the type defined by the string dtype'''
 44        typeval = val.__class__.__name__
 45        if dtype == 'name':
 46            if typeval in ES.className and val.name:
 47                name = val.name
 48            else:
 49                name = default
 50            if maxlen:
 51                return name[:maxlen]
 52            return name
 53        if dtype == 'simple':
 54            if typeval in ES.className:
 55                return val.vSimple(string=string)
 56            else:
 57                return val
 58        if dtype == 'json':
 59            if typeval in ES.className:
 60                return val.json()
 61            else:
 62                return val
 63        if dtype == 'obj':
 64            if typeval in ES.className:
 65                return val.to_obj(encoded=False)
 66            else:
 67                return val
 68        if dtype == 'str':
 69            if maxlen:
 70                return str(val)[:maxlen]
 71            else:
 72                return str(val)
 73        if not dtype:
 74            return ESValue._castsimple(val)
 75        if dtype == 'int':
 76            try:
 77                return int(val.lstrip())
 78            except:
 79                return math.nan
 80        if dtype == 'float':
 81            if typeval in ['float', 'int']:
 82                return float(val)
 83            if typeval in ES.className:
 84                return val.to_float()
 85            try:
 86                return float(val.lstrip())
 87            except:
 88                return math.nan
 89        if dtype == 'datetime':
 90            try:
 91                return TimeInterval._dattz(datetime.datetime.fromisoformat(val))
 92            except ValueError:
 93                return datetime.datetime.min.replace(tzinfo=datetime.timezone.utc)
 94        if dtype == 'coord':
 95            if util.c1.search(val) and util.c2.search(val):
 96                return [float(util.c1.search(val)[0][:-1]), float(util.c2.search(val)[0][1:])]
 97            else:
 98                return [-1, -1]
 99        if typeval in ES.ESclassName:
100            return val
101        if dtype in ES.typeName:  # tous les types environmental sensing
102            return ESValue.from_obj(val, ES.typeName[dtype])
103        raise utilError("dtype : " + dtype + " inconsistent with data")
104
105    @staticmethod
106    def castobj(lis, classvalue=None):
107        ''' convert a list of values in the ESValue defined by the string classvalue'''
108        if not lis:
109            return lis
110        return [util.castval(val, classvalue) for val in lis]
111
112    @staticmethod
113    def castval(val, classvalue=None):
114        ''' convert a value in the ESValue defined by the string classvalue'''
115        classn, name, value = ESValue._decodevalue(val)
116        if classn:
117            classvalue = classn
118        if classvalue is None:
119            dtype = None
120        else:
121            dtype = ES.valname[classvalue]
122
123        if dtype is None:
124            return util.cast(val)
125        if classvalue in ES.ESclassName:  # ESValue.cast()
126            return util.cast(ESValue.from_obj(val, classvalue), dtype)
127        if classvalue == ES.ES_clsName:   # cast auto ESValue
128            return _classval()[ESValue.valClassName(val)].from_obj(val)
129            # return _classval()[ESValue.valClassName(val)](val)
130        if classvalue in ES.className:
131            return _classval()[classvalue].obj(value)
132            # return _classval()[classvalue](value)
133        return val"""
134
135    @staticmethod
136    def couplinginfos(l1, l2):
137        '''return a dict with the coupling info between two list'''
138        if not l1 or not l2:
139            return {'dist': 0, 'rateder': 0, 'disttomin': 0, 'disttomax': 0,
140                    'distmin': 0, 'distmax': 0, 'diff': 0, 'typecoupl': 'null'}
141        ls = len(util.tocodec(l1))
142        lo = len(util.tocodec(l2))
143        x0 = max(ls, lo)
144        x1 = ls * lo
145        diff = abs(ls - lo)
146        if min(ls, lo) == 1:
147            if ls == 1:
148                typec = 'derived'
149            else:
150                typec = 'derive'
151            return {'dist': x0, 'rateder': 0, 'disttomin': 0, 'disttomax': 0,
152                    'distmin': x0, 'distmax': x1, 'diff': diff, 'typecoupl': typec}
153        x = len(util.tocodec([tuple((v1, v2)) for v1, v2 in zip(l1, l2)]))
154        dic = {'dist': x, 'rateder': (x - x0) / (x1 - x0),
155               'disttomin': x - x0,  'disttomax': x1 - x,
156               'distmin': x0, 'distmax': x1, 'diff': diff}
157        if dic['rateder'] == 0 and dic['diff'] == 0:
158            dic['typecoupl'] = 'coupled'
159        elif dic['rateder'] == 0 and ls < lo:
160            dic['typecoupl'] = 'derived'
161        elif dic['rateder'] == 0 and ls > lo:
162            dic['typecoupl'] = 'derive'
163        elif dic['rateder'] == 1:
164            dic['typecoupl'] = 'crossed'
165        elif ls < lo:
166            dic['typecoupl'] = 'linked'
167        else:
168            dic['typecoupl'] = 'link'
169        return dic
170
171    @staticmethod
172    def filter(func, lis, res, *args, **kwargs):
173        '''apply "func" to each value of "lis" and tests if equals "res".
174        Return the list of index with True result.'''
175        lisf = util.funclist(lis, func, *args, **kwargs)
176        return [i for i in range(len(lis)) if lisf[i] == res]
177
178    @staticmethod
179    def funclist(value, func, *args, **kwargs):
180        '''return the function func applied to the object value with parameters args and kwargs'''
181        if func in (None, []):
182            return value
183        lis = []
184        if not (isinstance(value, list) or value.__class__.__name__ in ['Field', 'Dataset', 'Observation']):
185            listval = [value]
186        else:
187            listval = value
188        for val in listval:
189            try:
190                lis.append(val.func(*args, **kwargs))
191            except:
192                try:
193                    lis.append(func(val, *args, **kwargs))
194                except:
195                    try:
196                        lis.append(listval.func(val, *args, **kwargs))
197                    except:
198                        try:
199                            lis.append(func(listval, val, *args, **kwargs))
200                        except:
201                            raise utilError("unable to apply func")
202        if len(lis) == 1:
203            return lis[0]
204        return lis
205
206    @staticmethod
207    def hash(listval):
208        ''' return sum of hash values in the list'''
209        # return sum([hash(i) for i in listval])
210        return hash(tuple(listval))
211
212    @staticmethod
213    def idxfull(setidx):
214        '''return additional keys for each index in the setidx list to have crossed setidx'''
215        setcodec = [set(idx.keys) for idx in setidx]
216        lenfull = util.mul([len(codec) for codec in setcodec])
217        if lenfull <= len(setidx[0]):
218            return []
219        complet = Counter(list(product(*setcodec)))
220        complet.subtract(
221            Counter(util.tuple(util.transpose([idx.keys for idx in setidx]))))
222        keysadd = util.transpose(util.list(list(complet.elements())))
223        if not keysadd:
224            return []
225        return keysadd
226
227    @staticmethod
228    def isEqual(value, tovalue=None, **kwargs):
229        ''' return True if value and tovalue are equal'''
230        return value.__class__.__name__ == tovalue.__class__.__name__ and \
231            value == tovalue
232
233    @staticmethod
234    def isNotEqual(value, tovalue=None, **kwargs):
235        ''' return True if value and tovalue are not equal'''
236        return value.__class__.__name__ != tovalue.__class__.__name__ or \
237            value != tovalue
238
239    @staticmethod
240    def isNotNull(value, nullvalue=None, **kwargs):
241        '''return boolean. True if value' is not a NullValue'''
242        if value.__class__.__name__ in ES.valname:
243            return value != value.__class__(nullvalue)
244        return not value is None
245
246    @staticmethod
247    def idxlink(ref, l2):
248        ''' return a dict for each different tuple (ref value, l2 value)'''
249        return dict(set(zip(ref, l2)))
250        #lis = set(util.tuple(util.transpose([ref, l2])))
251        # if not len(lis) == len(set(ref)):
252        #    return {}
253        # return dict(lis)
254
255    @staticmethod
256    def json(val, **option):
257        datecast = True
258        if 'datetime' in option:
259            datecast = option['datetime']
260        val = ESValue.uncastsimple(val, datecast)
261        if isinstance(val, (str, int, float, bool, list, dict, type(None), bytes, datetime.datetime)):
262            return val
263        if option['simpleval']:
264            return val.json(**option)
265        if val.__class__.__name__ in ES.ESclassName:  # ESValue
266            if not option['typevalue']:
267                return val.json(**option)
268            else:
269                return {ES.valname[val.__class__.__name__]: val.json(**option)}
270        if val.__class__.__name__ == 'Dataset':
271            return {ES.ili_valName: val.json(**option)}
272        if val.__class__.__name__ == 'Field':
273            return {ES.iin_valName: val.json(**option)}
274        if val.__class__.__name__ == 'Observation':
275            return {ES.obs_valName: val.to_obj(**option)}
276
277    @staticmethod
278    def list(tuplelists):
279        '''transform a list of tuples in a list of lists'''
280        return list(map(list, tuplelists))
281
282    @staticmethod
283    def listed(idx):
284        '''transform a tuple of tuple in a list of list'''
285        return [val if not isinstance(val, tuple) else util.listed(val) for val in idx]
286
287    @staticmethod
288    def mul(values):
289        '''return the product of values in a list or tuple (math.prod)'''
290        mul = 1
291        for val in values:
292            mul *= val
293        return mul
294
295    @staticmethod
296    def pparent(row, infos):
297        '''return field 'pparent' '''
298        if row < 0:
299            return row
300        field = infos[row]
301        # if field['pparent'] != 0:
302        if field['pparent'] != -2:
303            return field['pparent']
304        if field['cat'] == 'primary':
305            field['pparent'] = field['num']
306        elif field['cat'] == 'unique':
307            field['pparent'] = -1
308        else:
309            field['pparent'] = util.pparent(field['parent'], infos)
310        return field['pparent']
311
312    @staticmethod
313    def pparent2(row, infos):
314        '''return field 'pparent' '''
315        if row < 0:
316            return row
317        field = infos[row]
318        # if field['pparent'] != 0:
319        if field['pparent'] != -2:
320            return field['pparent']
321        if field['cat'] == 'primary':
322            field['pparent'] = field['num']
323        elif field['cat'] == 'unique':
324            field['pparent'] = -1
325        else:
326            field['pparent'] = util.pparent2(field['parent'], infos)
327        return field['pparent']
328
329    @staticmethod
330    def reindex(oldkeys, oldcodec, newcodec):
331        '''new keys with new order of codec'''
332        dic = {newcodec[i]: i for i in range(len(newcodec))}
333        return [dic[oldcodec[key]] for key in oldkeys]
334
335    @staticmethod
336    def reorder(values, sort=None):
337        '''return a new values list following the order define by sort'''
338        if not sort:
339            return values
340        return [values[ind] for ind in sort]
341
342    @staticmethod
343    def resetidx(values):
344        '''return codec and keys from a list of values'''
345        codec = util.tocodec(values)
346        return (codec, util.tokeys(values, codec))
347
348    @staticmethod
349    def str(listvalues):
350        '''return a list with values in the str format'''
351        return [str(val) for val in listvalues]
352
353    @staticmethod
354    def tokeys(values, codec=None):
355        ''' return a list of keys from a list of values'''
356        if not codec:
357            codec = util.tocodec(values)
358        dic = {codec[i]: i for i in range(len(codec))}  # !!!!long
359        keys = [dic[val] for val in values]    # hyper long
360        return keys
361
362    @staticmethod
363    def tovalues(keys, codec):
364        '''return a list of values from a list of keys and a list of codec values'''
365        return [codec[key] for key in keys]
366
367    @staticmethod
368    def tonumpy(valuelist, func=None, **kwargs):
369        '''return a Numpy Array from a list of values converted by func'''
370        if func is None:
371            func = identity
372        if func == 'index':
373            return np.array(list(range(len(valuelist))))
374        valList = util.funclist(valuelist, func, **kwargs)
375        if isinstance(valList[0], str):
376            try:
377                datetime.datetime.fromisoformat(valList[0])
378            except:
379                return np.array(valList)
380            return np.array(valList, dtype=np.datetime64)
381        if isinstance(valList[0], datetime.datetime):
382            return np.array(valList, dtype=np.datetime64)
383        return np.array(valList)
384
385    @staticmethod
386    def tocodec(values, keys=None):
387        '''extract a list of unique values'''
388        if not keys:
389            #return list(set(values))
390            return list(dict.fromkeys(values))
391        ind, codec = zip(*sorted(set(zip(keys, values))))
392        return list(codec)
393
394    @staticmethod
395    def transpose(idxlist):
396        '''exchange row/column in a list of list'''
397        if not isinstance(idxlist, list):
398            raise utilError('index not transposable')
399        if not idxlist:
400            return []
401        size = min([len(ix) for ix in idxlist])
402        return [[ix[ind] for ix in idxlist] for ind in range(size)]
403
404    @staticmethod
405    def tuple(idx):
406        '''transform a list of list in a list of tuple'''
407        return [val if not isinstance(val, list) else tuple(val) for val in idx]
408
409    @staticmethod
410    def tupled(idx):
411        '''transform a list of list in a tuple of tuple'''
412        return tuple([val if not isinstance(val, list) else util.tupled(val) for val in idx])
413
414class utilError(Exception):
415    ''' util Exception'''
416    # pass
def identity(*args, **kwargs):
21def identity(*args, **kwargs):
22    '''return the same value as args or kwargs'''
23    if len(args) > 0:
24        return args[0]
25    if len(kwargs) > 0:
26        return kwargs[list(kwargs.keys())[0]]
27    return None

return the same value as args or kwargs

class util:
 30class util:
 31    ''' common functions for Field and Dataset class'''
 32# %% util
 33    c1 = re.compile('\d+\.?\d*[,\-_ ;:]')
 34    c2 = re.compile('[,\-_ ;:]\d+\.?\d*')
 35
 36    @staticmethod
 37    def canonorder(lenidx):
 38        '''return a list of crossed keys from a list of number of values'''
 39        listrange = [range(lidx) for lidx in lenidx]
 40        return util.transpose(util.list(list(product(*listrange))))
 41
 42    """@staticmethod
 43    def cast(val, dtype=None, string=True, default=None, maxlen=None):
 44        ''' convert val in the type defined by the string dtype'''
 45        typeval = val.__class__.__name__
 46        if dtype == 'name':
 47            if typeval in ES.className and val.name:
 48                name = val.name
 49            else:
 50                name = default
 51            if maxlen:
 52                return name[:maxlen]
 53            return name
 54        if dtype == 'simple':
 55            if typeval in ES.className:
 56                return val.vSimple(string=string)
 57            else:
 58                return val
 59        if dtype == 'json':
 60            if typeval in ES.className:
 61                return val.json()
 62            else:
 63                return val
 64        if dtype == 'obj':
 65            if typeval in ES.className:
 66                return val.to_obj(encoded=False)
 67            else:
 68                return val
 69        if dtype == 'str':
 70            if maxlen:
 71                return str(val)[:maxlen]
 72            else:
 73                return str(val)
 74        if not dtype:
 75            return ESValue._castsimple(val)
 76        if dtype == 'int':
 77            try:
 78                return int(val.lstrip())
 79            except:
 80                return math.nan
 81        if dtype == 'float':
 82            if typeval in ['float', 'int']:
 83                return float(val)
 84            if typeval in ES.className:
 85                return val.to_float()
 86            try:
 87                return float(val.lstrip())
 88            except:
 89                return math.nan
 90        if dtype == 'datetime':
 91            try:
 92                return TimeInterval._dattz(datetime.datetime.fromisoformat(val))
 93            except ValueError:
 94                return datetime.datetime.min.replace(tzinfo=datetime.timezone.utc)
 95        if dtype == 'coord':
 96            if util.c1.search(val) and util.c2.search(val):
 97                return [float(util.c1.search(val)[0][:-1]), float(util.c2.search(val)[0][1:])]
 98            else:
 99                return [-1, -1]
100        if typeval in ES.ESclassName:
101            return val
102        if dtype in ES.typeName:  # tous les types environmental sensing
103            return ESValue.from_obj(val, ES.typeName[dtype])
104        raise utilError("dtype : " + dtype + " inconsistent with data")
105
106    @staticmethod
107    def castobj(lis, classvalue=None):
108        ''' convert a list of values in the ESValue defined by the string classvalue'''
109        if not lis:
110            return lis
111        return [util.castval(val, classvalue) for val in lis]
112
113    @staticmethod
114    def castval(val, classvalue=None):
115        ''' convert a value in the ESValue defined by the string classvalue'''
116        classn, name, value = ESValue._decodevalue(val)
117        if classn:
118            classvalue = classn
119        if classvalue is None:
120            dtype = None
121        else:
122            dtype = ES.valname[classvalue]
123
124        if dtype is None:
125            return util.cast(val)
126        if classvalue in ES.ESclassName:  # ESValue.cast()
127            return util.cast(ESValue.from_obj(val, classvalue), dtype)
128        if classvalue == ES.ES_clsName:   # cast auto ESValue
129            return _classval()[ESValue.valClassName(val)].from_obj(val)
130            # return _classval()[ESValue.valClassName(val)](val)
131        if classvalue in ES.className:
132            return _classval()[classvalue].obj(value)
133            # return _classval()[classvalue](value)
134        return val"""
135
136    @staticmethod
137    def couplinginfos(l1, l2):
138        '''return a dict with the coupling info between two list'''
139        if not l1 or not l2:
140            return {'dist': 0, 'rateder': 0, 'disttomin': 0, 'disttomax': 0,
141                    'distmin': 0, 'distmax': 0, 'diff': 0, 'typecoupl': 'null'}
142        ls = len(util.tocodec(l1))
143        lo = len(util.tocodec(l2))
144        x0 = max(ls, lo)
145        x1 = ls * lo
146        diff = abs(ls - lo)
147        if min(ls, lo) == 1:
148            if ls == 1:
149                typec = 'derived'
150            else:
151                typec = 'derive'
152            return {'dist': x0, 'rateder': 0, 'disttomin': 0, 'disttomax': 0,
153                    'distmin': x0, 'distmax': x1, 'diff': diff, 'typecoupl': typec}
154        x = len(util.tocodec([tuple((v1, v2)) for v1, v2 in zip(l1, l2)]))
155        dic = {'dist': x, 'rateder': (x - x0) / (x1 - x0),
156               'disttomin': x - x0,  'disttomax': x1 - x,
157               'distmin': x0, 'distmax': x1, 'diff': diff}
158        if dic['rateder'] == 0 and dic['diff'] == 0:
159            dic['typecoupl'] = 'coupled'
160        elif dic['rateder'] == 0 and ls < lo:
161            dic['typecoupl'] = 'derived'
162        elif dic['rateder'] == 0 and ls > lo:
163            dic['typecoupl'] = 'derive'
164        elif dic['rateder'] == 1:
165            dic['typecoupl'] = 'crossed'
166        elif ls < lo:
167            dic['typecoupl'] = 'linked'
168        else:
169            dic['typecoupl'] = 'link'
170        return dic
171
172    @staticmethod
173    def filter(func, lis, res, *args, **kwargs):
174        '''apply "func" to each value of "lis" and tests if equals "res".
175        Return the list of index with True result.'''
176        lisf = util.funclist(lis, func, *args, **kwargs)
177        return [i for i in range(len(lis)) if lisf[i] == res]
178
179    @staticmethod
180    def funclist(value, func, *args, **kwargs):
181        '''return the function func applied to the object value with parameters args and kwargs'''
182        if func in (None, []):
183            return value
184        lis = []
185        if not (isinstance(value, list) or value.__class__.__name__ in ['Field', 'Dataset', 'Observation']):
186            listval = [value]
187        else:
188            listval = value
189        for val in listval:
190            try:
191                lis.append(val.func(*args, **kwargs))
192            except:
193                try:
194                    lis.append(func(val, *args, **kwargs))
195                except:
196                    try:
197                        lis.append(listval.func(val, *args, **kwargs))
198                    except:
199                        try:
200                            lis.append(func(listval, val, *args, **kwargs))
201                        except:
202                            raise utilError("unable to apply func")
203        if len(lis) == 1:
204            return lis[0]
205        return lis
206
207    @staticmethod
208    def hash(listval):
209        ''' return sum of hash values in the list'''
210        # return sum([hash(i) for i in listval])
211        return hash(tuple(listval))
212
213    @staticmethod
214    def idxfull(setidx):
215        '''return additional keys for each index in the setidx list to have crossed setidx'''
216        setcodec = [set(idx.keys) for idx in setidx]
217        lenfull = util.mul([len(codec) for codec in setcodec])
218        if lenfull <= len(setidx[0]):
219            return []
220        complet = Counter(list(product(*setcodec)))
221        complet.subtract(
222            Counter(util.tuple(util.transpose([idx.keys for idx in setidx]))))
223        keysadd = util.transpose(util.list(list(complet.elements())))
224        if not keysadd:
225            return []
226        return keysadd
227
228    @staticmethod
229    def isEqual(value, tovalue=None, **kwargs):
230        ''' return True if value and tovalue are equal'''
231        return value.__class__.__name__ == tovalue.__class__.__name__ and \
232            value == tovalue
233
234    @staticmethod
235    def isNotEqual(value, tovalue=None, **kwargs):
236        ''' return True if value and tovalue are not equal'''
237        return value.__class__.__name__ != tovalue.__class__.__name__ or \
238            value != tovalue
239
240    @staticmethod
241    def isNotNull(value, nullvalue=None, **kwargs):
242        '''return boolean. True if value' is not a NullValue'''
243        if value.__class__.__name__ in ES.valname:
244            return value != value.__class__(nullvalue)
245        return not value is None
246
247    @staticmethod
248    def idxlink(ref, l2):
249        ''' return a dict for each different tuple (ref value, l2 value)'''
250        return dict(set(zip(ref, l2)))
251        #lis = set(util.tuple(util.transpose([ref, l2])))
252        # if not len(lis) == len(set(ref)):
253        #    return {}
254        # return dict(lis)
255
256    @staticmethod
257    def json(val, **option):
258        datecast = True
259        if 'datetime' in option:
260            datecast = option['datetime']
261        val = ESValue.uncastsimple(val, datecast)
262        if isinstance(val, (str, int, float, bool, list, dict, type(None), bytes, datetime.datetime)):
263            return val
264        if option['simpleval']:
265            return val.json(**option)
266        if val.__class__.__name__ in ES.ESclassName:  # ESValue
267            if not option['typevalue']:
268                return val.json(**option)
269            else:
270                return {ES.valname[val.__class__.__name__]: val.json(**option)}
271        if val.__class__.__name__ == 'Dataset':
272            return {ES.ili_valName: val.json(**option)}
273        if val.__class__.__name__ == 'Field':
274            return {ES.iin_valName: val.json(**option)}
275        if val.__class__.__name__ == 'Observation':
276            return {ES.obs_valName: val.to_obj(**option)}
277
278    @staticmethod
279    def list(tuplelists):
280        '''transform a list of tuples in a list of lists'''
281        return list(map(list, tuplelists))
282
283    @staticmethod
284    def listed(idx):
285        '''transform a tuple of tuple in a list of list'''
286        return [val if not isinstance(val, tuple) else util.listed(val) for val in idx]
287
288    @staticmethod
289    def mul(values):
290        '''return the product of values in a list or tuple (math.prod)'''
291        mul = 1
292        for val in values:
293            mul *= val
294        return mul
295
296    @staticmethod
297    def pparent(row, infos):
298        '''return field 'pparent' '''
299        if row < 0:
300            return row
301        field = infos[row]
302        # if field['pparent'] != 0:
303        if field['pparent'] != -2:
304            return field['pparent']
305        if field['cat'] == 'primary':
306            field['pparent'] = field['num']
307        elif field['cat'] == 'unique':
308            field['pparent'] = -1
309        else:
310            field['pparent'] = util.pparent(field['parent'], infos)
311        return field['pparent']
312
313    @staticmethod
314    def pparent2(row, infos):
315        '''return field 'pparent' '''
316        if row < 0:
317            return row
318        field = infos[row]
319        # if field['pparent'] != 0:
320        if field['pparent'] != -2:
321            return field['pparent']
322        if field['cat'] == 'primary':
323            field['pparent'] = field['num']
324        elif field['cat'] == 'unique':
325            field['pparent'] = -1
326        else:
327            field['pparent'] = util.pparent2(field['parent'], infos)
328        return field['pparent']
329
330    @staticmethod
331    def reindex(oldkeys, oldcodec, newcodec):
332        '''new keys with new order of codec'''
333        dic = {newcodec[i]: i for i in range(len(newcodec))}
334        return [dic[oldcodec[key]] for key in oldkeys]
335
336    @staticmethod
337    def reorder(values, sort=None):
338        '''return a new values list following the order define by sort'''
339        if not sort:
340            return values
341        return [values[ind] for ind in sort]
342
343    @staticmethod
344    def resetidx(values):
345        '''return codec and keys from a list of values'''
346        codec = util.tocodec(values)
347        return (codec, util.tokeys(values, codec))
348
349    @staticmethod
350    def str(listvalues):
351        '''return a list with values in the str format'''
352        return [str(val) for val in listvalues]
353
354    @staticmethod
355    def tokeys(values, codec=None):
356        ''' return a list of keys from a list of values'''
357        if not codec:
358            codec = util.tocodec(values)
359        dic = {codec[i]: i for i in range(len(codec))}  # !!!!long
360        keys = [dic[val] for val in values]    # hyper long
361        return keys
362
363    @staticmethod
364    def tovalues(keys, codec):
365        '''return a list of values from a list of keys and a list of codec values'''
366        return [codec[key] for key in keys]
367
368    @staticmethod
369    def tonumpy(valuelist, func=None, **kwargs):
370        '''return a Numpy Array from a list of values converted by func'''
371        if func is None:
372            func = identity
373        if func == 'index':
374            return np.array(list(range(len(valuelist))))
375        valList = util.funclist(valuelist, func, **kwargs)
376        if isinstance(valList[0], str):
377            try:
378                datetime.datetime.fromisoformat(valList[0])
379            except:
380                return np.array(valList)
381            return np.array(valList, dtype=np.datetime64)
382        if isinstance(valList[0], datetime.datetime):
383            return np.array(valList, dtype=np.datetime64)
384        return np.array(valList)
385
386    @staticmethod
387    def tocodec(values, keys=None):
388        '''extract a list of unique values'''
389        if not keys:
390            #return list(set(values))
391            return list(dict.fromkeys(values))
392        ind, codec = zip(*sorted(set(zip(keys, values))))
393        return list(codec)
394
395    @staticmethod
396    def transpose(idxlist):
397        '''exchange row/column in a list of list'''
398        if not isinstance(idxlist, list):
399            raise utilError('index not transposable')
400        if not idxlist:
401            return []
402        size = min([len(ix) for ix in idxlist])
403        return [[ix[ind] for ix in idxlist] for ind in range(size)]
404
405    @staticmethod
406    def tuple(idx):
407        '''transform a list of list in a list of tuple'''
408        return [val if not isinstance(val, list) else tuple(val) for val in idx]
409
410    @staticmethod
411    def tupled(idx):
412        '''transform a list of list in a tuple of tuple'''
413        return tuple([val if not isinstance(val, list) else util.tupled(val) for val in idx])

common functions for Field and Dataset class

c1 = re.compile('\\d+\\.?\\d*[,\\-_ ;:]')
c2 = re.compile('[,\\-_ ;:]\\d+\\.?\\d*')
@staticmethod
def canonorder(lenidx):
36    @staticmethod
37    def canonorder(lenidx):
38        '''return a list of crossed keys from a list of number of values'''
39        listrange = [range(lidx) for lidx in lenidx]
40        return util.transpose(util.list(list(product(*listrange))))

return a list of crossed keys from a list of number of values

@staticmethod
def couplinginfos(l1, l2):
136    @staticmethod
137    def couplinginfos(l1, l2):
138        '''return a dict with the coupling info between two list'''
139        if not l1 or not l2:
140            return {'dist': 0, 'rateder': 0, 'disttomin': 0, 'disttomax': 0,
141                    'distmin': 0, 'distmax': 0, 'diff': 0, 'typecoupl': 'null'}
142        ls = len(util.tocodec(l1))
143        lo = len(util.tocodec(l2))
144        x0 = max(ls, lo)
145        x1 = ls * lo
146        diff = abs(ls - lo)
147        if min(ls, lo) == 1:
148            if ls == 1:
149                typec = 'derived'
150            else:
151                typec = 'derive'
152            return {'dist': x0, 'rateder': 0, 'disttomin': 0, 'disttomax': 0,
153                    'distmin': x0, 'distmax': x1, 'diff': diff, 'typecoupl': typec}
154        x = len(util.tocodec([tuple((v1, v2)) for v1, v2 in zip(l1, l2)]))
155        dic = {'dist': x, 'rateder': (x - x0) / (x1 - x0),
156               'disttomin': x - x0,  'disttomax': x1 - x,
157               'distmin': x0, 'distmax': x1, 'diff': diff}
158        if dic['rateder'] == 0 and dic['diff'] == 0:
159            dic['typecoupl'] = 'coupled'
160        elif dic['rateder'] == 0 and ls < lo:
161            dic['typecoupl'] = 'derived'
162        elif dic['rateder'] == 0 and ls > lo:
163            dic['typecoupl'] = 'derive'
164        elif dic['rateder'] == 1:
165            dic['typecoupl'] = 'crossed'
166        elif ls < lo:
167            dic['typecoupl'] = 'linked'
168        else:
169            dic['typecoupl'] = 'link'
170        return dic

return a dict with the coupling info between two list

@staticmethod
def filter(func, lis, res, *args, **kwargs):
172    @staticmethod
173    def filter(func, lis, res, *args, **kwargs):
174        '''apply "func" to each value of "lis" and tests if equals "res".
175        Return the list of index with True result.'''
176        lisf = util.funclist(lis, func, *args, **kwargs)
177        return [i for i in range(len(lis)) if lisf[i] == res]

apply "func" to each value of "lis" and tests if equals "res". Return the list of index with True result.

@staticmethod
def funclist(value, func, *args, **kwargs):
179    @staticmethod
180    def funclist(value, func, *args, **kwargs):
181        '''return the function func applied to the object value with parameters args and kwargs'''
182        if func in (None, []):
183            return value
184        lis = []
185        if not (isinstance(value, list) or value.__class__.__name__ in ['Field', 'Dataset', 'Observation']):
186            listval = [value]
187        else:
188            listval = value
189        for val in listval:
190            try:
191                lis.append(val.func(*args, **kwargs))
192            except:
193                try:
194                    lis.append(func(val, *args, **kwargs))
195                except:
196                    try:
197                        lis.append(listval.func(val, *args, **kwargs))
198                    except:
199                        try:
200                            lis.append(func(listval, val, *args, **kwargs))
201                        except:
202                            raise utilError("unable to apply func")
203        if len(lis) == 1:
204            return lis[0]
205        return lis

return the function func applied to the object value with parameters args and kwargs

@staticmethod
def hash(listval):
207    @staticmethod
208    def hash(listval):
209        ''' return sum of hash values in the list'''
210        # return sum([hash(i) for i in listval])
211        return hash(tuple(listval))

return sum of hash values in the list

@staticmethod
def idxfull(setidx):
213    @staticmethod
214    def idxfull(setidx):
215        '''return additional keys for each index in the setidx list to have crossed setidx'''
216        setcodec = [set(idx.keys) for idx in setidx]
217        lenfull = util.mul([len(codec) for codec in setcodec])
218        if lenfull <= len(setidx[0]):
219            return []
220        complet = Counter(list(product(*setcodec)))
221        complet.subtract(
222            Counter(util.tuple(util.transpose([idx.keys for idx in setidx]))))
223        keysadd = util.transpose(util.list(list(complet.elements())))
224        if not keysadd:
225            return []
226        return keysadd

return additional keys for each index in the setidx list to have crossed setidx

@staticmethod
def isEqual(value, tovalue=None, **kwargs):
228    @staticmethod
229    def isEqual(value, tovalue=None, **kwargs):
230        ''' return True if value and tovalue are equal'''
231        return value.__class__.__name__ == tovalue.__class__.__name__ and \
232            value == tovalue

return True if value and tovalue are equal

@staticmethod
def isNotEqual(value, tovalue=None, **kwargs):
234    @staticmethod
235    def isNotEqual(value, tovalue=None, **kwargs):
236        ''' return True if value and tovalue are not equal'''
237        return value.__class__.__name__ != tovalue.__class__.__name__ or \
238            value != tovalue

return True if value and tovalue are not equal

@staticmethod
def isNotNull(value, nullvalue=None, **kwargs):
240    @staticmethod
241    def isNotNull(value, nullvalue=None, **kwargs):
242        '''return boolean. True if value' is not a NullValue'''
243        if value.__class__.__name__ in ES.valname:
244            return value != value.__class__(nullvalue)
245        return not value is None

return boolean. True if value' is not a NullValue

@staticmethod
def json(val, **option):
256    @staticmethod
257    def json(val, **option):
258        datecast = True
259        if 'datetime' in option:
260            datecast = option['datetime']
261        val = ESValue.uncastsimple(val, datecast)
262        if isinstance(val, (str, int, float, bool, list, dict, type(None), bytes, datetime.datetime)):
263            return val
264        if option['simpleval']:
265            return val.json(**option)
266        if val.__class__.__name__ in ES.ESclassName:  # ESValue
267            if not option['typevalue']:
268                return val.json(**option)
269            else:
270                return {ES.valname[val.__class__.__name__]: val.json(**option)}
271        if val.__class__.__name__ == 'Dataset':
272            return {ES.ili_valName: val.json(**option)}
273        if val.__class__.__name__ == 'Field':
274            return {ES.iin_valName: val.json(**option)}
275        if val.__class__.__name__ == 'Observation':
276            return {ES.obs_valName: val.to_obj(**option)}
@staticmethod
def list(tuplelists):
278    @staticmethod
279    def list(tuplelists):
280        '''transform a list of tuples in a list of lists'''
281        return list(map(list, tuplelists))

transform a list of tuples in a list of lists

@staticmethod
def listed(idx):
283    @staticmethod
284    def listed(idx):
285        '''transform a tuple of tuple in a list of list'''
286        return [val if not isinstance(val, tuple) else util.listed(val) for val in idx]

transform a tuple of tuple in a list of list

@staticmethod
def mul(values):
288    @staticmethod
289    def mul(values):
290        '''return the product of values in a list or tuple (math.prod)'''
291        mul = 1
292        for val in values:
293            mul *= val
294        return mul

return the product of values in a list or tuple (math.prod)

@staticmethod
def pparent(row, infos):
296    @staticmethod
297    def pparent(row, infos):
298        '''return field 'pparent' '''
299        if row < 0:
300            return row
301        field = infos[row]
302        # if field['pparent'] != 0:
303        if field['pparent'] != -2:
304            return field['pparent']
305        if field['cat'] == 'primary':
306            field['pparent'] = field['num']
307        elif field['cat'] == 'unique':
308            field['pparent'] = -1
309        else:
310            field['pparent'] = util.pparent(field['parent'], infos)
311        return field['pparent']

return field 'pparent'

@staticmethod
def pparent2(row, infos):
313    @staticmethod
314    def pparent2(row, infos):
315        '''return field 'pparent' '''
316        if row < 0:
317            return row
318        field = infos[row]
319        # if field['pparent'] != 0:
320        if field['pparent'] != -2:
321            return field['pparent']
322        if field['cat'] == 'primary':
323            field['pparent'] = field['num']
324        elif field['cat'] == 'unique':
325            field['pparent'] = -1
326        else:
327            field['pparent'] = util.pparent2(field['parent'], infos)
328        return field['pparent']

return field 'pparent'

@staticmethod
def reindex(oldkeys, oldcodec, newcodec):
330    @staticmethod
331    def reindex(oldkeys, oldcodec, newcodec):
332        '''new keys with new order of codec'''
333        dic = {newcodec[i]: i for i in range(len(newcodec))}
334        return [dic[oldcodec[key]] for key in oldkeys]

new keys with new order of codec

@staticmethod
def reorder(values, sort=None):
336    @staticmethod
337    def reorder(values, sort=None):
338        '''return a new values list following the order define by sort'''
339        if not sort:
340            return values
341        return [values[ind] for ind in sort]

return a new values list following the order define by sort

@staticmethod
def resetidx(values):
343    @staticmethod
344    def resetidx(values):
345        '''return codec and keys from a list of values'''
346        codec = util.tocodec(values)
347        return (codec, util.tokeys(values, codec))

return codec and keys from a list of values

@staticmethod
def str(listvalues):
349    @staticmethod
350    def str(listvalues):
351        '''return a list with values in the str format'''
352        return [str(val) for val in listvalues]

return a list with values in the str format

@staticmethod
def tokeys(values, codec=None):
354    @staticmethod
355    def tokeys(values, codec=None):
356        ''' return a list of keys from a list of values'''
357        if not codec:
358            codec = util.tocodec(values)
359        dic = {codec[i]: i for i in range(len(codec))}  # !!!!long
360        keys = [dic[val] for val in values]    # hyper long
361        return keys

return a list of keys from a list of values

@staticmethod
def tovalues(keys, codec):
363    @staticmethod
364    def tovalues(keys, codec):
365        '''return a list of values from a list of keys and a list of codec values'''
366        return [codec[key] for key in keys]

return a list of values from a list of keys and a list of codec values

@staticmethod
def tonumpy(valuelist, func=None, **kwargs):
368    @staticmethod
369    def tonumpy(valuelist, func=None, **kwargs):
370        '''return a Numpy Array from a list of values converted by func'''
371        if func is None:
372            func = identity
373        if func == 'index':
374            return np.array(list(range(len(valuelist))))
375        valList = util.funclist(valuelist, func, **kwargs)
376        if isinstance(valList[0], str):
377            try:
378                datetime.datetime.fromisoformat(valList[0])
379            except:
380                return np.array(valList)
381            return np.array(valList, dtype=np.datetime64)
382        if isinstance(valList[0], datetime.datetime):
383            return np.array(valList, dtype=np.datetime64)
384        return np.array(valList)

return a Numpy Array from a list of values converted by func

@staticmethod
def tocodec(values, keys=None):
386    @staticmethod
387    def tocodec(values, keys=None):
388        '''extract a list of unique values'''
389        if not keys:
390            #return list(set(values))
391            return list(dict.fromkeys(values))
392        ind, codec = zip(*sorted(set(zip(keys, values))))
393        return list(codec)

extract a list of unique values

@staticmethod
def transpose(idxlist):
395    @staticmethod
396    def transpose(idxlist):
397        '''exchange row/column in a list of list'''
398        if not isinstance(idxlist, list):
399            raise utilError('index not transposable')
400        if not idxlist:
401            return []
402        size = min([len(ix) for ix in idxlist])
403        return [[ix[ind] for ix in idxlist] for ind in range(size)]

exchange row/column in a list of list

@staticmethod
def tuple(idx):
405    @staticmethod
406    def tuple(idx):
407        '''transform a list of list in a list of tuple'''
408        return [val if not isinstance(val, list) else tuple(val) for val in idx]

transform a list of list in a list of tuple

@staticmethod
def tupled(idx):
410    @staticmethod
411    def tupled(idx):
412        '''transform a list of list in a tuple of tuple'''
413        return tuple([val if not isinstance(val, list) else util.tupled(val) for val in idx])

transform a list of list in a tuple of tuple

class utilError(builtins.Exception):
415class utilError(Exception):
416    ''' util Exception'''
417    # pass

util Exception

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args