richvalues 4.2.27__tar.gz → 4.2.28__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: richvalues
3
- Version: 4.2.27
3
+ Version: 4.2.28
4
4
  Summary: Python package for working with uncertainties and upper/lower limits
5
5
  Home-page: https://github.com/andresmegias/richvalues/
6
6
  Author: Andrés Megías Toledano
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "richvalues"
7
- version = "4.2.27"
7
+ version = "4.2.28"
8
8
  description = "Python package for working with uncertainties and upper/lower limits"
9
9
  license = {file="LICENSE"}
10
10
  authors = [{name="Andrés Megías Toledano"}]
@@ -37,7 +37,7 @@ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37
37
  POSSIBILITY OF SUCH DAMAGE.
38
38
  """
39
39
 
40
- __version__ = '4.2.27'
40
+ __version__ = '4.2.28'
41
41
  __author__ = 'Andrés Megías Toledano'
42
42
 
43
43
  import copy
@@ -61,10 +61,11 @@ defaultparams = {
61
61
  'maximum number of decimals to use parentheses': 5,
62
62
  'limit for extra significant figure': 2.5,
63
63
  'use extra significant figure for exact values': True,
64
+ 'use extra significant figure for upper/lower limits': True,
64
65
  'use extra significant figure for integers': True,
65
66
  'omit ones in scientific notation in LaTeX': False,
66
67
  'always show inferior and superior uncertainties': False,
67
- 'multiplication symbol for scientific notation in LaTeX': '\\cdot',
68
+ 'multiplication symbol for scientific notation in LaTeX': r'\cdot',
68
69
  'sigmas to define upper/lower limits from read values': 3.,
69
70
  'decimal exponent to define zero': -90.,
70
71
  'decimal exponent to define infinity': 90.,
@@ -273,8 +274,13 @@ def round_sf_unc(x, dx, n=None, min_exp=None, max_dec=None, extra_sf_lim=None):
273
274
  else:
274
275
  mantissa_dy, exp_dy = dy, '0'
275
276
  m = len(mantissa_dy.split('.')[1]) if '.' in dy else 0
276
- mantissa_y = ('{:.{}f}'.format(float(mantissa_y)*10**(-d), m)
277
+ mantissa_y = ('{:.{}f}'.format(float(mantissa_y), m)
277
278
  if x != 0 else '0')
279
+ if float(mantissa_y) == 10.:
280
+ exp_dy = int(exp_dy) + 1
281
+ exp_y = exp_dy
282
+ mantissa_y = '{:.{}f}'.format(float(mantissa_y)/10, m+1)
283
+ mantissa_dy = '{:.{}f}'.format(float(mantissa_dy)/10, m+1)
278
284
  y = '{}e{}'.format(mantissa_y, exp_dy)
279
285
  else:
280
286
  f = 10**(-int(exp_y))
@@ -282,9 +288,6 @@ def round_sf_unc(x, dx, n=None, min_exp=None, max_dec=None, extra_sf_lim=None):
282
288
  np.inf, max_dec, extra_sf_lim)
283
289
  y = '{}e{}'.format(mantissa_y, exp_y)
284
290
  dy = '{}e{}'.format(mantissa_dy, exp_y)
285
- if len(dy) > len(y)+1:
286
- min_exp = np.inf
287
- y, dy = round_sf_unc(x, dx, n, min_exp, max_dec, extra_sf_lim)
288
291
  elif dx == 0:
289
292
  y = round_sf(x, n, min_exp, extra_sf_lim)
290
293
  dy = '0e0'
@@ -297,7 +300,7 @@ def round_sf_unc(x, dx, n=None, min_exp=None, max_dec=None, extra_sf_lim=None):
297
300
  y = y.replace('e0','')
298
301
  dy = dy.replace('e0','')
299
302
  else:
300
- if not np.isinf(x):
303
+ if np.isfinite(x):
301
304
  if not 'e' in y:
302
305
  y = '{:e}'.format(float(y))
303
306
  exp = int(y.split('e')[1]) if 'e' in y else 0
@@ -475,9 +478,11 @@ class RichValue():
475
478
  integer, so when creating samples it will have integer values.
476
479
  """
477
480
 
481
+ input_main = copy.copy(main)
478
482
  input_domain = copy.copy(domain)
479
483
  domain = set_default_value(domain, 'domain')
480
- possible_kwargs = ['main_value', 'center', 'uncertainty', 'is_integer',
484
+ possible_kwargs = ['nominal', 'center', 'rel_unc', 'main_value'
485
+ 'uncertainty', 'relative_uncertainty', 'is_integer',
481
486
  'is_lower_limit', 'is_upper_limit', 'is_finite_range',
482
487
  'num_sf', 'min_exp', 'max_dec', 'extra_sf_lim']
483
488
  for kwarg in kwargs:
@@ -486,8 +491,20 @@ class RichValue():
486
491
  + " '{}'".format(kwarg))
487
492
  if 'main_value' in kwargs:
488
493
  main = kwargs['main_value']
494
+ elif 'nominal' in kwargs:
495
+ main = kwargs['nominal']
496
+ elif 'center' in kwargs:
497
+ main = kwargs['center']
498
+ is_uplim, is_lolim, is_range = False, False, False
499
+ if main is None:
500
+ raise TypeError("RichValue() missing required argument 'main'"
501
+ + " (first argument)")
489
502
  if 'uncertainty' in kwargs:
490
503
  unc = kwargs['uncertainty']
504
+ elif 'relative_uncertainty' in kwargs or 'rel_unc' in kwargs:
505
+ rel_unc = (kwargs['rel_unc'] if 'rel_unc' in kwargs
506
+ else kwargs['relative_uncertainty'])
507
+ unc = np.array(rel_unc) * main
491
508
  if 'is_upper_limit' in kwargs:
492
509
  is_uplim = kwargs['is_upper_limit']
493
510
  if 'is_lower_limit' in kwargs:
@@ -498,12 +515,6 @@ class RichValue():
498
515
  is_int = kwargs['is_integer']
499
516
  if 'show_both_uncertainties' in kwargs:
500
517
  show_both_uncs = kwargs['show_both_uncertainties']
501
- if 'center' in kwargs:
502
- main = kwargs['center']
503
- is_uplim, is_lolim, is_range = False, False, False
504
- if main is None:
505
- raise TypeError("RichValue() missing required argument 'main'"
506
- + " (first argument)")
507
518
 
508
519
  num_sf = (kwargs['num_sf'] if 'num_sf' in kwargs
509
520
  else defaultparams['number of significant figures'])
@@ -610,7 +621,7 @@ class RichValue():
610
621
  main = np.nan
611
622
  unc = [np.nan]*2
612
623
 
613
- if type(main) in [list, tuple]:
624
+ if type(main) is list:
614
625
  is_lolim, is_uplim, is_range = False, False, False
615
626
  if main[0] > main[1]:
616
627
  raise ValueError('Upper edge of interval must be greater or'
@@ -649,7 +660,8 @@ class RichValue():
649
660
  else:
650
661
  main = np.nan
651
662
  unc = [np.nan]*2
652
-
663
+
664
+ main = float(main)
653
665
  if np.isnan(main):
654
666
  is_uplim, is_lolim, is_range = False, False, False
655
667
  if is_lolim or is_uplim:
@@ -727,8 +739,11 @@ class RichValue():
727
739
  else:
728
740
  main = (x1 + x2) / 2
729
741
  unc = [(x2 - x1) / 2] * 2
730
-
731
- main = float(main)
742
+ if is_range and not np.array_equal(input_main, [main-unc[0], main+unc[1]]):
743
+ main = input_main
744
+ if is_int:
745
+ x1, x2 = main
746
+ main = [int(x1), int(x2)]
732
747
 
733
748
  global variable_count
734
749
  if domain[0] != domain[1]:
@@ -760,11 +775,14 @@ class RichValue():
760
775
  variable_dict[expression] = self
761
776
 
762
777
  def __setattr__(self, name, value):
763
- descriptors = ['main', 'unc', 'is_uplim', 'is_lolim', 'is_range',
764
- 'is_int', 'domain']
778
+ descriptors = ['main', 'unc', 'is_uplim', 'is_lolim',
779
+ 'is_range', 'is_int', 'domain']
765
780
  internals = ['pdf_info', 'variables', 'expression']
766
781
  display_options = ['num_sf', 'min_exp', 'max_dec', 'extra_sf_lim',
767
782
  'show_both_uncs']
783
+ if name == 'rel_unc':
784
+ self.unc = value * self.main
785
+ return
768
786
  if name in internals:
769
787
  raise AttributeError(f'Cannot modify attribute {name}.')
770
788
  return
@@ -778,9 +796,16 @@ class RichValue():
778
796
  value = int(value) if self.is_int else float(value)
779
797
  else:
780
798
  super().__setattr__('unc', [np.nan]*2)
799
+ elif isinstance(value, (list, tuple, np.ndarray)):
800
+ main = (value[0] + value[1]) / 2
801
+ unc = [main - value[0], value[1] - main]
802
+ super().__setattr__('unc', unc)
803
+ super().__setattr__('is_range', True)
804
+ value = main
781
805
  else:
782
806
  raise TypeError('Wrong type for attribute main.')
783
- elif name == 'unc' and isinstance(value, (int, float, np.integer, np.floating)):
807
+ elif name == 'unc' and isinstance(value,
808
+ (int, float, np.integer, np.floating)):
784
809
  if value < 0:
785
810
  raise ValueError('Uncertainty cannot be negative.')
786
811
  value = float(abs(value))
@@ -788,28 +813,37 @@ class RichValue():
788
813
  elif name in ('unc', 'domain'):
789
814
  if isinstance(value, (tuple, list)):
790
815
  if len(value) != 2:
791
- raise ValueError(f'Wrong number of elements for attribute {name}.')
816
+ raise ValueError(f'Wrong number of elements for'
817
+ f' attribute {name}.')
792
818
  else:
793
- if not (isinstance(value[0], (int, float, np.integer, np.floating))
794
- and isinstance(value[1], (int, float, np.integer, np.floating))):
795
- raise TypeError(f'Wrong type for elements of attribute {name}.')
819
+ if not (isinstance(value[0],
820
+ (int, float, np.integer, np.floating))
821
+ and isinstance(value[1],
822
+ (int, float, np.integer, np.floating))):
823
+ raise TypeError(f'Wrong type for elements of'
824
+ f' attribute {name}.')
796
825
  value = [float(value[0]), float(value[1])]
797
826
  elif isinstance(value, np.ndarray):
798
827
  if value.size > 2:
799
- raise ValueError(f'Too many elements for attribute {name}.')
828
+ raise ValueError(f'Too many elements for'
829
+ f' attribute {name}.')
800
830
  else:
801
831
  value = value.flatten().tolist()
802
- if not (isinstance(value[0], (int, float, np.integer, np.floating))
803
- and isinstance(value[1], (int, float, np.integer, np.floating))):
804
- raise TypeError('Wrong type for elements of attribute {name}.')
832
+ if not (isinstance(value[0],
833
+ (int, float, np.integer, np.floating))
834
+ and isinstance(value[1],
835
+ (int, float, np.integer, np.floating))):
836
+ raise TypeError('Wrong type for elements of'
837
+ f' attribute {name}.')
805
838
  else:
806
839
  raise TypeError(f'Wrong type for attribute {name}.')
807
840
  if name == 'unc':
808
841
  if value[1] < 0:
809
- raise ValueError('Superior uncertainty cannot be negaive.')
842
+ raise ValueError('Superior uncertainty cannot be'
843
+ ' negaive.')
810
844
  elif value[0] == 0. != value[1] or value[1] == 0. != value[0]:
811
- raise ValueError('Superior and inferior uncertainties must be'
812
- ' either both zero or both non-zero.')
845
+ raise ValueError('Superior and inferior uncertainties'
846
+ ' must be either both zero or both non-zero.')
813
847
  value[0] = abs(value[0])
814
848
  if any(np.isinf(value)) or any(np.isnan(value)):
815
849
  value = [np.nan]*2
@@ -886,6 +920,22 @@ class RichValue():
886
920
  else:
887
921
  raise AttributeError('Wrong attribute for RichValue class.')
888
922
 
923
+ def __getattribute__(self, name):
924
+ if name in ('main', 'unc'):
925
+ self_main = self.__dict__['main']
926
+ if type(self_main) is list:
927
+ main = (self_main[0] + self_main[1]) / 2
928
+ if name == 'main':
929
+ value = main
930
+ else:
931
+ unc = [main - self_main[0], self_main[1] - main]
932
+ value = unc
933
+ else:
934
+ value = super().__getattribute__(name)
935
+ else:
936
+ value = super().__getattribute__(name)
937
+ return value
938
+
889
939
  @property
890
940
  def is_lim(self):
891
941
  """Upper/lower limit."""
@@ -1063,7 +1113,8 @@ class RichValue():
1063
1113
  elif self.is_lolim:
1064
1114
  interv = [self.main, self.domain[1]]
1065
1115
  elif self.is_range:
1066
- interv = [self.main - self.unc[0], self.main + self.unc[1]]
1116
+ interv = (self.__dict__['main'] if type(self.__dict__['main']) is list
1117
+ else [self.main - self.unc[0], self.main + self.unc[1]])
1067
1118
  else:
1068
1119
  interv = [np.nan, np.nan]
1069
1120
  if self.is_int:
@@ -1208,6 +1259,8 @@ class RichValue():
1208
1259
  show_asterisk = defaultparams['show asterisk for rich values with custom PDF']
1209
1260
  use_extra_sf_in_exacts = \
1210
1261
  defaultparams['use extra significant figure for exact values']
1262
+ use_extra_sf_in_lims = \
1263
+ defaultparams['use extra significant figure for upper/lower limits']
1211
1264
  use_extra_sf_in_ints = \
1212
1265
  defaultparams['use extra significant figure for integers']
1213
1266
  unc = self.unc
@@ -1238,6 +1291,13 @@ class RichValue():
1238
1291
  if '.' not in y_:
1239
1292
  y, (dy1, dy2) = y_, (dy1_, dy2_)
1240
1293
  num_sf += 1
1294
+ if self.is_int:
1295
+ if '.' in y:
1296
+ y = str(round(float(y)))
1297
+ if '.' in dy1:
1298
+ dy1 = str(round(float(dy1)))
1299
+ if '.' in dy2:
1300
+ dy2 = str(round(float(dy2)))
1241
1301
  if 'e' in y:
1242
1302
  y, a = y.split('e')
1243
1303
  a = int(a)
@@ -1265,6 +1325,8 @@ class RichValue():
1265
1325
  if not use_exp:
1266
1326
  text = text.replace(' e0','')
1267
1327
  else:
1328
+ if use_extra_sf_in_lims:
1329
+ num_sf += 1
1268
1330
  y = round_sf(x, num_sf, min_exp, extra_sf_lim)
1269
1331
  if use_extra_sf_in_ints and '.' not in y and y.endswith('0'):
1270
1332
  y_ = round_sf(x, num_sf+1, min_exp, extra_sf_lim)
@@ -1304,7 +1366,7 @@ class RichValue():
1304
1366
  elif not self.is_range and np.isnan(self.main):
1305
1367
  text = 'nan'
1306
1368
  else:
1307
- x1, x2 = self.main - unc[0], self.main + unc[1]
1369
+ x1, x2 = self.interval()
1308
1370
  if self.is_int:
1309
1371
  x1 = np.ceil(x1)
1310
1372
  x2 = np.floor(x2)
@@ -1317,19 +1379,50 @@ class RichValue():
1317
1379
  x.__dict__['extra_sf_lim'] = extra_sf_lim
1318
1380
  x.__dict__['range_bound'] = True
1319
1381
  while str(x1) == str(x2):
1320
- x1.num_sf = x1.num_sf + 1
1321
- x2.num_sf = x2.num_sf + 1
1382
+ x1.num_sf += 1
1383
+ x2.num_sf += 1
1322
1384
  num_sf += 1
1385
+ if '.' in str(x1) and '.' not in str(x2):
1386
+ while '.' not in str(x2):
1387
+ x2.num_sf += 1
1323
1388
  text = '{} -- {}'.format(x1, x2)
1389
+ if ' e' in text:
1390
+ exp1 = int(str(x1).split(' e')[1]) if 'e' in str(x1) else 0
1391
+ exp2 = int(str(x2).split(' e')[1]) if 'e' in str(x2) else 0
1392
+ exp = round((exp1 + exp2) / 2)
1393
+ if ((abs(exp2-exp) < min_exp//2) and (abs(exp-exp1) < min_exp//2)
1394
+ and abs(exp) >= min_exp):
1395
+ x1.main /= 10**exp
1396
+ x2.main /= 10**exp
1397
+ x1.min_exp = np.inf
1398
+ x2.min_exp = np.inf
1399
+ if '.' in str(x1) and '.' not in str(x2):
1400
+ while '.' not in str(x2):
1401
+ x2.num_sf += 1
1402
+ text = '({} -- {}) e{}'.format(x1, x2, exp)
1324
1403
  if show_domain and self.domain[0] != self.domain[1] and not range_bound:
1325
1404
  text += ' [{}, {}]'.format(self.domain[0], self.domain[1])
1326
1405
  return text
1327
1406
 
1328
1407
  def __repr__(self):
1408
+ text = self._format_as_rich_value()
1329
1409
  if self.is_exact:
1330
- return self.center.__format__('')
1331
- else:
1332
- return self._format_as_rich_value()
1410
+ text_ = text.split(' [')[0] if '[' in text else text
1411
+ text_ = text_.replace(' e', 'e')
1412
+ if '.' in text_ and float(text_) != self.main:
1413
+ if ' e' in text:
1414
+ mantissa, exp = text.split(' ')
1415
+ text = f'{mantissa}... {exp}'
1416
+ else:
1417
+ text += '...'
1418
+ else:
1419
+ if float(text_) != self.main:
1420
+ text = str(int(self.main))
1421
+ elif self.is_lim:
1422
+ text = text.replace('< ', '<').replace('> ', '>')
1423
+ elif self.is_range and not text.startswith('('):
1424
+ text = text.replace(' e', 'e')
1425
+ return text
1333
1426
 
1334
1427
  def __str__(self):
1335
1428
  return self._format_as_rich_value()
@@ -1351,16 +1444,25 @@ class RichValue():
1351
1444
  char = '<' if self.is_uplim else '>'
1352
1445
  text = char + self.main.__format__(format_spec)
1353
1446
  elif self.is_range:
1354
- x1 = self.main - self.unc[0]
1355
- x2 = self.main + self.unc[1]
1447
+ x1, x2 = self.interval()
1356
1448
  text = x1.__format__(format_spec) + ' -- ' + x2.__format__(format_spec)
1357
1449
  else:
1358
1450
  text = str(self)
1359
1451
  return text
1452
+
1453
+ def __round__(self, n=0):
1454
+ rval = copy.copy(self)
1455
+ rval.min_exp = 0
1456
+ exp = int(repr(rval).split(' e')[1])
1457
+ n += exp
1458
+ rval.num_sf = n
1459
+ rval.min_exp = np.inf
1460
+ text = repr(rval)
1461
+ return text
1360
1462
 
1361
1463
  def latex(self, show_dollars=True, mult_symbol=None,
1362
- use_extra_sf_in_exacts=None, use_extra_sf_in_ints=None,
1363
- omit_ones_in_sci_notation=None):
1464
+ use_extra_sf_in_exacts=None, use_extra_sf_in_lims=None,
1465
+ use_extra_sf_in_ints=None, omit_ones_in_sci_notation=None):
1364
1466
  """Display rich value in LaTeX format."""
1365
1467
  mult_symbol = set_default_value(mult_symbol,
1366
1468
  'multiplication symbol for scientific notation in LaTeX')
@@ -1368,6 +1470,8 @@ class RichValue():
1368
1470
  'use extra significant figure for exact values')
1369
1471
  use_extra_sf_in_ints = set_default_value(use_extra_sf_in_ints,
1370
1472
  'use extra significant figure for integers')
1473
+ use_extra_sf_in_lims = set_default_value(use_extra_sf_in_lims,
1474
+ 'use extra significant figure for upper/lower limits')
1371
1475
  omit_ones_in_sci_notation = set_default_value(omit_ones_in_sci_notation,
1372
1476
  'omit ones in scientific notation in LaTeX')
1373
1477
  if not np.isfinite(self.main):
@@ -1430,6 +1534,8 @@ class RichValue():
1430
1534
  extra_sf_lim)
1431
1535
  text = y + '_{-'+dy[0]+'}^{+'+dy[1]+'}'
1432
1536
  else:
1537
+ if use_extra_sf_in_lims:
1538
+ num_sf += 1
1433
1539
  y = round_sf(x, num_sf, min_exp, extra_sf_lim)
1434
1540
  if use_extra_sf_in_ints and '.' not in y and y.endswith('0'):
1435
1541
  y_ = round_sf(x, num_sf+1, min_exp, extra_sf_lim)
@@ -1496,9 +1602,11 @@ class RichValue():
1496
1602
  text = (y + dy1 + dy2 + ' '
1497
1603
  + mult_symbol + ' 10^{'+a+'}')
1498
1604
  else:
1499
- text = (y + '_{-'+dy1+'}^{+'+dy2+'} '
1500
- + mult_symbol + ' 10^{'+a+'}')
1605
+ text = (r'\left(' + y + r'\,_{-'+dy1+'}^{+'+dy2+'}'
1606
+ + r' \right)' + mult_symbol + ' 10^{'+a+'}')
1501
1607
  else:
1608
+ if use_extra_sf_in_lims:
1609
+ num_sf += 1
1502
1610
  if self.is_lolim:
1503
1611
  symbol = '>'
1504
1612
  y = int(np.floor(x)) if self.is_int and a > num_sf else x
@@ -1530,7 +1638,7 @@ class RichValue():
1530
1638
  if '.' not in text:
1531
1639
  text = text.replace('1 {} '.format(mult_symbol), '')
1532
1640
  else:
1533
- x1, x2 = self.main - unc[0], self.main + unc[1]
1641
+ x1, x2 = self.interval()
1534
1642
  if self.is_int:
1535
1643
  x1 = np.ceil(x1)
1536
1644
  x2 = np.floor(x2)
@@ -1543,11 +1651,32 @@ class RichValue():
1543
1651
  x.__dict__['extra_sf_lim'] = extra_sf_lim
1544
1652
  x.__dict__['range_bound'] = True
1545
1653
  while str(x1) == str(x2):
1546
- x1.num_sf = x1.num_sf + 1
1547
- x2.num_sf = x2.num_sf + 1
1654
+ x1.num_sf += 1
1655
+ x2.num_sf += 1
1548
1656
  num_sf += 1
1549
- text = '{} -- {}'.format(x1.latex(show_dollars=False),
1550
- x2.latex(show_dollars=False))
1657
+ if '.' in str(x1) and '.' not in str(x2):
1658
+ while '.' not in str(x2):
1659
+ x2.num_sf += 1
1660
+ text = r'{} \,{}\, {}'.format(x1.latex(show_dollars=False),
1661
+ r'\text{ --- }', x2.latex(show_dollars=False))
1662
+ if ' e' in str(x):
1663
+ exp1 = int(str(x1).split(' e')[1]) if 'e' in str(x1) else 0
1664
+ exp2 = int(str(x2).split(' e')[1]) if 'e' in str(x2) else 0
1665
+ exp = round((exp1 + exp2) / 2)
1666
+ if ((abs(exp2-exp) <= min_exp//2) and (abs(exp-exp1) <= min_exp//2)
1667
+ and abs(exp) >= min_exp):
1668
+ x1.main /= 10**exp
1669
+ x2.main /= 10**exp
1670
+ x1.min_exp = np.inf
1671
+ x2.min_exp = np.inf
1672
+ if '.' in str(x1) and '.' not in str(x2):
1673
+ while '.' not in str(x2):
1674
+ x2.num_sf += 1
1675
+ text = r'({} \,{}\, {})'.format(x1.latex(show_dollars=False),
1676
+ r'\text{---}', x2.latex(show_dollars))
1677
+ text += ' ' + mult_symbol + ' 10^{'+str(exp)+'}'
1678
+ elif ' e' in str(x1):
1679
+ text = text.replace(r' \,\text', r' \text')
1551
1680
  else:
1552
1681
  text = (str(self.main).replace('NaN','nan').replace('nan','...')
1553
1682
  .replace('inf','\\infty'))
@@ -1859,6 +1988,10 @@ class RichValue():
1859
1988
  domain = [-np.inf, np.inf]
1860
1989
  rvalue = RichValue(x, dx, self.is_uplim, self.is_lolim,
1861
1990
  domain=domain, is_int=is_int)
1991
+ min_exp = self.min_exp
1992
+ num_sf = self.num_sf
1993
+ max_dec = self.max_dec
1994
+ extra_sf_lim = self.extra_sf_lim
1862
1995
  else:
1863
1996
  other_ = other if type(other) is RichValue else RichValue(other)
1864
1997
  domain = None
@@ -1870,10 +2003,14 @@ class RichValue():
1870
2003
  [self, other_], domain=domain)
1871
2004
  else:
1872
2005
  rvalue = RichValue(0., domain=domain, is_int=self.is_int)
1873
- rvalue.num_sf = self.num_sf
1874
- rvalue.min_exp = self.min_exp
1875
- rvalue.max_dec = self.max_dec
1876
- rvalue.extra_sf_lim = self.extra_sf_lim
2006
+ num_sf = int(np.mean([self.num_sf, other.num_sf]))
2007
+ min_exp = round(np.mean([self.min_exp, other.min_exp]))
2008
+ max_dec = round(np.mean([self.max_dec, other.max_dec]))
2009
+ extra_sf_lim = max([self.extra_sf_lim, other.extra_sf_lim])
2010
+ rvalue.num_sf = num_sf
2011
+ rvalue.min_exp = min_exp
2012
+ rvalue.max_dec = max_dec
2013
+ rvalue.extra_sf_lim = extra_sf_lim
1877
2014
  else:
1878
2015
  vars_str = ','.join(variables)
1879
2016
  function = eval('lambda {}: {}'.format(vars_str, expression))
@@ -1983,22 +2120,7 @@ class RichValue():
1983
2120
  if self.is_range:
1984
2121
  sample_ = np.random.uniform(x1, x2, N)
1985
2122
  elif np.isfinite(xrange):
1986
- if self.is_uplim:
1987
- # pdf = lambda x: truncated_normal_pdf(x,
1988
- # self.domain[0],
1989
- # (self.main - self.domain[0])/3,
1990
- # [self.domain[0], self.main])
1991
- # sample_ = sample_from_pdf(pdf, N,
1992
- # [self.domain[0], self.main])
1993
- sample_ = np.random.uniform(x1, x2, N)
1994
- else:
1995
- # pdf = lambda x: truncated_normal_pdf(x,
1996
- # self.domain[1],
1997
- # (self.domain[1] - self.main)/3,
1998
- # [self.main, self.domain[1]])
1999
- # sample_ = sample_from_pdf(pdf, N,
2000
- # [self.main, self.domain[1]])
2001
- sample_ = np.random.uniform(x1, x2, N)
2123
+ sample_ = np.random.uniform(x1, x2, N)
2002
2124
  if N >= 20:
2003
2125
  diffs = sample_ - x1
2004
2126
  inds = np.argsort(diffs)
@@ -2038,6 +2160,11 @@ class RichValue():
2038
2160
  @main_value.setter
2039
2161
  def main_value(self, x): self.main = x
2040
2162
 
2163
+ @property
2164
+ def nominal(self): return self.nominal
2165
+ @nominal.setter
2166
+ def nominal(self, x): self.nominal = x
2167
+
2041
2168
  @property
2042
2169
  def uncertainty(self): return self.unc
2043
2170
  @uncertainty.setter
@@ -2114,27 +2241,49 @@ class RichArray(np.ndarray):
2114
2241
  corresponds to an integer variable. The default is False.
2115
2242
  """
2116
2243
 
2117
- acronyms = ['main_values', 'uncertainties', 'are_integers',
2118
- 'are_lower_limits', 'are_upper_limits', 'are_finite_ranges']
2244
+ acronyms = ['nominals', 'centers', 'rel_uncs', 'main_values',
2245
+ 'uncertainties', 'relative_uncertainties', 'are_integers',
2246
+ 'are_lower_limits', 'are_upper_limits', 'are_finite_ranges']
2119
2247
  for kwarg in kwargs:
2120
2248
  if kwarg not in acronyms:
2121
2249
  raise TypeError("RichArray() got an unexpected keyword argument"
2122
2250
  + " '{}'".format(kwarg))
2123
2251
  if 'main_values' in kwargs:
2124
2252
  mains = kwargs['main_values']
2253
+ elif 'nominals' in kwargs:
2254
+ mains = kwargs['nominals']
2255
+ elif 'centers' in kwargs:
2256
+ mains = kwargs['centers']
2257
+ are_uplims, are_lolims, are_ranges = False, False, False
2125
2258
  if mains is None:
2126
- raise TypeError("RichArray() missing required argument 'mains'"
2127
- + " (pos 1)")
2259
+ raise TypeError("RichValue() missing required argument 'mains'"
2260
+ + " (first argument)")
2128
2261
  if 'uncertainties' in kwargs:
2129
2262
  uncs = kwargs['uncertainties']
2130
- if 'are_lower_limits' in kwargs:
2131
- are_lolims = kwargs['are_lower_limits']
2263
+ elif 'relative_uncertainties' in kwargs or 'rel_uncs' in kwargs:
2264
+ rel_uncs = (kwargs['rel_uncs'] if 'rel_uncs' in kwargs
2265
+ else kwargs['relative_uncertainties'])
2266
+ rel_uncs = np.array(rel_uncs)
2267
+ mains = np.array(mains)
2268
+ shape = mains.shape
2269
+ if rel_uncs.size == 1:
2270
+ rel_uncs = np.full((*shape, 2), rel_uncs).transpose()
2271
+ elif len(rel_uncs) == 2 and type(rel_uncs[0]) is not np.ndarray:
2272
+ rel_uncs = np.array([np.full(shape, rel_uncs[0]),
2273
+ np.full(shape, rel_uncs[1])])
2274
+ elif rel_uncs.shape == (*shape, 2):
2275
+ rel_uncs = rel_uncs.transpose()
2276
+ elif rel_uncs.shape == shape:
2277
+ rel_uncs = np.array([[rel_uncs]]*2).reshape((2, *shape))
2278
+ uncs = (rel_uncs * mains).transpose()
2132
2279
  if 'are_upper_limits' in kwargs:
2133
- are_uplims = kwargs['are_upper_limits']
2134
- if 'are_finite_ranges' in kwargs:
2135
- are_ranges = kwargs['are_finite_ranges']
2136
- if 'are_integers' in kwargs:
2137
- are_ints = kwargs['are_integers']
2280
+ is_uplim = kwargs['are_upper_limits']
2281
+ if 'are_lower_limits' in kwargs:
2282
+ is_lolim = kwargs['are_lower_limits']
2283
+ if 'are_finite_range' in kwargs:
2284
+ is_range = kwargs['are_finite_ranges']
2285
+ if 'are_integer' in kwargs:
2286
+ is_int = kwargs['are_integers']
2138
2287
 
2139
2288
  mains = np.array(mains)
2140
2289
  shape = mains.shape
@@ -2373,6 +2522,17 @@ class RichArray(np.ndarray):
2373
2522
  return np.array([x.moments(n, central, standarized)
2374
2523
  for x in self.flat]).reshape(self.shape)
2375
2524
 
2525
+ def __setattr__(self, name, value):
2526
+ alt_names = {'mains': 'main', 'uncs': 'unc', 'rel_uncs': 'rel_unc',
2527
+ 'are_uplims': 'is_uplim', 'are_lolims': 'is_lolim',
2528
+ 'are_ranges': 'is_range', 'nums_sf': 'num_sf',
2529
+ 'min_exps': 'min_exp', 'max_decs': 'max_dec',
2530
+ 'extra_sf_lims': 'extra_sf_lim'}
2531
+ if name in alt_names.keys():
2532
+ name = alt_names[name]
2533
+ for x in self.flat:
2534
+ setattr(x, name, value)
2535
+
2376
2536
  def set_params(self, params):
2377
2537
  """Set the rich value parameters of each entry of the rich array."""
2378
2538
  abbreviations = {'is integer': 'is_int',
@@ -2433,13 +2593,16 @@ class RichArray(np.ndarray):
2433
2593
  """Apply a function to the rich array."""
2434
2594
  return function_with_rich_arrays(function, self, **kwargs)
2435
2595
 
2436
- def mean(self):
2437
- return np.array(self).mean()
2596
+ def mean(self, axis, dtype, out, **kwargs):
2597
+ return np.array(self).mean(axis, dtype, out, **kwargs)
2438
2598
 
2439
2599
  def std(self):
2440
2600
  std_function = lambda u: (np.sum((u-u.mean())**2)/(len(self)-1))**0.5
2441
- return self.function(std_function)
2442
-
2601
+ return self.function(std_function)
2602
+
2603
+ def __str__(self):
2604
+ text = np.array2string(self, separator=', ')
2605
+ return text
2443
2606
 
2444
2607
  # Attribute synonims.
2445
2608
  main = mains
@@ -3411,7 +3574,8 @@ def add_rich_values(x, y):
3411
3574
  extra_sf_lim = max(x.extra_sf_lim, y.extra_sf_lim)
3412
3575
  is_int = x.is_int and y.is_int
3413
3576
  domain = [x.domain[0] + y.domain[0], x.domain[1] + y.domain[1]]
3414
- if (x.is_exact or y.is_exact) and (x.is_interv or y.is_interv):
3577
+ if ((x.is_exact or y.is_exact) and (x.is_interv or y.is_interv)
3578
+ or x.is_interv and y.is_interv):
3415
3579
  z = list(np.array(x.interval()) + np.array(y.interval()))
3416
3580
  z = RichValue(z, domain=domain, is_int=is_int)
3417
3581
  elif (not (x.is_interv or y.is_interv)
@@ -3450,6 +3614,8 @@ def multiply_rich_values(x, y):
3450
3614
  else:
3451
3615
  z = function_with_rich_values(lambda a,b: a*b, [x, y], domain=domain,
3452
3616
  is_vectorizable=True)
3617
+ if x.is_range and y.is_centered or x.is_centered and y.is_range:
3618
+ z.is_range = True
3453
3619
  z.num_sf = num_sf
3454
3620
  z.min_exp = min_exp
3455
3621
  z.max_dec = max_dec
@@ -3503,6 +3669,10 @@ def divide_rich_values(x, y):
3503
3669
  else:
3504
3670
  z = function_with_rich_values(lambda a,b: a/b, [x, y], domain=domain,
3505
3671
  is_vectorizable=True)
3672
+ if ((x.is_range and y.is_centered or x.is_centered and y.is_range)
3673
+ and z.is_centered):
3674
+ z.main = z.interval(3.)
3675
+ # z.is_range = True
3506
3676
  z.num_sf = num_sf
3507
3677
  z.min_exp = min_exp
3508
3678
  z.max_dec = max_dec
@@ -3801,7 +3971,7 @@ def parse_as_rich_value(text, abbreviations={}, use_default_extra_sf_lim=False):
3801
3971
  if mantissa <= default_extra_sf_lim:
3802
3972
  default_num_sf = defaultparams['number of significant figures']
3803
3973
  if num_sf < default_num_sf + 1:
3804
- extra_sf_lim = mantissa
3974
+ extra_sf_lim = mantissa - 0.5
3805
3975
  x = x.replace('e0', '')
3806
3976
  main = _eval(x)
3807
3977
  unc = [_eval(dx1), _eval(dx2)]
@@ -3809,7 +3979,13 @@ def parse_as_rich_value(text, abbreviations={}, use_default_extra_sf_lim=False):
3809
3979
  min_exp = min(min_exp, default_min_exp)
3810
3980
  else:
3811
3981
  text = text.replace(' --','--').replace('-- ','--')
3812
- text1, text2 = text.split('--')
3982
+ if text.startswith('('):
3983
+ group, exp = text.split(' e')
3984
+ text1, text2 = group[1:-1].split('--')
3985
+ text1 += 'e'+exp
3986
+ text2 += 'e'+exp
3987
+ else:
3988
+ text1, text2 = text.split('--')
3813
3989
  x1, _, _, _, _, _, me1, md1, el1 = parse_as_rich_value(text1,
3814
3990
  abbreviations, use_default_extra_sf_lim)
3815
3991
  x2, _, _, _, _, _, me2, md2, el2 = parse_as_rich_value(text2,
@@ -3877,6 +4053,11 @@ def rich_value(text=None, domain=None, is_int=None, pdf=None, support=None,
3877
4053
  ' represent the rich value, not both.')
3878
4054
 
3879
4055
  input_domain = copy.copy(domain)
4056
+ if type(input_domain) is str:
4057
+ if input_domain in ('p', 'pos', 'positive'):
4058
+ input_domain = [0., np.inf]
4059
+ elif input_domain in ('n', 'neg', 'negative'):
4060
+ input_domain = [-np.inf, 0.]
3880
4061
  abbreviations = {'inf': 'np.inf', 'tau': 'math.tau', 'pi': 'np.pi',
3881
4062
  'nan': 'np.nan', 'NaN': 'np.nan', 'none': 'np.nan'}
3882
4063
 
@@ -5057,28 +5238,26 @@ def evaluate_sample(sample, function=None, args=None, len_samples=None,
5057
5238
  if not is_range:
5058
5239
  probs_hr, bins_hr = np.histogram(sample, bins=60, density=True)
5059
5240
  probs_hr /= probs_hr.max()
5060
- hr1f, hr2f, lrf, rf = 0.9, 0.8, 0.5, 0.4
5061
- cond_hr1 = (probs_hr[0] > hr1f or probs_hr[-1] > hr1f)
5062
- cond_hr2 = probs_hr[0] > hr2f
5063
- is_lim = cond_hr1 or cond_hr2
5241
+ cond_hr = probs_hr[0] > 0.9 or probs_hr[-1] > 0.9
5242
+ is_lim = cond_hr and (x1 == domain[0] or x1 == domain[1])
5064
5243
  if is_lim:
5065
5244
  ord_range_b1 = magnitude_order_range([x1, median])
5066
5245
  ord_range_b2 = magnitude_order_range([median, x2])
5067
5246
  x1, x2 = add_zero_infs([x1, x2], zero_log-6, inf_log-6)
5068
5247
  if (ord_range_b1 > inf_log-6 and ord_range_b2 > inf_log-6
5069
- and cond_hr2):
5248
+ and cond_hr):
5070
5249
  is_lim = False
5071
5250
  is_nan = True
5072
5251
  if not is_lim and not is_nan:
5073
- cond_lr = lrf < probs_lr[0] < 1.
5252
+ cond_lr = 0.5 < probs_lr[0] < 1.
5074
5253
  interval_1s = [np.quantile(sample, 0.15865),
5075
5254
  np.quantile(sample, 0.84135)]
5076
5255
  ord_range_1s = magnitude_order_range(interval_1s)
5077
5256
  ord_range_x = magnitude_order_range([x1, x2])
5078
- cond_ord = (ord_range_x - ord_range_1s < rf if x1 != x2
5257
+ cond_ord = (ord_range_x - ord_range_1s < 0.4 if x1 != x2
5079
5258
  else False)
5080
- is_range = (cond_ord or (not cond_ord and cond_hr2)
5081
- or (not cond_ord and not cond_hr2 and cond_lr))
5259
+ is_range = (cond_ord or (not cond_ord and cond_hr)
5260
+ or (not cond_ord and not cond_hr and cond_lr))
5082
5261
  if not is_range:
5083
5262
  is_centered = True
5084
5263
  if is_lim and args is not None:
@@ -5143,7 +5322,7 @@ def evaluate_sample(sample, function=None, args=None, len_samples=None,
5143
5322
  x1, x2 = add_zero_infs([x1, x2], zero_log+6, inf_log-6)
5144
5323
  ampls = [x1 - domain[0], domain[1] - x2]
5145
5324
  rel_ampls = np.array(ampls) / (x2 - x1)
5146
- threshold = 0.01
5325
+ threshold = 1e-9
5147
5326
  if rel_ampls[0] < threshold:
5148
5327
  x1 = domain[0]
5149
5328
  if rel_ampls[1] < threshold:
@@ -5157,6 +5336,10 @@ def evaluate_sample(sample, function=None, args=None, len_samples=None,
5157
5336
  if is_centered:
5158
5337
  if not is_exact:
5159
5338
  main, unc = center_and_uncs(sample)
5339
+ asymmetry = abs(unc[1] - unc[0]) / (unc[0] + unc[1])
5340
+ threshold = min(0.05, 10. / np.sqrt(len_samples))
5341
+ if asymmetry < threshold:
5342
+ unc = (unc[0] + unc[1]) / 2
5160
5343
  rvalue = RichValue(main, unc, domain=domain, is_int=is_int)
5161
5344
  else:
5162
5345
  rvalue = RichValue(main, unc=0., domain=domain, is_int=is_int)
@@ -5307,6 +5490,7 @@ def function_with_rich_values(function, args, len_samples=None, domain=None,
5307
5490
 
5308
5491
  if len_samples is None:
5309
5492
  len_samples = int(len(args)**0.5 * defaultparams['size of samples'])
5493
+ len_samples = int(len_samples)
5310
5494
  num_sf = int(np.median([arg.num_sf for arg in args]))
5311
5495
  min_exp = round(np.mean([arg.min_exp for arg in args]))
5312
5496
  max_dec = round(np.mean([arg.max_dec for arg in args]))
@@ -6135,9 +6319,9 @@ def concatenate(arrs, **kwargs):
6135
6319
  # Mathematical functions.
6136
6320
  get_operation_function = lambda x: (function if type(x) in
6137
6321
  (str, RichValue, ComplexRichValue) else array_function)
6138
- def sqrt(x):
6322
+ def sqrt(x, **kwargs):
6139
6323
  function_ = get_operation_function(x)
6140
- return function_('np.sqrt({})', x, domain=[0., np.inf])
6324
+ return function_('np.sqrt({})', x, domain=[0., np.inf], **kwargs)
6141
6325
  def exp(x):
6142
6326
  function_ = get_operation_function(x)
6143
6327
  return function_('np.exp({})', x, domain=[0., np.inf])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: richvalues
3
- Version: 4.2.27
3
+ Version: 4.2.28
4
4
  Summary: Python package for working with uncertainties and upper/lower limits
5
5
  Home-page: https://github.com/andresmegias/richvalues/
6
6
  Author: Andrés Megías Toledano
@@ -5,7 +5,7 @@ with open('README.md', 'r') as file:
5
5
 
6
6
  setuptools.setup(
7
7
  name = 'richvalues',
8
- version = '4.2.27',
8
+ version = '4.2.28',
9
9
  license = 'BSD-3-Clause',
10
10
  author = 'Andrés Megías Toledano',
11
11
  description = 'Python package for working with uncertainties and upper/lower limits',
File without changes
File without changes
File without changes