SankeyExcelParser 1.0.0b0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. SankeyExcelParser/__init__.py +0 -0
  2. SankeyExcelParser/io_excel.py +1867 -0
  3. SankeyExcelParser/io_excel_constants.py +811 -0
  4. SankeyExcelParser/sankey.py +3138 -0
  5. SankeyExcelParser/sankey_utils/__init__.py +0 -0
  6. SankeyExcelParser/sankey_utils/data.py +1118 -0
  7. SankeyExcelParser/sankey_utils/excel_source.py +31 -0
  8. SankeyExcelParser/sankey_utils/flux.py +344 -0
  9. SankeyExcelParser/sankey_utils/functions.py +278 -0
  10. SankeyExcelParser/sankey_utils/node.py +340 -0
  11. SankeyExcelParser/sankey_utils/protos/__init__.py +0 -0
  12. SankeyExcelParser/sankey_utils/protos/flux.py +84 -0
  13. SankeyExcelParser/sankey_utils/protos/node.py +386 -0
  14. SankeyExcelParser/sankey_utils/protos/sankey_object.py +135 -0
  15. SankeyExcelParser/sankey_utils/protos/tag_group.py +95 -0
  16. SankeyExcelParser/sankey_utils/sankey_object.py +165 -0
  17. SankeyExcelParser/sankey_utils/table_object.py +37 -0
  18. SankeyExcelParser/sankey_utils/tag.py +95 -0
  19. SankeyExcelParser/sankey_utils/tag_group.py +206 -0
  20. SankeyExcelParser/su_trace.py +239 -0
  21. SankeyExcelParser/tests/integration/__init__.py +0 -0
  22. SankeyExcelParser/tests/integration/test_base.py +356 -0
  23. SankeyExcelParser/tests/integration/test_run_check_input.py +100 -0
  24. SankeyExcelParser/tests/integration/test_run_conversions.py +96 -0
  25. SankeyExcelParser/tests/integration/test_run_load_input.py +94 -0
  26. SankeyExcelParser/tests/unit/__init__.py +0 -0
  27. SankeyExcelParser-1.0.0b0.data/scripts/run_parse_and_write_excel.py +155 -0
  28. SankeyExcelParser-1.0.0b0.data/scripts/run_parse_excel.py +115 -0
  29. SankeyExcelParser-1.0.0b0.dist-info/METADATA +113 -0
  30. SankeyExcelParser-1.0.0b0.dist-info/RECORD +32 -0
  31. SankeyExcelParser-1.0.0b0.dist-info/WHEEL +5 -0
  32. SankeyExcelParser-1.0.0b0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1118 @@
1
+ """
2
+ Author : Vincent LE DOZE
3
+ Date : 31/05/23
4
+
5
+ This file contains descriptions for Data class
6
+
7
+ """
8
+
9
+ # Local modules -----------------------------------------------------
10
+ from SankeyExcelParser.sankey_utils.sankey_object import SankeyObject
11
+ from SankeyExcelParser.sankey_utils.protos.flux import _ProtoFlux
12
+ import SankeyExcelParser.io_excel_constants as CONST
13
+
14
+
15
+ # CLASS ----------------------------------------------------------------------------
16
+ class ProtoData(object):
17
+ """
18
+ Define Data simple object.
19
+
20
+ Parameters
21
+ ----------
22
+ :param natural_unit: Unité naturelle de reference pour la donnée.
23
+ :type natural_unit: str
24
+
25
+ :param factor: Facteur de conversion
26
+ :type factor: float
27
+
28
+ :param source: source de la donnée.
29
+ :type source: str
30
+
31
+ :param hypothesis: Descriptions des hypothèes de données
32
+ :type hypothesis: str
33
+ """
34
+
35
+ def __init__(
36
+ self,
37
+ **kwargs
38
+ ):
39
+ # Attributes
40
+ self._natural_unit = None
41
+ self._factor = None
42
+ self._source = None
43
+ self._hypothesis = None
44
+ self.update(**kwargs)
45
+
46
+ def update(
47
+ self,
48
+ **kwargs
49
+ ):
50
+ for key, value in kwargs.items():
51
+ setattr(self, key, value)
52
+
53
+ def _set_as_int(
54
+ self,
55
+ key,
56
+ value
57
+ ):
58
+ # None case
59
+ if value is None:
60
+ setattr(self, key, None)
61
+ return
62
+ # Real affectation
63
+ try:
64
+ value_as_int = int(value)
65
+ setattr(self, key, value_as_int)
66
+ except Exception:
67
+ pass
68
+
69
+ def _set_as_float(
70
+ self,
71
+ key,
72
+ value
73
+ ):
74
+ # None case
75
+ if value is None:
76
+ setattr(self, key, None)
77
+ return
78
+ # Real affectation
79
+ try:
80
+ value_as_float = float(value)
81
+ # NAN protection
82
+ if (value_as_float != value_as_float):
83
+ return
84
+ setattr(self, key, value_as_float)
85
+ except Exception:
86
+ pass
87
+
88
+ def _get_as_round_value(
89
+ self,
90
+ key,
91
+ digits
92
+ ):
93
+ try:
94
+ return round(getattr(self, key), digits)
95
+ except Exception:
96
+ return getattr(self, key)
97
+
98
+ def _set_as_str(
99
+ self,
100
+ key,
101
+ value
102
+ ):
103
+ # None case
104
+ if value is None:
105
+ setattr(self, key, None)
106
+ return
107
+ # Real affectation
108
+ try:
109
+ value_as_str = str(value)
110
+ setattr(self, key, value_as_str)
111
+ except Exception:
112
+ pass
113
+
114
+ @property
115
+ def natural_unit(self):
116
+ return self._natural_unit
117
+
118
+ @natural_unit.setter
119
+ def natural_unit(self, _):
120
+ self._set_as_str('_natural_unit', _)
121
+
122
+ @property
123
+ def factor(self):
124
+ return self._factor
125
+
126
+ @factor.setter
127
+ def factor(self, _):
128
+ self._set_as_float('_factor', _)
129
+
130
+ @property
131
+ def source(self):
132
+ return self._source
133
+
134
+ @source.setter
135
+ def source(self, _):
136
+ self._set_as_str('_source', _)
137
+
138
+ @property
139
+ def hypothesis(self):
140
+ return self._hypothesis
141
+
142
+ @hypothesis.setter
143
+ def hypothesis(self, _):
144
+ self._set_as_str('_hypothesis', _)
145
+
146
+
147
+ class DataMinMax(ProtoData):
148
+ """
149
+ Define Data constraint object.
150
+ Inherits from ProtoData.
151
+
152
+ Parameters
153
+ ----------
154
+ :param reference: Reference of the object on that the min max applies to.
155
+ :param reference: Flux | Data
156
+
157
+ :param min_val: Value that reference value must be superior to
158
+ :type min_val: float
159
+
160
+ :param min_quantity: Quantity that reference value must be superior to
161
+ :type min_quantity: float
162
+
163
+ :param max_val: Value that reference value must be inferior to
164
+ :type max_val: float
165
+
166
+ :param max_quantity: Quantity that reference value must be inferior to
167
+ :type max_quantity: float
168
+ """
169
+ def __init__(
170
+ self,
171
+ reference,
172
+ **kwargs
173
+ ):
174
+ # Attributes
175
+ self._reference = reference
176
+ self._min_val = None
177
+ self._min_quantity = None
178
+ self._max_val = None
179
+ self._max_quantity = None
180
+ # Init parent attributes
181
+ ProtoData.__init__(self)
182
+ # Update initial values
183
+ self.update(**kwargs)
184
+
185
+ @property
186
+ def min_val(self):
187
+ return self._min_val
188
+
189
+ @min_val.setter
190
+ def min_val(self, _):
191
+ self._set_as_float('_min_val', _)
192
+
193
+ @property
194
+ def min_quantity(self):
195
+ return self._min_quantity
196
+
197
+ @min_quantity.setter
198
+ def min_quantity(self, _):
199
+ self._set_as_float('_min_quantity', _)
200
+
201
+ @property
202
+ def max_val(self):
203
+ return self._max_val
204
+
205
+ @max_val.setter
206
+ def max_val(self, _):
207
+ self._set_as_float('_max_val', _)
208
+
209
+ @property
210
+ def max_quantity(self):
211
+ return self._max_quantity
212
+
213
+ @max_quantity.setter
214
+ def max_quantity(self, _):
215
+ self._set_as_float('_max_quantity', _)
216
+
217
+ def update_table(
218
+ self,
219
+ columns_datataggs_names: list,
220
+ columns_fluxtaggs_names: list,
221
+ extra_infos_names: list,
222
+ table: list
223
+ ):
224
+ """
225
+ Update a panda table with this data infos.
226
+ The columns are organized as :
227
+
228
+ 2. Origin
229
+ 3. Destination
230
+ 4. All data tags (multiple columns) : for each column we have the respective tags or None
231
+ 5. All flux tags (multiple columns) : for each column we have the respective tags or None
232
+ 6. Min value
233
+ 7. Max value
234
+ 8. Min Quantity
235
+ 9. Max quantity
236
+ 10. Natural unit
237
+ 11. Factor
238
+ 12. Sources
239
+ 13. Hypothesis
240
+ 14. All remaining extra infos (if present, multiple columns)
241
+
242
+ Parameters
243
+ ----------
244
+ :param columns_datataggs_names: List of tag groups for data tags columns
245
+ :type columns_datataggs_names: list[str] | list[TagGroup]
246
+
247
+ :param columns_fluxtaggs_names: List of tag groups for flux tags columns
248
+ :type columns_fluxtaggs_names: list[str] | list[TagGroup]
249
+
250
+ :param extra_infos_names: List of extra infos to search for extra infos columns
251
+ :type extra_infos_names: list[str]
252
+
253
+ :param table: Table (2D-list) where all infos will be added
254
+ :type table: list (modified)
255
+
256
+ """
257
+ # Only for data with value
258
+ if (self._min_val is None) and \
259
+ (self._min_quantity is None) and \
260
+ (self._max_val is None) and \
261
+ (self._max_quantity is None):
262
+ return
263
+ # Create table line with corresponding data
264
+ line = [
265
+ self._reference.orig.name,
266
+ self._reference.dest.name]
267
+ # Get tags
268
+ line += self._reference.get_tags_from_taggroups(
269
+ columns_datataggs_names, return_names_instead_of_refs=True)
270
+ line += self._reference.get_tags_from_taggroups(
271
+ columns_fluxtaggs_names, return_names_instead_of_refs=True)
272
+ # Create table line with corresponding data
273
+ line += [
274
+ self._min_val,
275
+ self._max_val,
276
+ self._min_quantity,
277
+ self._max_quantity,
278
+ self._natural_unit,
279
+ self._factor,
280
+ self._source,
281
+ self._hypothesis]
282
+ # Add extra info cols if needed
283
+ for extra_info_name in extra_infos_names:
284
+ if extra_info_name in self._reference.extra_infos.keys():
285
+ line.append(self._reference.extra_infos[extra_info_name])
286
+ else:
287
+ line.append(None)
288
+ # We can add it directly in the table
289
+ table.append(line)
290
+
291
+
292
+ class DataConstraint(ProtoData):
293
+ """
294
+ Define Data constraint object.
295
+
296
+ Parameters
297
+ ----------
298
+ :param reference: Reference of the object on that the constraint applies to.
299
+ :param reference: Flux | Data
300
+
301
+ :param eq: Value that reference value must be equal to
302
+ :type eq: float
303
+
304
+ :param ineq_inf: Value that reference value must be superior to
305
+ :type ineq_inf: float
306
+
307
+ :param ineq_sup: Value that reference value must be inferior to
308
+ :type ineq_sup: float
309
+
310
+ :param traduction: What the constraint value mean for the reference.
311
+ :type traduction: str
312
+ """
313
+ def __init__(
314
+ self,
315
+ id,
316
+ reference,
317
+ **kwargs
318
+ ):
319
+ # Attributes
320
+ self._id = -1
321
+ self._set_as_int('_id', id)
322
+ self._reference = reference
323
+ self._eq = None
324
+ self._ineq_inf = None
325
+ self._ineq_sup = None
326
+ self._traduction = None
327
+ # Init parent attributes
328
+ ProtoData.__init__(self)
329
+ # Update initial values
330
+ self.update(**kwargs)
331
+
332
+ @property
333
+ def id(self):
334
+ return self._id
335
+
336
+ @property
337
+ def reference(self):
338
+ return self._reference
339
+
340
+ @property
341
+ def eq(self):
342
+ return self._eq
343
+
344
+ @eq.setter
345
+ def eq(self, _):
346
+ self._set_as_float('_eq', _)
347
+
348
+ @property
349
+ def ineq_inf(self):
350
+ return self._ineq_inf
351
+
352
+ @ineq_inf.setter
353
+ def ineq_inf(self, _):
354
+ self._set_as_float('_ineq_inf', _)
355
+
356
+ @property
357
+ def ineq_sup(self):
358
+ return self._ineq_sup
359
+
360
+ @ineq_sup.setter
361
+ def ineq_sup(self, _):
362
+ self._set_as_float('_ineq_sup', _)
363
+
364
+ @property
365
+ def traduction(self):
366
+ return self._traduction
367
+
368
+ @traduction.setter
369
+ def traduction(self, _):
370
+ self._set_as_str('_traduction', _)
371
+
372
+ def update_table(
373
+ self,
374
+ columns_datataggs_names: list,
375
+ columns_fluxtaggs_names: list,
376
+ extra_infos_names: list,
377
+ table: list
378
+ ):
379
+ """
380
+ Update a panda table with this data infos.
381
+ The columns are organized as :
382
+
383
+ 1. Id
384
+ 2. Origin
385
+ 3. Destination
386
+ 4. All data tags (multiple columns) : for each column we have the respective tags or None
387
+ 5. All flux tags (multiple columns) : for each column we have the respective tags or None
388
+ 6. Eq
389
+ 7. Ineq inf
390
+ 8. Ineq sup
391
+ 9. traduction
392
+ 10. source
393
+ 11. hypothesis
394
+ 12. All remaining extra infos (if present, multiple columns)
395
+
396
+
397
+ Parameters
398
+ ----------
399
+ :param columns_datataggs_names: List of tag groups for data tags columns
400
+ :type columns_datataggs_names: list[str] | list[TagGroup]
401
+
402
+ :param columns_fluxtaggs_names: List of tag groups for flux tags columns
403
+ :type columns_fluxtaggs_names: list[str] | list[TagGroup]
404
+
405
+ :param extra_infos_names: List of extra infos to search for extra infos columns
406
+ :type extra_infos_names: list[str]
407
+
408
+ :param table: Table (2D-list) where all infos will be added
409
+ :type table: list[list] (modified)
410
+
411
+ """
412
+ # Only for data with value
413
+ if (self._eq is None) and \
414
+ (self._ineq_inf is None) and \
415
+ (self._ineq_sup is None):
416
+ return
417
+ # Create table line with corresponding data
418
+ line = [
419
+ self._id,
420
+ self._reference.orig.name,
421
+ self._reference.dest.name]
422
+ # Get tags
423
+ line += self._reference.get_tags_from_taggroups(
424
+ columns_datataggs_names, return_names_instead_of_refs=True)
425
+ line += self._reference.get_tags_from_taggroups(
426
+ columns_fluxtaggs_names, return_names_instead_of_refs=True)
427
+ # Create table line with corresponding data
428
+ line += [
429
+ self._eq,
430
+ self._ineq_inf,
431
+ self._ineq_sup,
432
+ self._traduction,
433
+ self._source,
434
+ self._hypothesis]
435
+ # Add extra info cols if needed
436
+ for extra_info_name in extra_infos_names:
437
+ if extra_info_name in self._reference.extra_infos.keys():
438
+ line.append(self._reference.extra_infos[extra_info_name])
439
+ else:
440
+ line.append(None)
441
+ # We can add it directly in the table
442
+ table.append(line)
443
+
444
+ def get_as_dict(self):
445
+ # Init output
446
+ output = {}
447
+ # Get values
448
+ output['reference'] = {}
449
+ if isinstance(self._reference, _ProtoFlux):
450
+ output['reference']['orig'] = self.reference.orig.name
451
+ output['reference']['dest'] = self.reference.dest.name
452
+ if isinstance(self._reference, Data):
453
+ output['reference']['orig'] = self.reference.flux.orig.name
454
+ output['reference']['dest'] = self.reference.flux.dest.name
455
+ output['tags'] = [
456
+ [_.group.name, _.name]
457
+ for _ in self.reference.tags]
458
+ output['eq'] = self.eq
459
+ output['ineq_inf'] = self.ineq_inf
460
+ output['ineq_suo'] = self.ineq_sup
461
+ return output
462
+
463
+
464
+ class Data(ProtoData, SankeyObject):
465
+ """
466
+ Define Data object.
467
+ Inherits from `ProtoData` and `SankeyObject`
468
+
469
+ Parameters
470
+ ----------
471
+ :param flux: Flux associé à la donnée.
472
+ :type flux: Flux
473
+
474
+ :param tags: list of tags associated to the data
475
+ :type tags: list
476
+
477
+ :param sigma: Ecart type (Incertitude de la donnée).
478
+ L'incertitude porte sur les données. \
479
+ Elle est soit renseignée par la source et recopiée ici, \
480
+ soit renseignée de manière arbitraire par la personne faisant \
481
+ l'AFM en fonction de la confiance dans les données présentées \
482
+ par la source, selon la méthodologie décrite dans la première \
483
+ feuille de cet Excel.
484
+ :type sigma: float
485
+
486
+ :param min: Borne inférieure de la valeur possible de la donnée. \
487
+ Valeur obligatoire pour réaliser l'AFM.
488
+ :type min: DataMinMax
489
+
490
+ :param max: Borne supérieur de la valeur possible de la donnée. \
491
+ Valeur obligatoire pour réaliser l'AFM.
492
+ :type max: DataMinMax
493
+
494
+ :param constraints: TODO
495
+ :type constraints: dict
496
+
497
+ :param alterego: If data, then alterego is the associated result and vice-versa
498
+ :type alterego: Data
499
+ """
500
+
501
+ def __init__(
502
+ self,
503
+ **kwargs
504
+ ):
505
+ # Data values attributes
506
+ self._value = None
507
+ self._quantity = None
508
+ # Flux attribute
509
+ self._flux = None
510
+ # Uncertainty attributes
511
+ self._sigma = None
512
+ self._min_max = DataMinMax(self)
513
+ # Reconcilliation attributes
514
+ self._constraints = {}
515
+ self._alterego = None
516
+ self._analysis_vector = []
517
+ # Init super constructor
518
+ ProtoData.__init__(self)
519
+ SankeyObject.__init__(self)
520
+ # Update all present attributes
521
+ self.update(**kwargs)
522
+
523
+ def update_unknown_only(
524
+ self,
525
+ **kwargs
526
+ ):
527
+ for key, value in kwargs.items():
528
+ if getattr(self, key) is None:
529
+ setattr(self, key, value)
530
+
531
+ @property
532
+ def value(self):
533
+ return self._value
534
+
535
+ @value.setter
536
+ def value(self, _):
537
+ self._set_as_float('_value', _)
538
+
539
+ def value_rounded(self, _):
540
+ return self._get_as_round_value('_value', _)
541
+
542
+ @property
543
+ def quantity(self):
544
+ return self._quantity
545
+
546
+ @quantity.setter
547
+ def quantity(self, _):
548
+ self._set_as_float('_quantity', _)
549
+
550
+ def quantity_rounded(self, _):
551
+ return self._get_as_round_value('_quantity', _)
552
+
553
+ @property
554
+ def flux(self):
555
+ return self._flux
556
+
557
+ @flux.setter
558
+ def flux(self, _):
559
+ # None case
560
+ if _ is None:
561
+ self._flux = None
562
+ return
563
+ # Real affectation
564
+ if isinstance(_, _ProtoFlux):
565
+ self._flux = _
566
+
567
+ @property
568
+ def orig(self):
569
+ return self.flux.orig
570
+
571
+ @property
572
+ def dest(self):
573
+ return self.flux.dest
574
+
575
+ @property
576
+ def natural_unit(self):
577
+ if (self._natural_unit is None) and (self._flux is not None):
578
+ return self._flux.natural_unit
579
+ return self._natural_unit
580
+
581
+ @natural_unit.setter
582
+ def natural_unit(self, _):
583
+ self._set_as_str('_natural_unit', _)
584
+
585
+ def natural_unit_rounded(self, _):
586
+ return self._get_as_round_value('_natural_unit', _)
587
+
588
+ @property
589
+ def factor(self):
590
+ if (self._factor is None) and (self._flux is not None):
591
+ return self._flux.factor
592
+ return self._factor
593
+
594
+ @factor.setter
595
+ def factor(self, _):
596
+ self._set_as_float('_factor', _)
597
+
598
+ def factor_rounded(self, _):
599
+ return self._get_as_round_value('_factor', _)
600
+
601
+ @property
602
+ def sigma(self):
603
+ if (self._sigma is not None):
604
+ return self._sigma
605
+ if (self._value is not None):
606
+ return self._value*CONST.DEFAULT_SIGMA_RELATIVE
607
+ return None
608
+
609
+ @sigma.setter
610
+ def sigma(self, _):
611
+ self._set_as_float('_sigma', _)
612
+
613
+ def sigma_rounded(self, _):
614
+ return self._get_as_round_value('_sigma', _)
615
+
616
+ @property
617
+ def sigma_relative(self):
618
+ if (self._value is not None):
619
+ if (self._sigma is not None):
620
+ if abs(self._value) > 0.0:
621
+ return self._sigma/self._value
622
+ else:
623
+ return self._sigma
624
+ return None
625
+
626
+ @sigma_relative.setter
627
+ def sigma_relative(self, _):
628
+ if _ is None:
629
+ _ = CONST.DEFAULT_SIGMA_RELATIVE
630
+ try:
631
+ self._set_as_float('_sigma', self._value*_)
632
+ except Exception:
633
+ pass
634
+
635
+ def sigma_relative_rounded(self, _):
636
+ return self._get_as_round_value('sigma_relative', _)
637
+
638
+ @property
639
+ def sigma_percent(self):
640
+ if (self._value is not None):
641
+ if (self._sigma is not None):
642
+ if abs(self._value) > 0.0:
643
+ return (self._sigma/self._value)*100.0
644
+ else:
645
+ return self._sigma*100.0
646
+ return None
647
+
648
+ @sigma_percent.setter
649
+ def sigma_percent(self, _):
650
+ if _ is None:
651
+ _ = CONST.DEFAULT_SIGMA_PERCENT
652
+ try:
653
+ self._set_as_float('_sigma', _*self._value/100.0)
654
+ except Exception:
655
+ pass
656
+
657
+ @property
658
+ def min_max(self):
659
+ return self._min_max
660
+
661
+ @property
662
+ def min_val(self):
663
+ return self._min_max.min_val
664
+
665
+ @min_val.setter
666
+ def min_val(self, _):
667
+ self._min_max.min_val = _
668
+
669
+ @property
670
+ def max_val(self):
671
+ return self._min_max.max_val
672
+
673
+ @max_val.setter
674
+ def max_val(self, _):
675
+ self._min_max.max_val = _
676
+
677
+ @property
678
+ def constraints(self):
679
+ return self._constraints
680
+
681
+ def add_constraint(self, id_constraint, **kwargs):
682
+ # Create a new piece of constraint
683
+ constraint = DataConstraint(id_constraint, self, **kwargs)
684
+ # Update constraint for given id
685
+ if id_constraint in self._constraints.keys():
686
+ self._constraints[id_constraint].append(constraint)
687
+ else:
688
+ self._constraints[id_constraint] = [constraint]
689
+ # Return constraint
690
+ return constraint
691
+
692
+ @property
693
+ def alterego(self):
694
+ return self._alterego
695
+
696
+ @alterego.setter
697
+ def alterego(self, _):
698
+ if self._alterego is None:
699
+ if isinstance(_, Data):
700
+ self._alterego = _
701
+ _.alterego = self
702
+ else:
703
+ # Reset alterego link
704
+ if _ is None:
705
+ old_alterego = self._alterego
706
+ self._alterego = None
707
+ old_alterego.altergo = None
708
+
709
+ @property
710
+ def analysis_vector(self):
711
+ # Protection with copy
712
+ return self._analysis_vector.copy()
713
+
714
+ @analysis_vector.setter
715
+ def analysis_vector(self, _):
716
+ if isinstance(_, list):
717
+ self._analysis_vector = _
718
+
719
+ def update_table(
720
+ self,
721
+ columns_datataggs_names: list,
722
+ columns_fluxtaggs_names: list,
723
+ extra_infos_names: list,
724
+ table: list,
725
+ rounding_digits: int = 2,
726
+ as_result: bool = False,
727
+ table_for_analysis: list = None
728
+ ):
729
+ """
730
+ Update a panda table with this data infos.
731
+
732
+ Parameters
733
+ ----------
734
+ :param columns_datataggs_names: List of tag groups for data tags columns
735
+ :type columns_datataggs_names: list[str] | list[TagGroup]
736
+
737
+ :param columns_fluxtaggs_names: List of tag groups for flux tags columns
738
+ :type columns_fluxtaggs_names: list[str] | list[TagGroup]
739
+
740
+ :param extra_infos_names: List of extra infos to search for extra infos columns
741
+ :type extra_infos_names: list[str]
742
+
743
+ :param table: Table (2D-list) where all infos will be added
744
+ :type table: list[list] (modified)
745
+
746
+ :param rounding_digits: Rounding precision for floating values
747
+ :type rounding digits: int (default=2)
748
+
749
+ :param as_result: Table columns are differents if data is a result.
750
+ :type as_result: bool (default=False)
751
+
752
+ :param table_for_analysis: Table (2D-list) analysis can be updated if data is result
753
+ :type table_for_analysis: list[list] (default=None)
754
+ """
755
+ if as_result is True:
756
+ # Result table
757
+ self._update_results_table(
758
+ columns_datataggs_names,
759
+ columns_fluxtaggs_names,
760
+ table,
761
+ rounding_digits=rounding_digits)
762
+ # Analysis table if needed
763
+ if table_for_analysis is not None:
764
+ self._update_analysis_table(
765
+ columns_datataggs_names,
766
+ columns_fluxtaggs_names,
767
+ table_for_analysis,
768
+ rounding_digits=rounding_digits)
769
+ else:
770
+ self._update_data_table(
771
+ columns_datataggs_names,
772
+ columns_fluxtaggs_names,
773
+ extra_infos_names,
774
+ table,
775
+ rounding_digits=rounding_digits)
776
+
777
+ def _update_data_table(
778
+ self,
779
+ columns_datataggs_names: list,
780
+ columns_fluxtaggs_names: list,
781
+ extra_infos_names: list,
782
+ table: list,
783
+ rounding_digits: int = 2
784
+ ):
785
+ """
786
+ Update a panda table with this data infos.
787
+ The columns are organized as :
788
+
789
+ 1. Origin
790
+ 2. Destination
791
+ 3. All data tags (multiple columns) : for each column we have the respective tags or None
792
+ 4. All flux tags (multiple columns) : for each column we have the respective tags or None
793
+ 5. Value
794
+ 6. Quantity
795
+ 7. Natural unit
796
+ 8. factor
797
+ 9. Sigma relative to value
798
+ 10. source
799
+ 11. hypothesis
800
+ 12. All remaining extra infos (if present, multiple columns)
801
+
802
+ Parameters
803
+ ----------
804
+ :param columns_datataggs_names: List of tag groups for data tags columns
805
+ :type columns_datataggs_names: list[str] | list[TagGroup]
806
+
807
+ :param columns_fluxtaggs_names: List of tag groups for flux tags columns
808
+ :type columns_fluxtaggs_names: list[str] | list[TagGroup]
809
+
810
+ :param extra_infos_names: List of extra infos to search for extra infos columns
811
+ :type extra_infos_names: list[str]
812
+
813
+ :param table: Table (2D-list) where all infos will be added
814
+ :type table: list[list] (modified)
815
+
816
+ """
817
+ # Write only if we have something to write
818
+ if (self.value is None) & (self.quantity is None):
819
+ return
820
+ # Create table line with corresponding data
821
+ line = [
822
+ self.orig.name,
823
+ self.dest.name]
824
+ # Get tags
825
+ line += self.get_tags_from_taggroups(
826
+ columns_datataggs_names, return_names_instead_of_refs=True)
827
+ line += self.get_tags_from_taggroups(
828
+ columns_fluxtaggs_names, return_names_instead_of_refs=True)
829
+ # Create table line with corresponding data
830
+ line += [
831
+ self.value_rounded(rounding_digits),
832
+ self.quantity_rounded(rounding_digits),
833
+ self.natural_unit,
834
+ self.factor_rounded(rounding_digits),
835
+ self.sigma_relative_rounded(rounding_digits),
836
+ self.source,
837
+ self.hypothesis]
838
+ # Add extra info cols if needed
839
+ for extra_info_name in extra_infos_names:
840
+ if extra_info_name in self.extra_infos.keys():
841
+ line.append(self.extra_infos[extra_info_name])
842
+ else:
843
+ line.append(None)
844
+ # We can add it directly in the table
845
+ table.append(line)
846
+
847
+ def _update_results_table(
848
+ self,
849
+ columns_datataggs_names: list,
850
+ columns_fluxtaggs_names: list,
851
+ table: list,
852
+ rounding_digits: int = 2
853
+ ):
854
+ """
855
+ Update a panda table with this data infos.
856
+ The columns are organized as :
857
+
858
+ 1. Origin
859
+ 2. Destination
860
+ 3. All data tags (multiple columns) : for each column we have the respective tags or None
861
+ 4. All flux tags (multiple columns) : for each column we have the respective tags or None
862
+ 5. Value
863
+ 6. Free min
864
+ 7. Free max
865
+
866
+ Parameters
867
+ ----------
868
+ :param columns_datataggs_names: List of tag groups for data tags columns
869
+ :type columns_datataggs_names: list[str] | list[TagGroup]
870
+
871
+ :param columns_fluxtaggs_names: List of tag groups for flux tags columns
872
+ :type columns_fluxtaggs_names: list[str] | list[TagGroup]
873
+
874
+ :param table: Table (2D-list) where all infos will be added
875
+ :type table: list[list] (modified)
876
+
877
+ """
878
+ # Create table line with corresponding data
879
+ line = [
880
+ self.orig.name,
881
+ self.dest.name]
882
+ # Get tags
883
+ line += self.get_tags_from_taggroups(
884
+ columns_datataggs_names, return_names_instead_of_refs=True)
885
+ line += self.get_tags_from_taggroups(
886
+ columns_fluxtaggs_names, return_names_instead_of_refs=True)
887
+ # Create table line with corresponding data
888
+ line += [
889
+ self.value_rounded(rounding_digits),
890
+ self.min_val,
891
+ self.max_val]
892
+ # We can add it directly in the table
893
+ table.append(line)
894
+
895
+ def _update_analysis_table(
896
+ self,
897
+ columns_datataggs_names: list,
898
+ columns_fluxtaggs_names: list,
899
+ table: list,
900
+ rounding_digits: int = 2
901
+ ):
902
+ """
903
+ Update a panda table with this data infos.
904
+ The columns are organized as :
905
+
906
+ 1. Origin
907
+ 2. Destination
908
+ 3. All data tags (multiple columns) : for each column we have the respective tags or None
909
+ 4. All flux tags (multiple columns) : for each column we have the respective tags or None
910
+ 5. Result Value
911
+ 6. Result Free min
912
+ 7. Result Free max
913
+ 8. Input Value
914
+ 9. Input Sigma
915
+ 10. Input relative Sigma (percent)
916
+ 11. Input min value
917
+ 12. Input max value
918
+ 13. Relative difference between Input and Result (relative to Input Sigma)
919
+ 14. Result classification = (Mesurée / Redondandes / Determinable / Indeterminable (libre))
920
+ 15+. Ai Matrix (if asked) = Liste de lignes dans lesquelles la donnée est
921
+ impliquée dans la matrice de contrainte.
922
+
923
+ Parameters
924
+ ----------
925
+ :param columns_datataggs_names: List of tag groups for data tags columns
926
+ :type columns_datataggs_names: list[str] | list[TagGroup]
927
+
928
+ :param columns_fluxtaggs_names: List of tag groups for flux tags columns
929
+ :type columns_fluxtaggs_names: list[str] | list[TagGroup]
930
+
931
+ :param table: Table (2D-list) where all infos will be added
932
+ :type table: list[list] (modified)
933
+
934
+ """
935
+ # Only if we have something to put inside
936
+ if (len(self._analysis_vector) > 0):
937
+ # Create table line with corresponding data
938
+ line = [
939
+ self.orig.name,
940
+ self.dest.name]
941
+ # Get tags
942
+ line += self.get_tags_from_taggroups(
943
+ columns_datataggs_names, return_names_instead_of_refs=True)
944
+ line += self.get_tags_from_taggroups(
945
+ columns_fluxtaggs_names, return_names_instead_of_refs=True)
946
+ # Create table line with corresponding result data
947
+ line += [
948
+ self.value_rounded(rounding_digits),
949
+ self.min_val,
950
+ self.max_val]
951
+ # Create table line with corresponding analysis data
952
+ line += [
953
+ round(_, rounding_digits)
954
+ if isinstance(_, float)
955
+ else _
956
+ for _ in self._analysis_vector]
957
+ # We can add it directly in the table
958
+ table.append(line)
959
+
960
+ def get_as_dict(self):
961
+ # Init output
962
+ output = {}
963
+ # Get values
964
+ output['value'] = self.value
965
+ output['tags'] = [
966
+ [_.group.name, _.name] for _ in self.tags]
967
+ try:
968
+ output['alterego'] = self.alterego.value
969
+ except Exception:
970
+ pass
971
+ return output
972
+
973
+ def __repr__(self):
974
+ s = '{}'.format(self._value)
975
+ for tag in self._tags:
976
+ s += ' | {}'.format(tag)
977
+ return s
978
+
979
+
980
+ def MCData(SankeyObject):
981
+ """
982
+ Define a MonteCarlo Data object.
983
+ Inherits from `SankeyObject`
984
+
985
+ Parameters
986
+ ----------
987
+ :param flux: Flux associé à la donnée.
988
+ :type flux: Flux
989
+
990
+ :param tags: list of tags associated to the data
991
+ :type tags: list
992
+
993
+ :param starting_data: Input data
994
+ :type starting_data: Data
995
+
996
+ :param result_data: Output data
997
+ :type result_data: Data
998
+ """
999
+
1000
+ def __init__(
1001
+ self,
1002
+ **kwargs
1003
+ ):
1004
+ # Flux attribute
1005
+ self._flux = None
1006
+ # Input and output data
1007
+ self._starting_data = None
1008
+ self._result_data = None
1009
+ # Probalities and histogrammes
1010
+ self._probas = {}
1011
+ self._hists = {}
1012
+ # Update all present attributes
1013
+ self.update(**kwargs)
1014
+
1015
+ def update(
1016
+ self,
1017
+ **kwargs
1018
+ ):
1019
+ for key, value in kwargs.items():
1020
+ setattr(self, key, value)
1021
+
1022
+ @property
1023
+ def flux(self):
1024
+ return self._flux
1025
+
1026
+ @flux.setter
1027
+ def flux(self, _):
1028
+ # None case
1029
+ if _ is None:
1030
+ self._flux = None
1031
+ return
1032
+ # Real affectation
1033
+ if isinstance(_, _ProtoFlux):
1034
+ self._flux = _
1035
+
1036
+ @property
1037
+ def starting_data(self):
1038
+ return self._starting_data
1039
+
1040
+ @starting_data.setter
1041
+ def starting_data(self, _):
1042
+ # None case
1043
+ if _ is None:
1044
+ self._starting_data = None
1045
+ return
1046
+ # Real affectation
1047
+ if isinstance(_, Data):
1048
+ self._starting_data = _
1049
+
1050
+ @property
1051
+ def result_data(self):
1052
+ return self._result_data
1053
+
1054
+ @result_data.setter
1055
+ def result_data(self, _):
1056
+ # None case
1057
+ if _ is None:
1058
+ self._result_data = None
1059
+ return
1060
+ # Real affectation
1061
+ if isinstance(_, Data):
1062
+ self._result_data = _
1063
+
1064
+ @property
1065
+ def min(self):
1066
+ return self._result_data.min_val
1067
+
1068
+ @min.setter
1069
+ def min(self, _):
1070
+ self._result_data.min_val = _
1071
+
1072
+ @property
1073
+ def max(self):
1074
+ return self._result_data.max_val
1075
+
1076
+ @max.setter
1077
+ def max(self, _):
1078
+ self._result_data.max_val = _
1079
+
1080
+ def add_proba(self, _key, _value):
1081
+ if (_key is not None):
1082
+ try:
1083
+ value = float(_value)
1084
+ key = str(_key)
1085
+ # NAN protection
1086
+ if (value == value):
1087
+ self._probas[key] = value
1088
+ except Exception:
1089
+ pass
1090
+
1091
+ def get_proba(self, _key):
1092
+ if (_key is not None):
1093
+ try:
1094
+ key = str(_key)
1095
+ return self._probas[key]
1096
+ except Exception:
1097
+ pass
1098
+ return None
1099
+
1100
+ def add_hist(self, _key, _value):
1101
+ if (_key is not None):
1102
+ try:
1103
+ value = float(_value)
1104
+ key = str(_key)
1105
+ # NAN protection
1106
+ if (value == value):
1107
+ self._hists[key] = value
1108
+ except Exception:
1109
+ pass
1110
+
1111
+ def get_hist(self, _key):
1112
+ if (_key is not None):
1113
+ try:
1114
+ key = str(_key)
1115
+ return self._hists[key]
1116
+ except Exception:
1117
+ pass
1118
+ return None