ntv-numpy.ntv_numpy.xdataset

@author: Philippe@loco-labs.io

The xdataset module is part of the ntv-numpy.ntv_numpy package (specification document).

It contains the classes Xdataset, XdatasetInterface, XdatasetCategory for the multidimensional dataset.

For more information, see the user guide or the github repository.

  1# -*- coding: utf-8 -*-
  2"""
  3@author: Philippe@loco-labs.io
  4
  5The `xdataset` module is part of the `ntv-numpy.ntv_numpy` package ([specification document](
  6https://loco-philippe.github.io/ES/JSON%20semantic%20format%20(JSON-NTV).htm)).
  7
  8It contains the classes `Xdataset`, `XdatasetInterface`, `XdatasetCategory` for 
  9the multidimensional dataset.
 10
 11For more information, see the
 12[user guide](https://loco-philippe.github.io/ntv-numpy/docs/user_guide.html)
 13 or the [github repository](https://github.com/loco-philippe/ntv-numpy).
 14"""
 15from abc import ABC, abstractmethod
 16import json
 17import pprint
 18from json_ntv import Ntv
 19from ntv_numpy.ndarray import Nutil
 20from ntv_numpy.xndarray import Xndarray
 21from ntv_numpy.xconnector import XarrayConnec, ScippConnec, AstropyNDDataConnec
 22from ntv_numpy.xconnector import PandasConnec
 23
 24
 25class XdatasetCategory(ABC):
 26    ''' category of Xndarray (dynamic tuple of full_name) - see Xdataset docstring'''
 27
 28    xnd: list = NotImplemented
 29    names: list = NotImplemented
 30
 31    @abstractmethod
 32    def dims(self, var, json_name=False):
 33        '''method defined in Xdataset class'''
 34
 35    @property
 36    def data_arrays(self):
 37        '''return a tuple of data_arrays Xndarray full_name'''
 38        return tuple(sorted(nda for nda in self.namedarrays
 39                            if not nda in self.dimensions + self.uniques))
 40
 41    @property
 42    def dimensions(self):
 43        '''return a tuple of dimensions Xndarray full_name'''
 44        dimable = []
 45        for var in self.variables:
 46            dimable += self.dims(var)
 47        return tuple(sorted(set(nda for nda in dimable if nda in self.namedarrays)))
 48
 49    @property
 50    def shape(self):
 51        '''return an array with the length of dimensions'''
 52        return [len(self[dim]) for dim in self.dimensions]
 53
 54    @property
 55    def coordinates(self):
 56        '''return a tuple of coordinates Xndarray full_name'''
 57        dims = set(self.dimensions)
 58        if not dims:
 59            return ()
 60        return tuple(sorted(set(xnda.name for xnda in self.xnd
 61                                if xnda.xtype == 'variable' and set(xnda.links) != dims)))
 62
 63    @property
 64    def data_vars(self):
 65        '''return a tuple of data_vars Xndarray full_name'''
 66        dims = set(self.dimensions)
 67        if not dims:
 68            return self.variables
 69        return tuple(sorted(xnda.name for xnda in self.xnd
 70                            if xnda.xtype == 'variable' and set(xnda.links) == dims))
 71
 72    @property
 73    def namedarrays(self):
 74        '''return a tuple of namedarray Xndarray full_name'''
 75        return tuple(sorted(xnda.name for xnda in self.xnd if xnda.xtype == 'namedarray'))
 76
 77    @property
 78    def variables(self):
 79        '''return a tuple of variables Xndarray full_name'''
 80        return tuple(sorted(xnda.name for xnda in self.xnd if xnda.xtype == 'variable'))
 81
 82    @property
 83    def undef_vars(self):
 84        '''return a tuple of variables Xndarray full_name with inconsistent shape'''
 85        return tuple(sorted(var for var in self.variables if self[var].shape !=
 86                            [len(self[dim]) for dim in self.dims(var)]))
 87
 88    @property
 89    def undef_links(self):
 90        '''return a tuple of variables Xndarray full_name with inconsistent links'''
 91        return tuple(sorted(link for var in self.variables for link in self[var].links
 92                            if not link in self.names))
 93
 94    @property
 95    def masks(self):
 96        '''return a tuple of additional Xndarray full_name with boolean ntv_type'''
 97        return tuple(sorted(xnda.full_name for xnda in self.xnd
 98                            if xnda.xtype == 'additional' and xnda.ntv_type == 'boolean'))
 99
100    @property
101    def data_add(self):
102        '''return a tuple of additional Xndarray full_name with not boolean ntv_type'''
103        return tuple(sorted(xnda.full_name for xnda in self.xnd
104                            if xnda.xtype == 'additional' and xnda.ntv_type != 'boolean'))
105
106    @property
107    def metadata(self):
108        '''return a tuple of metadata Xndarray full_name'''
109        return tuple(sorted(xnda.full_name for xnda in self.xnd if xnda.xtype == 'meta'))
110
111    @property
112    def uniques(self):
113        '''return a tuple of unique Xndarray full_name'''
114        return tuple(full_name for full_name in self.namedarrays if len(self[full_name]) == 1)
115
116    @property
117    def additionals(self):
118        '''return a tuple of additionals Xndarray full_name'''
119        return tuple(sorted(xnda.full_name for xnda in self.xnd if xnda.xtype == 'additional'))
120
121    def group(self, grp):
122        '''return a tuple of Xndarray full_name with the same name'''
123        if isinstance(grp, str):
124            return tuple(sorted(xnda.full_name for xnda in self.xnd
125                                if grp in (xnda.name, xnda.full_name)))
126        return tuple(sorted(nam for gr_nam in grp for nam in self.group(gr_nam)))
127
128    def add_group(self, add_name):
129        '''return a tuple of Xndarray full_name with the same add_name'''
130        return tuple(sorted(xnda.full_name for xnda in self.xnd if xnda.add_name == add_name))
131
132
133class XdatasetInterface(ABC):
134    ''' Xdataset interface - see Xdataset docstring'''
135
136    name: str = NotImplemented
137    xnd: list = NotImplemented
138
139    @staticmethod
140    def read_json(jsn, **kwargs):
141        ''' convert json data into a Xdataset.
142
143        *Parameters*
144
145        - **convert** : boolean (default True) - If True, convert json data with
146        non Numpy ntv_type into Xndarray with python type
147        '''
148        option = {'convert': True} | kwargs
149        jso = json.loads(jsn) if isinstance(jsn, str) else jsn
150        value, name = Ntv.decode_json(jso)[:2]
151
152        xnd = [Xndarray.read_json({key: val}, **option)
153               for key, val in value.items()]
154        return Xdataset(xnd, name)
155
156    def to_json(self, **kwargs):
157        ''' convert a Xdataset into json-value.
158
159        *Parameters*
160
161        - **encoded** : Boolean (default False) - json value if False else json text
162        - **header** : Boolean (default True) - including 'xdataset' type
163        - **notype** : list of Boolean (default list of None) - including data type if False
164        - **novalue** : Boolean (default False) - including value if False
165        - **noshape** : Boolean (default True) - if True, without shape if dim < 1
166        - **format** : list of string (default list of 'full') - representation
167        format of the ndarray,
168        '''
169        notype = kwargs['notype'] if ('notype' in kwargs and isinstance(kwargs['notype'], list) and
170                                      len(kwargs['notype']) == len(self)) else [False] * len(self)
171        forma = kwargs['format'] if ('format' in kwargs and isinstance(kwargs['format'], list) and
172                                     len(kwargs['format']) == len(self)) else ['full'] * len(self)
173        noshape = kwargs.get('noshape', True)
174        dic_xnd = {}
175        for xna, notyp, forma in zip(self.xnd, notype, forma):
176            dic_xnd |= xna.to_json(notype=notyp, novalue=kwargs.get('novalue', False),
177                                   noshape=noshape, format=forma, header=False)
178        return Nutil.json_ntv(self.name, 'xdataset', dic_xnd,
179                              header=kwargs.get('header', True),
180                              encoded=kwargs.get('encoded', False))
181
182    def to_xarray(self, **kwargs):
183        '''return a DataArray or a Dataset from a Xdataset
184
185        *Parameters*
186
187        - **dataset** : Boolean (default True) - if False and a single data_var, return a DataArray
188        '''
189        return XarrayConnec.xexport(self, **kwargs)
190
191    @staticmethod
192    def from_xarray(xar, **kwargs):
193        '''return a Xdataset from a DataArray or a Dataset'''
194        return XarrayConnec.ximport(xar, Xdataset, **kwargs)
195
196    def to_scipp(self, **kwargs):
197        '''return a sc.DataArray or a sc.Dataset from a Xdataset
198
199        *Parameters*
200
201        - **dataset** : Boolean (default True) - if False and a single data_var,
202        return a DataArray
203        - **datagroup** : Boolean (default True) - if True return a DataGroup with
204        metadata and data_arrays
205        - **ntv_type** : Boolean (default True) - if True add ntv-type to the name
206        '''
207        return ScippConnec.xexport(self, **kwargs)
208
209    @staticmethod
210    def from_scipp(sci, **kwargs):
211        '''return a Xdataset from a scipp object DataArray, Dataset or DataGroup'''
212        return ScippConnec.ximport(sci, Xdataset, **kwargs)
213
214    def to_nddata(self, **kwargs):
215        '''return a NDData from a Xdataset'''
216        return AstropyNDDataConnec.xexport(self, **kwargs)
217
218    @staticmethod
219    def from_nddata(ndd, **kwargs):
220        '''return a Xdataset from a NDData'''
221        return AstropyNDDataConnec.ximport(ndd, Xdataset, **kwargs)
222
223    def to_dataframe(self, **kwargs):
224        '''return a pd.DataFrame from a Xdataset'''
225        return PandasConnec.xexport(self, **kwargs)
226
227    @staticmethod
228    def from_dataframe(dfr, **kwargs):
229        '''return a Xdataset from a pd.DataFrame'''
230        return PandasConnec.ximport(dfr, Xdataset, **kwargs)
231
232
233class Xdataset(XdatasetCategory, XdatasetInterface):
234    ''' Representation of a multidimensional Dataset
235
236    *Attributes :*
237    - **name** :  String - name of the Xdataset
238    - **xnd**:   list of Xndarray
239
240    *dynamic values (@property)*
241    - `xtype`
242    - `validity`
243    - `dic_xnd`
244    - `partition`
245    - `length`
246    - `info`
247
248    *methods*
249    - `parent`
250    - `dims`
251    - `shape_dims`
252    - `to_canonical`
253    - `to_ndarray`
254    - `to_darray`
255
256    *XdatasetCategory (@property)*
257    - `names`
258    - `data_arrays`
259    - `dimensions`
260    - `coordinates`
261    - `data_vars`
262    - `namedarrays`
263    - `variables`
264    - `undef_vars`
265    - `undef_links`
266    - `masks`
267    - `data_add`
268    - `meta`
269    - `metadata`
270    - `uniques`
271    - `additionals`
272    - `group`
273    - `add_group`
274
275    *XdatasetInterface methods *
276    - `read_json` (static)
277    - `to_json`
278    - `from_xarray` (static)
279    - `to_xarray`
280    - `from_scipp` (static)
281    - `to_scipp`
282    - `from_nddata` (static)
283    - `to_nddata`
284    - `from_dataframe` (static)
285    - `to_dataframe`
286    '''
287
288    def __init__(self, xnd=None, name=None):
289        '''Xdataset constructor
290
291            *Parameters*
292
293            - **xnd** : Xdataset/Xndarray/list of Xndarray (default None),
294            - **name** : String (default None) - name of the Xdataset
295        '''
296        self.name = name
297        match xnd:
298            case list():
299                self.xnd = xnd
300            case xdat if isinstance(xdat, Xdataset):
301                self.name = xdat.name
302                self.xnd = xdat.xnd
303            case xnda if isinstance(xnda, Xndarray):
304                self.xnd = [xnda]
305            case _:
306                self.xnd = []
307
308    def __repr__(self):
309        '''return classname and number of value'''
310        return (self.__class__.__name__ + '[' + str(len(self)) + ']\n' +
311                pprint.pformat(self.to_json(novalue=True, header=False, noshape=False)))
312
313    def __str__(self):
314        '''return json string format'''
315        return json.dumps(self.to_json())
316
317    def __eq__(self, other):
318        '''equal if xnd are equal'''
319        for xnda in self.xnd:
320            if not xnda in other:
321                return False
322        for xnda in other.xnd:
323            if not xnda in self:
324                return False
325        return True
326
327    def __len__(self):
328        '''number of Xndarray'''
329        return len(self.xnd)
330
331    def __contains__(self, item):
332        ''' item of xnd'''
333        return item in self.xnd
334
335    def __getitem__(self, selec):
336        ''' return Xndarray or tuple of Xndarray with selec:
337            - string : name of a xndarray,
338            - integer : index of a xndarray,
339            - index selector : index interval
340            - tuple : names or index '''
341        if selec is None or selec == '' or selec in ([], ()):
342            return self
343        if isinstance(selec, (list, tuple)) and len(selec) == 1:
344            selec = selec[0]
345        if isinstance(selec, tuple):
346            return [self[i] for i in selec]
347        if isinstance(selec, str):
348            return self.dic_xnd[selec]
349        if isinstance(selec, list):
350            return self[selec[0]][selec[1:]]
351        return self.xnd[selec]
352
353    def __delitem__(self, ind):
354        '''remove a Xndarray (ind is index, name or tuple of names).'''
355        if isinstance(ind, int):
356            del self.xnd[ind]
357        elif isinstance(ind, str):
358            del self.xnd[self.names.index(ind)]
359        elif isinstance(ind, tuple):
360            ind_n = [self.names[i] if isinstance(i, int) else i for i in ind]
361            for i in ind_n:
362                del self[i]
363
364    def __copy__(self):
365        ''' Copy all the data '''
366        return self.__class__(self)
367
368    def parent(self, var):
369        '''return the Xndarray parent (where the full_name is equal to the name)'''
370        if var.name in self.names:
371            return self[var.name]
372        return var
373
374    def dims(self, var, json_name=False):
375        '''return the list of parent namedarrays of the links of a Xndarray
376
377        *parameters*
378
379        - **var**: string - full_name of the Xndarray
380        - **json_name**: boolean (defaut False) - if True return json_name else full_name
381        '''
382        if not var in self.names:
383            return None
384        if self[var].add_name and not self[var].links:
385            return self.dims(self[var].name, json_name)
386        if var in self.namedarrays:
387            return [self[var].json_name if json_name else var]
388        if not var in self.variables + self.additionals:
389            return None
390        list_dims = []
391        for link in self[var].links:
392            list_dims += self.dims(link, json_name) if self.dims(link,
393                                                                 json_name) else [link]
394        return list_dims
395
396    def shape_dims(self, var):
397        '''return a shape with the dimensions associated to the var full_name'''
398        return [len(self[dim]) for dim in self.dims(var)
399                ] if set(self.dims(var)) <= set(self.names) else None
400
401    @property
402    def validity(self):
403        '''return the validity state: 'inconsistent', 'undifined' or 'valid' '''
404        for xnda in self:
405            if xnda.mode in ['relative', 'inconsistent']:
406                return 'undefined'
407        if self.undef_links or self.undef_vars:
408            return 'inconsistent'
409        return 'valid'
410
411    @property
412    def xtype(self):
413        '''return the Xdataset type: 'meta', 'group', 'mono', 'multi' '''
414        if self.metadata and not (self.additionals or self.variables or
415                                  self.namedarrays):
416            return 'meta'
417        if self.validity != 'valid':
418            return 'group'
419        match len(self.data_vars):
420            case 0:
421                return 'group'
422            case 1:
423                return 'mono'
424            case _:
425                return 'multi'
426
427    @property
428    def dic_xnd(self):
429        '''return a dict of Xndarray where key is the full_name'''
430        return {xnda.full_name: xnda for xnda in self.xnd}
431
432    @property
433    def length(self):
434        '''return the max length of Xndarray'''
435        return max(len(xnda) for xnda in self.xnd)
436
437    @property
438    def names(self):
439        '''return a tuple with the Xndarray full_name'''
440        return tuple(xnda.full_name for xnda in self.xnd)
441
442    @property
443    def partition(self):
444        '''return a dict of Xndarray grouped with category'''
445        dic = {}
446        dic |= {'data_vars': list(self.data_vars)} if self.data_vars else {}
447        dic |= {'data_arrays': list(self.data_arrays)
448                } if self.data_arrays else {}
449        dic |= {'dimensions': list(self.dimensions)} if self.dimensions else {}
450        dic |= {'coordinates': list(self.coordinates)
451                } if self.coordinates else {}
452        dic |= {'additionals': list(self.additionals)
453                } if self.additionals else {}
454        dic |= {'metadata': list(self.metadata)} if self.metadata else {}
455        dic |= {'uniques': list(self.uniques)} if self.uniques else {}
456        return dic
457
458    @property
459    def info(self):
460        '''return a dict with Xdataset information '''
461        inf = {'name': self.name, 'xtype': self.xtype} | self.partition
462        inf['validity'] = self.validity
463        inf['length'] = len(self[self.data_vars[0]]) if self.data_vars else 0
464        inf['width'] = len(self)
465        data = {name: {key: val for key, val in self[name].info.items() if key != 'name'}
466                for name in self.names}
467        return {'structure': {key: val for key, val in inf.items() if val},
468                'data': {key: val for key, val in data.items() if val}}
469
470    @property
471    def tab_info(self):
472        '''return a dict with Xdataset information for tabular interface'''
473        info = self.info
474        data = info['data']
475        t_info = {}
476        if 'dimensions' in info['structure']:
477            t_info['dimensions'] = info['structure']['dimensions']
478        t_info['data'] = {name: {key: val for key, val in data[name].items()
479                                 if key in ['shape', 'xtype', 'meta', 'links']}
480                          for name in data}
481        return t_info
482
483    def to_canonical(self):
484        '''remove optional links of the included Xndarray'''
485        for name in self.names:
486            if self[name].links in ([self[name].name], [name]):
487                self[name].links = None
488        for add in self.additionals:
489            if self[add].links in [self[self[add].name].links,
490                                   [self[add].name]]:
491                self[add].links = None
492        for unic in self.uniques:
493            self[unic].links = None
494        return self
495
496    def to_ndarray(self, full_name):
497        '''convert a Xndarray from a Xdataset in a np.ndarray'''
498        if self.shape_dims(full_name) is None:
499            data = self[full_name].ndarray
500        else:
501            data = self[full_name].darray.reshape(self.shape_dims(full_name))
502        if data.dtype.name[:8] == 'datetime':
503            data = data.astype('datetime64[ns]')
504        return data
505
506    def to_darray(self, full_name):
507        '''convert a Xndarray from a Xdataset in a flattened np.ndarray'''
508        data = self[full_name].darray
509        if data.dtype.name[:8] == 'datetime':
510            data = data.astype('datetime64[ns]')
511        return data
class XdatasetCategory(abc.ABC):
 26class XdatasetCategory(ABC):
 27    ''' category of Xndarray (dynamic tuple of full_name) - see Xdataset docstring'''
 28
 29    xnd: list = NotImplemented
 30    names: list = NotImplemented
 31
 32    @abstractmethod
 33    def dims(self, var, json_name=False):
 34        '''method defined in Xdataset class'''
 35
 36    @property
 37    def data_arrays(self):
 38        '''return a tuple of data_arrays Xndarray full_name'''
 39        return tuple(sorted(nda for nda in self.namedarrays
 40                            if not nda in self.dimensions + self.uniques))
 41
 42    @property
 43    def dimensions(self):
 44        '''return a tuple of dimensions Xndarray full_name'''
 45        dimable = []
 46        for var in self.variables:
 47            dimable += self.dims(var)
 48        return tuple(sorted(set(nda for nda in dimable if nda in self.namedarrays)))
 49
 50    @property
 51    def shape(self):
 52        '''return an array with the length of dimensions'''
 53        return [len(self[dim]) for dim in self.dimensions]
 54
 55    @property
 56    def coordinates(self):
 57        '''return a tuple of coordinates Xndarray full_name'''
 58        dims = set(self.dimensions)
 59        if not dims:
 60            return ()
 61        return tuple(sorted(set(xnda.name for xnda in self.xnd
 62                                if xnda.xtype == 'variable' and set(xnda.links) != dims)))
 63
 64    @property
 65    def data_vars(self):
 66        '''return a tuple of data_vars Xndarray full_name'''
 67        dims = set(self.dimensions)
 68        if not dims:
 69            return self.variables
 70        return tuple(sorted(xnda.name for xnda in self.xnd
 71                            if xnda.xtype == 'variable' and set(xnda.links) == dims))
 72
 73    @property
 74    def namedarrays(self):
 75        '''return a tuple of namedarray Xndarray full_name'''
 76        return tuple(sorted(xnda.name for xnda in self.xnd if xnda.xtype == 'namedarray'))
 77
 78    @property
 79    def variables(self):
 80        '''return a tuple of variables Xndarray full_name'''
 81        return tuple(sorted(xnda.name for xnda in self.xnd if xnda.xtype == 'variable'))
 82
 83    @property
 84    def undef_vars(self):
 85        '''return a tuple of variables Xndarray full_name with inconsistent shape'''
 86        return tuple(sorted(var for var in self.variables if self[var].shape !=
 87                            [len(self[dim]) for dim in self.dims(var)]))
 88
 89    @property
 90    def undef_links(self):
 91        '''return a tuple of variables Xndarray full_name with inconsistent links'''
 92        return tuple(sorted(link for var in self.variables for link in self[var].links
 93                            if not link in self.names))
 94
 95    @property
 96    def masks(self):
 97        '''return a tuple of additional Xndarray full_name with boolean ntv_type'''
 98        return tuple(sorted(xnda.full_name for xnda in self.xnd
 99                            if xnda.xtype == 'additional' and xnda.ntv_type == 'boolean'))
100
101    @property
102    def data_add(self):
103        '''return a tuple of additional Xndarray full_name with not boolean ntv_type'''
104        return tuple(sorted(xnda.full_name for xnda in self.xnd
105                            if xnda.xtype == 'additional' and xnda.ntv_type != 'boolean'))
106
107    @property
108    def metadata(self):
109        '''return a tuple of metadata Xndarray full_name'''
110        return tuple(sorted(xnda.full_name for xnda in self.xnd if xnda.xtype == 'meta'))
111
112    @property
113    def uniques(self):
114        '''return a tuple of unique Xndarray full_name'''
115        return tuple(full_name for full_name in self.namedarrays if len(self[full_name]) == 1)
116
117    @property
118    def additionals(self):
119        '''return a tuple of additionals Xndarray full_name'''
120        return tuple(sorted(xnda.full_name for xnda in self.xnd if xnda.xtype == 'additional'))
121
122    def group(self, grp):
123        '''return a tuple of Xndarray full_name with the same name'''
124        if isinstance(grp, str):
125            return tuple(sorted(xnda.full_name for xnda in self.xnd
126                                if grp in (xnda.name, xnda.full_name)))
127        return tuple(sorted(nam for gr_nam in grp for nam in self.group(gr_nam)))
128
129    def add_group(self, add_name):
130        '''return a tuple of Xndarray full_name with the same add_name'''
131        return tuple(sorted(xnda.full_name for xnda in self.xnd if xnda.add_name == add_name))

category of Xndarray (dynamic tuple of full_name) - see Xdataset docstring

xnd: list = NotImplemented
names: list = NotImplemented
@abstractmethod
def dims(self, var, json_name=False):
32    @abstractmethod
33    def dims(self, var, json_name=False):
34        '''method defined in Xdataset class'''

method defined in Xdataset class

data_arrays
36    @property
37    def data_arrays(self):
38        '''return a tuple of data_arrays Xndarray full_name'''
39        return tuple(sorted(nda for nda in self.namedarrays
40                            if not nda in self.dimensions + self.uniques))

return a tuple of data_arrays Xndarray full_name

dimensions
42    @property
43    def dimensions(self):
44        '''return a tuple of dimensions Xndarray full_name'''
45        dimable = []
46        for var in self.variables:
47            dimable += self.dims(var)
48        return tuple(sorted(set(nda for nda in dimable if nda in self.namedarrays)))

return a tuple of dimensions Xndarray full_name

shape
50    @property
51    def shape(self):
52        '''return an array with the length of dimensions'''
53        return [len(self[dim]) for dim in self.dimensions]

return an array with the length of dimensions

coordinates
55    @property
56    def coordinates(self):
57        '''return a tuple of coordinates Xndarray full_name'''
58        dims = set(self.dimensions)
59        if not dims:
60            return ()
61        return tuple(sorted(set(xnda.name for xnda in self.xnd
62                                if xnda.xtype == 'variable' and set(xnda.links) != dims)))

return a tuple of coordinates Xndarray full_name

data_vars
64    @property
65    def data_vars(self):
66        '''return a tuple of data_vars Xndarray full_name'''
67        dims = set(self.dimensions)
68        if not dims:
69            return self.variables
70        return tuple(sorted(xnda.name for xnda in self.xnd
71                            if xnda.xtype == 'variable' and set(xnda.links) == dims))

return a tuple of data_vars Xndarray full_name

namedarrays
73    @property
74    def namedarrays(self):
75        '''return a tuple of namedarray Xndarray full_name'''
76        return tuple(sorted(xnda.name for xnda in self.xnd if xnda.xtype == 'namedarray'))

return a tuple of namedarray Xndarray full_name

variables
78    @property
79    def variables(self):
80        '''return a tuple of variables Xndarray full_name'''
81        return tuple(sorted(xnda.name for xnda in self.xnd if xnda.xtype == 'variable'))

return a tuple of variables Xndarray full_name

undef_vars
83    @property
84    def undef_vars(self):
85        '''return a tuple of variables Xndarray full_name with inconsistent shape'''
86        return tuple(sorted(var for var in self.variables if self[var].shape !=
87                            [len(self[dim]) for dim in self.dims(var)]))

return a tuple of variables Xndarray full_name with inconsistent shape

masks
95    @property
96    def masks(self):
97        '''return a tuple of additional Xndarray full_name with boolean ntv_type'''
98        return tuple(sorted(xnda.full_name for xnda in self.xnd
99                            if xnda.xtype == 'additional' and xnda.ntv_type == 'boolean'))

return a tuple of additional Xndarray full_name with boolean ntv_type

data_add
101    @property
102    def data_add(self):
103        '''return a tuple of additional Xndarray full_name with not boolean ntv_type'''
104        return tuple(sorted(xnda.full_name for xnda in self.xnd
105                            if xnda.xtype == 'additional' and xnda.ntv_type != 'boolean'))

return a tuple of additional Xndarray full_name with not boolean ntv_type

metadata
107    @property
108    def metadata(self):
109        '''return a tuple of metadata Xndarray full_name'''
110        return tuple(sorted(xnda.full_name for xnda in self.xnd if xnda.xtype == 'meta'))

return a tuple of metadata Xndarray full_name

uniques
112    @property
113    def uniques(self):
114        '''return a tuple of unique Xndarray full_name'''
115        return tuple(full_name for full_name in self.namedarrays if len(self[full_name]) == 1)

return a tuple of unique Xndarray full_name

additionals
117    @property
118    def additionals(self):
119        '''return a tuple of additionals Xndarray full_name'''
120        return tuple(sorted(xnda.full_name for xnda in self.xnd if xnda.xtype == 'additional'))

return a tuple of additionals Xndarray full_name

def group(self, grp):
122    def group(self, grp):
123        '''return a tuple of Xndarray full_name with the same name'''
124        if isinstance(grp, str):
125            return tuple(sorted(xnda.full_name for xnda in self.xnd
126                                if grp in (xnda.name, xnda.full_name)))
127        return tuple(sorted(nam for gr_nam in grp for nam in self.group(gr_nam)))

return a tuple of Xndarray full_name with the same name

def add_group(self, add_name):
129    def add_group(self, add_name):
130        '''return a tuple of Xndarray full_name with the same add_name'''
131        return tuple(sorted(xnda.full_name for xnda in self.xnd if xnda.add_name == add_name))

return a tuple of Xndarray full_name with the same add_name

class XdatasetInterface(abc.ABC):
134class XdatasetInterface(ABC):
135    ''' Xdataset interface - see Xdataset docstring'''
136
137    name: str = NotImplemented
138    xnd: list = NotImplemented
139
140    @staticmethod
141    def read_json(jsn, **kwargs):
142        ''' convert json data into a Xdataset.
143
144        *Parameters*
145
146        - **convert** : boolean (default True) - If True, convert json data with
147        non Numpy ntv_type into Xndarray with python type
148        '''
149        option = {'convert': True} | kwargs
150        jso = json.loads(jsn) if isinstance(jsn, str) else jsn
151        value, name = Ntv.decode_json(jso)[:2]
152
153        xnd = [Xndarray.read_json({key: val}, **option)
154               for key, val in value.items()]
155        return Xdataset(xnd, name)
156
157    def to_json(self, **kwargs):
158        ''' convert a Xdataset into json-value.
159
160        *Parameters*
161
162        - **encoded** : Boolean (default False) - json value if False else json text
163        - **header** : Boolean (default True) - including 'xdataset' type
164        - **notype** : list of Boolean (default list of None) - including data type if False
165        - **novalue** : Boolean (default False) - including value if False
166        - **noshape** : Boolean (default True) - if True, without shape if dim < 1
167        - **format** : list of string (default list of 'full') - representation
168        format of the ndarray,
169        '''
170        notype = kwargs['notype'] if ('notype' in kwargs and isinstance(kwargs['notype'], list) and
171                                      len(kwargs['notype']) == len(self)) else [False] * len(self)
172        forma = kwargs['format'] if ('format' in kwargs and isinstance(kwargs['format'], list) and
173                                     len(kwargs['format']) == len(self)) else ['full'] * len(self)
174        noshape = kwargs.get('noshape', True)
175        dic_xnd = {}
176        for xna, notyp, forma in zip(self.xnd, notype, forma):
177            dic_xnd |= xna.to_json(notype=notyp, novalue=kwargs.get('novalue', False),
178                                   noshape=noshape, format=forma, header=False)
179        return Nutil.json_ntv(self.name, 'xdataset', dic_xnd,
180                              header=kwargs.get('header', True),
181                              encoded=kwargs.get('encoded', False))
182
183    def to_xarray(self, **kwargs):
184        '''return a DataArray or a Dataset from a Xdataset
185
186        *Parameters*
187
188        - **dataset** : Boolean (default True) - if False and a single data_var, return a DataArray
189        '''
190        return XarrayConnec.xexport(self, **kwargs)
191
192    @staticmethod
193    def from_xarray(xar, **kwargs):
194        '''return a Xdataset from a DataArray or a Dataset'''
195        return XarrayConnec.ximport(xar, Xdataset, **kwargs)
196
197    def to_scipp(self, **kwargs):
198        '''return a sc.DataArray or a sc.Dataset from a Xdataset
199
200        *Parameters*
201
202        - **dataset** : Boolean (default True) - if False and a single data_var,
203        return a DataArray
204        - **datagroup** : Boolean (default True) - if True return a DataGroup with
205        metadata and data_arrays
206        - **ntv_type** : Boolean (default True) - if True add ntv-type to the name
207        '''
208        return ScippConnec.xexport(self, **kwargs)
209
210    @staticmethod
211    def from_scipp(sci, **kwargs):
212        '''return a Xdataset from a scipp object DataArray, Dataset or DataGroup'''
213        return ScippConnec.ximport(sci, Xdataset, **kwargs)
214
215    def to_nddata(self, **kwargs):
216        '''return a NDData from a Xdataset'''
217        return AstropyNDDataConnec.xexport(self, **kwargs)
218
219    @staticmethod
220    def from_nddata(ndd, **kwargs):
221        '''return a Xdataset from a NDData'''
222        return AstropyNDDataConnec.ximport(ndd, Xdataset, **kwargs)
223
224    def to_dataframe(self, **kwargs):
225        '''return a pd.DataFrame from a Xdataset'''
226        return PandasConnec.xexport(self, **kwargs)
227
228    @staticmethod
229    def from_dataframe(dfr, **kwargs):
230        '''return a Xdataset from a pd.DataFrame'''
231        return PandasConnec.ximport(dfr, Xdataset, **kwargs)

Xdataset interface - see Xdataset docstring

name: str = NotImplemented
xnd: list = NotImplemented
@staticmethod
def read_json(jsn, **kwargs):
140    @staticmethod
141    def read_json(jsn, **kwargs):
142        ''' convert json data into a Xdataset.
143
144        *Parameters*
145
146        - **convert** : boolean (default True) - If True, convert json data with
147        non Numpy ntv_type into Xndarray with python type
148        '''
149        option = {'convert': True} | kwargs
150        jso = json.loads(jsn) if isinstance(jsn, str) else jsn
151        value, name = Ntv.decode_json(jso)[:2]
152
153        xnd = [Xndarray.read_json({key: val}, **option)
154               for key, val in value.items()]
155        return Xdataset(xnd, name)

convert json data into a Xdataset.

Parameters

  • convert : boolean (default True) - If True, convert json data with non Numpy ntv_type into Xndarray with python type
def to_json(self, **kwargs):
157    def to_json(self, **kwargs):
158        ''' convert a Xdataset into json-value.
159
160        *Parameters*
161
162        - **encoded** : Boolean (default False) - json value if False else json text
163        - **header** : Boolean (default True) - including 'xdataset' type
164        - **notype** : list of Boolean (default list of None) - including data type if False
165        - **novalue** : Boolean (default False) - including value if False
166        - **noshape** : Boolean (default True) - if True, without shape if dim < 1
167        - **format** : list of string (default list of 'full') - representation
168        format of the ndarray,
169        '''
170        notype = kwargs['notype'] if ('notype' in kwargs and isinstance(kwargs['notype'], list) and
171                                      len(kwargs['notype']) == len(self)) else [False] * len(self)
172        forma = kwargs['format'] if ('format' in kwargs and isinstance(kwargs['format'], list) and
173                                     len(kwargs['format']) == len(self)) else ['full'] * len(self)
174        noshape = kwargs.get('noshape', True)
175        dic_xnd = {}
176        for xna, notyp, forma in zip(self.xnd, notype, forma):
177            dic_xnd |= xna.to_json(notype=notyp, novalue=kwargs.get('novalue', False),
178                                   noshape=noshape, format=forma, header=False)
179        return Nutil.json_ntv(self.name, 'xdataset', dic_xnd,
180                              header=kwargs.get('header', True),
181                              encoded=kwargs.get('encoded', False))

convert a Xdataset into json-value.

Parameters

  • encoded : Boolean (default False) - json value if False else json text
  • header : Boolean (default True) - including 'xdataset' type
  • notype : list of Boolean (default list of None) - including data type if False
  • novalue : Boolean (default False) - including value if False
  • noshape : Boolean (default True) - if True, without shape if dim < 1
  • format : list of string (default list of 'full') - representation format of the ndarray,
def to_xarray(self, **kwargs):
183    def to_xarray(self, **kwargs):
184        '''return a DataArray or a Dataset from a Xdataset
185
186        *Parameters*
187
188        - **dataset** : Boolean (default True) - if False and a single data_var, return a DataArray
189        '''
190        return XarrayConnec.xexport(self, **kwargs)

return a DataArray or a Dataset from a Xdataset

Parameters

  • dataset : Boolean (default True) - if False and a single data_var, return a DataArray
@staticmethod
def from_xarray(xar, **kwargs):
192    @staticmethod
193    def from_xarray(xar, **kwargs):
194        '''return a Xdataset from a DataArray or a Dataset'''
195        return XarrayConnec.ximport(xar, Xdataset, **kwargs)

return a Xdataset from a DataArray or a Dataset

def to_scipp(self, **kwargs):
197    def to_scipp(self, **kwargs):
198        '''return a sc.DataArray or a sc.Dataset from a Xdataset
199
200        *Parameters*
201
202        - **dataset** : Boolean (default True) - if False and a single data_var,
203        return a DataArray
204        - **datagroup** : Boolean (default True) - if True return a DataGroup with
205        metadata and data_arrays
206        - **ntv_type** : Boolean (default True) - if True add ntv-type to the name
207        '''
208        return ScippConnec.xexport(self, **kwargs)

return a sc.DataArray or a sc.Dataset from a Xdataset

Parameters

  • dataset : Boolean (default True) - if False and a single data_var, return a DataArray
  • datagroup : Boolean (default True) - if True return a DataGroup with metadata and data_arrays
  • ntv_type : Boolean (default True) - if True add ntv-type to the name
@staticmethod
def from_scipp(sci, **kwargs):
210    @staticmethod
211    def from_scipp(sci, **kwargs):
212        '''return a Xdataset from a scipp object DataArray, Dataset or DataGroup'''
213        return ScippConnec.ximport(sci, Xdataset, **kwargs)

return a Xdataset from a scipp object DataArray, Dataset or DataGroup

def to_nddata(self, **kwargs):
215    def to_nddata(self, **kwargs):
216        '''return a NDData from a Xdataset'''
217        return AstropyNDDataConnec.xexport(self, **kwargs)

return a NDData from a Xdataset

@staticmethod
def from_nddata(ndd, **kwargs):
219    @staticmethod
220    def from_nddata(ndd, **kwargs):
221        '''return a Xdataset from a NDData'''
222        return AstropyNDDataConnec.ximport(ndd, Xdataset, **kwargs)

return a Xdataset from a NDData

def to_dataframe(self, **kwargs):
224    def to_dataframe(self, **kwargs):
225        '''return a pd.DataFrame from a Xdataset'''
226        return PandasConnec.xexport(self, **kwargs)

return a pd.DataFrame from a Xdataset

@staticmethod
def from_dataframe(dfr, **kwargs):
228    @staticmethod
229    def from_dataframe(dfr, **kwargs):
230        '''return a Xdataset from a pd.DataFrame'''
231        return PandasConnec.ximport(dfr, Xdataset, **kwargs)

return a Xdataset from a pd.DataFrame

class Xdataset(XdatasetCategory, XdatasetInterface):
234class Xdataset(XdatasetCategory, XdatasetInterface):
235    ''' Representation of a multidimensional Dataset
236
237    *Attributes :*
238    - **name** :  String - name of the Xdataset
239    - **xnd**:   list of Xndarray
240
241    *dynamic values (@property)*
242    - `xtype`
243    - `validity`
244    - `dic_xnd`
245    - `partition`
246    - `length`
247    - `info`
248
249    *methods*
250    - `parent`
251    - `dims`
252    - `shape_dims`
253    - `to_canonical`
254    - `to_ndarray`
255    - `to_darray`
256
257    *XdatasetCategory (@property)*
258    - `names`
259    - `data_arrays`
260    - `dimensions`
261    - `coordinates`
262    - `data_vars`
263    - `namedarrays`
264    - `variables`
265    - `undef_vars`
266    - `undef_links`
267    - `masks`
268    - `data_add`
269    - `meta`
270    - `metadata`
271    - `uniques`
272    - `additionals`
273    - `group`
274    - `add_group`
275
276    *XdatasetInterface methods *
277    - `read_json` (static)
278    - `to_json`
279    - `from_xarray` (static)
280    - `to_xarray`
281    - `from_scipp` (static)
282    - `to_scipp`
283    - `from_nddata` (static)
284    - `to_nddata`
285    - `from_dataframe` (static)
286    - `to_dataframe`
287    '''
288
289    def __init__(self, xnd=None, name=None):
290        '''Xdataset constructor
291
292            *Parameters*
293
294            - **xnd** : Xdataset/Xndarray/list of Xndarray (default None),
295            - **name** : String (default None) - name of the Xdataset
296        '''
297        self.name = name
298        match xnd:
299            case list():
300                self.xnd = xnd
301            case xdat if isinstance(xdat, Xdataset):
302                self.name = xdat.name
303                self.xnd = xdat.xnd
304            case xnda if isinstance(xnda, Xndarray):
305                self.xnd = [xnda]
306            case _:
307                self.xnd = []
308
309    def __repr__(self):
310        '''return classname and number of value'''
311        return (self.__class__.__name__ + '[' + str(len(self)) + ']\n' +
312                pprint.pformat(self.to_json(novalue=True, header=False, noshape=False)))
313
314    def __str__(self):
315        '''return json string format'''
316        return json.dumps(self.to_json())
317
318    def __eq__(self, other):
319        '''equal if xnd are equal'''
320        for xnda in self.xnd:
321            if not xnda in other:
322                return False
323        for xnda in other.xnd:
324            if not xnda in self:
325                return False
326        return True
327
328    def __len__(self):
329        '''number of Xndarray'''
330        return len(self.xnd)
331
332    def __contains__(self, item):
333        ''' item of xnd'''
334        return item in self.xnd
335
336    def __getitem__(self, selec):
337        ''' return Xndarray or tuple of Xndarray with selec:
338            - string : name of a xndarray,
339            - integer : index of a xndarray,
340            - index selector : index interval
341            - tuple : names or index '''
342        if selec is None or selec == '' or selec in ([], ()):
343            return self
344        if isinstance(selec, (list, tuple)) and len(selec) == 1:
345            selec = selec[0]
346        if isinstance(selec, tuple):
347            return [self[i] for i in selec]
348        if isinstance(selec, str):
349            return self.dic_xnd[selec]
350        if isinstance(selec, list):
351            return self[selec[0]][selec[1:]]
352        return self.xnd[selec]
353
354    def __delitem__(self, ind):
355        '''remove a Xndarray (ind is index, name or tuple of names).'''
356        if isinstance(ind, int):
357            del self.xnd[ind]
358        elif isinstance(ind, str):
359            del self.xnd[self.names.index(ind)]
360        elif isinstance(ind, tuple):
361            ind_n = [self.names[i] if isinstance(i, int) else i for i in ind]
362            for i in ind_n:
363                del self[i]
364
365    def __copy__(self):
366        ''' Copy all the data '''
367        return self.__class__(self)
368
369    def parent(self, var):
370        '''return the Xndarray parent (where the full_name is equal to the name)'''
371        if var.name in self.names:
372            return self[var.name]
373        return var
374
375    def dims(self, var, json_name=False):
376        '''return the list of parent namedarrays of the links of a Xndarray
377
378        *parameters*
379
380        - **var**: string - full_name of the Xndarray
381        - **json_name**: boolean (defaut False) - if True return json_name else full_name
382        '''
383        if not var in self.names:
384            return None
385        if self[var].add_name and not self[var].links:
386            return self.dims(self[var].name, json_name)
387        if var in self.namedarrays:
388            return [self[var].json_name if json_name else var]
389        if not var in self.variables + self.additionals:
390            return None
391        list_dims = []
392        for link in self[var].links:
393            list_dims += self.dims(link, json_name) if self.dims(link,
394                                                                 json_name) else [link]
395        return list_dims
396
397    def shape_dims(self, var):
398        '''return a shape with the dimensions associated to the var full_name'''
399        return [len(self[dim]) for dim in self.dims(var)
400                ] if set(self.dims(var)) <= set(self.names) else None
401
402    @property
403    def validity(self):
404        '''return the validity state: 'inconsistent', 'undifined' or 'valid' '''
405        for xnda in self:
406            if xnda.mode in ['relative', 'inconsistent']:
407                return 'undefined'
408        if self.undef_links or self.undef_vars:
409            return 'inconsistent'
410        return 'valid'
411
412    @property
413    def xtype(self):
414        '''return the Xdataset type: 'meta', 'group', 'mono', 'multi' '''
415        if self.metadata and not (self.additionals or self.variables or
416                                  self.namedarrays):
417            return 'meta'
418        if self.validity != 'valid':
419            return 'group'
420        match len(self.data_vars):
421            case 0:
422                return 'group'
423            case 1:
424                return 'mono'
425            case _:
426                return 'multi'
427
428    @property
429    def dic_xnd(self):
430        '''return a dict of Xndarray where key is the full_name'''
431        return {xnda.full_name: xnda for xnda in self.xnd}
432
433    @property
434    def length(self):
435        '''return the max length of Xndarray'''
436        return max(len(xnda) for xnda in self.xnd)
437
438    @property
439    def names(self):
440        '''return a tuple with the Xndarray full_name'''
441        return tuple(xnda.full_name for xnda in self.xnd)
442
443    @property
444    def partition(self):
445        '''return a dict of Xndarray grouped with category'''
446        dic = {}
447        dic |= {'data_vars': list(self.data_vars)} if self.data_vars else {}
448        dic |= {'data_arrays': list(self.data_arrays)
449                } if self.data_arrays else {}
450        dic |= {'dimensions': list(self.dimensions)} if self.dimensions else {}
451        dic |= {'coordinates': list(self.coordinates)
452                } if self.coordinates else {}
453        dic |= {'additionals': list(self.additionals)
454                } if self.additionals else {}
455        dic |= {'metadata': list(self.metadata)} if self.metadata else {}
456        dic |= {'uniques': list(self.uniques)} if self.uniques else {}
457        return dic
458
459    @property
460    def info(self):
461        '''return a dict with Xdataset information '''
462        inf = {'name': self.name, 'xtype': self.xtype} | self.partition
463        inf['validity'] = self.validity
464        inf['length'] = len(self[self.data_vars[0]]) if self.data_vars else 0
465        inf['width'] = len(self)
466        data = {name: {key: val for key, val in self[name].info.items() if key != 'name'}
467                for name in self.names}
468        return {'structure': {key: val for key, val in inf.items() if val},
469                'data': {key: val for key, val in data.items() if val}}
470
471    @property
472    def tab_info(self):
473        '''return a dict with Xdataset information for tabular interface'''
474        info = self.info
475        data = info['data']
476        t_info = {}
477        if 'dimensions' in info['structure']:
478            t_info['dimensions'] = info['structure']['dimensions']
479        t_info['data'] = {name: {key: val for key, val in data[name].items()
480                                 if key in ['shape', 'xtype', 'meta', 'links']}
481                          for name in data}
482        return t_info
483
484    def to_canonical(self):
485        '''remove optional links of the included Xndarray'''
486        for name in self.names:
487            if self[name].links in ([self[name].name], [name]):
488                self[name].links = None
489        for add in self.additionals:
490            if self[add].links in [self[self[add].name].links,
491                                   [self[add].name]]:
492                self[add].links = None
493        for unic in self.uniques:
494            self[unic].links = None
495        return self
496
497    def to_ndarray(self, full_name):
498        '''convert a Xndarray from a Xdataset in a np.ndarray'''
499        if self.shape_dims(full_name) is None:
500            data = self[full_name].ndarray
501        else:
502            data = self[full_name].darray.reshape(self.shape_dims(full_name))
503        if data.dtype.name[:8] == 'datetime':
504            data = data.astype('datetime64[ns]')
505        return data
506
507    def to_darray(self, full_name):
508        '''convert a Xndarray from a Xdataset in a flattened np.ndarray'''
509        data = self[full_name].darray
510        if data.dtype.name[:8] == 'datetime':
511            data = data.astype('datetime64[ns]')
512        return data

Representation of a multidimensional Dataset

Attributes :

  • name : String - name of the Xdataset
  • xnd: list of Xndarray

dynamic values (@property)

methods

XdatasetCategory (@property)

*XdatasetInterface methods *

Xdataset(xnd=None, name=None)
289    def __init__(self, xnd=None, name=None):
290        '''Xdataset constructor
291
292            *Parameters*
293
294            - **xnd** : Xdataset/Xndarray/list of Xndarray (default None),
295            - **name** : String (default None) - name of the Xdataset
296        '''
297        self.name = name
298        match xnd:
299            case list():
300                self.xnd = xnd
301            case xdat if isinstance(xdat, Xdataset):
302                self.name = xdat.name
303                self.xnd = xdat.xnd
304            case xnda if isinstance(xnda, Xndarray):
305                self.xnd = [xnda]
306            case _:
307                self.xnd = []

Xdataset constructor

Parameters

  • xnd : Xdataset/Xndarray/list of Xndarray (default None),
  • name : String (default None) - name of the Xdataset
name = NotImplemented
def parent(self, var):
369    def parent(self, var):
370        '''return the Xndarray parent (where the full_name is equal to the name)'''
371        if var.name in self.names:
372            return self[var.name]
373        return var

return the Xndarray parent (where the full_name is equal to the name)

def dims(self, var, json_name=False):
375    def dims(self, var, json_name=False):
376        '''return the list of parent namedarrays of the links of a Xndarray
377
378        *parameters*
379
380        - **var**: string - full_name of the Xndarray
381        - **json_name**: boolean (defaut False) - if True return json_name else full_name
382        '''
383        if not var in self.names:
384            return None
385        if self[var].add_name and not self[var].links:
386            return self.dims(self[var].name, json_name)
387        if var in self.namedarrays:
388            return [self[var].json_name if json_name else var]
389        if not var in self.variables + self.additionals:
390            return None
391        list_dims = []
392        for link in self[var].links:
393            list_dims += self.dims(link, json_name) if self.dims(link,
394                                                                 json_name) else [link]
395        return list_dims

return the list of parent namedarrays of the links of a Xndarray

parameters

  • var: string - full_name of the Xndarray
  • json_name: boolean (defaut False) - if True return json_name else full_name
def shape_dims(self, var):
397    def shape_dims(self, var):
398        '''return a shape with the dimensions associated to the var full_name'''
399        return [len(self[dim]) for dim in self.dims(var)
400                ] if set(self.dims(var)) <= set(self.names) else None

return a shape with the dimensions associated to the var full_name

validity
402    @property
403    def validity(self):
404        '''return the validity state: 'inconsistent', 'undifined' or 'valid' '''
405        for xnda in self:
406            if xnda.mode in ['relative', 'inconsistent']:
407                return 'undefined'
408        if self.undef_links or self.undef_vars:
409            return 'inconsistent'
410        return 'valid'

return the validity state: 'inconsistent', 'undifined' or 'valid'

xtype
412    @property
413    def xtype(self):
414        '''return the Xdataset type: 'meta', 'group', 'mono', 'multi' '''
415        if self.metadata and not (self.additionals or self.variables or
416                                  self.namedarrays):
417            return 'meta'
418        if self.validity != 'valid':
419            return 'group'
420        match len(self.data_vars):
421            case 0:
422                return 'group'
423            case 1:
424                return 'mono'
425            case _:
426                return 'multi'

return the Xdataset type: 'meta', 'group', 'mono', 'multi'

dic_xnd
428    @property
429    def dic_xnd(self):
430        '''return a dict of Xndarray where key is the full_name'''
431        return {xnda.full_name: xnda for xnda in self.xnd}

return a dict of Xndarray where key is the full_name

length
433    @property
434    def length(self):
435        '''return the max length of Xndarray'''
436        return max(len(xnda) for xnda in self.xnd)

return the max length of Xndarray

names
438    @property
439    def names(self):
440        '''return a tuple with the Xndarray full_name'''
441        return tuple(xnda.full_name for xnda in self.xnd)

return a tuple with the Xndarray full_name

partition
443    @property
444    def partition(self):
445        '''return a dict of Xndarray grouped with category'''
446        dic = {}
447        dic |= {'data_vars': list(self.data_vars)} if self.data_vars else {}
448        dic |= {'data_arrays': list(self.data_arrays)
449                } if self.data_arrays else {}
450        dic |= {'dimensions': list(self.dimensions)} if self.dimensions else {}
451        dic |= {'coordinates': list(self.coordinates)
452                } if self.coordinates else {}
453        dic |= {'additionals': list(self.additionals)
454                } if self.additionals else {}
455        dic |= {'metadata': list(self.metadata)} if self.metadata else {}
456        dic |= {'uniques': list(self.uniques)} if self.uniques else {}
457        return dic

return a dict of Xndarray grouped with category

info
459    @property
460    def info(self):
461        '''return a dict with Xdataset information '''
462        inf = {'name': self.name, 'xtype': self.xtype} | self.partition
463        inf['validity'] = self.validity
464        inf['length'] = len(self[self.data_vars[0]]) if self.data_vars else 0
465        inf['width'] = len(self)
466        data = {name: {key: val for key, val in self[name].info.items() if key != 'name'}
467                for name in self.names}
468        return {'structure': {key: val for key, val in inf.items() if val},
469                'data': {key: val for key, val in data.items() if val}}

return a dict with Xdataset information

tab_info
471    @property
472    def tab_info(self):
473        '''return a dict with Xdataset information for tabular interface'''
474        info = self.info
475        data = info['data']
476        t_info = {}
477        if 'dimensions' in info['structure']:
478            t_info['dimensions'] = info['structure']['dimensions']
479        t_info['data'] = {name: {key: val for key, val in data[name].items()
480                                 if key in ['shape', 'xtype', 'meta', 'links']}
481                          for name in data}
482        return t_info

return a dict with Xdataset information for tabular interface

def to_canonical(self):
484    def to_canonical(self):
485        '''remove optional links of the included Xndarray'''
486        for name in self.names:
487            if self[name].links in ([self[name].name], [name]):
488                self[name].links = None
489        for add in self.additionals:
490            if self[add].links in [self[self[add].name].links,
491                                   [self[add].name]]:
492                self[add].links = None
493        for unic in self.uniques:
494            self[unic].links = None
495        return self

remove optional links of the included Xndarray

def to_ndarray(self, full_name):
497    def to_ndarray(self, full_name):
498        '''convert a Xndarray from a Xdataset in a np.ndarray'''
499        if self.shape_dims(full_name) is None:
500            data = self[full_name].ndarray
501        else:
502            data = self[full_name].darray.reshape(self.shape_dims(full_name))
503        if data.dtype.name[:8] == 'datetime':
504            data = data.astype('datetime64[ns]')
505        return data

convert a Xndarray from a Xdataset in a np.ndarray

def to_darray(self, full_name):
507    def to_darray(self, full_name):
508        '''convert a Xndarray from a Xdataset in a flattened np.ndarray'''
509        data = self[full_name].darray
510        if data.dtype.name[:8] == 'datetime':
511            data = data.astype('datetime64[ns]')
512        return data

convert a Xndarray from a Xdataset in a flattened np.ndarray