richvalues 4.0.6__tar.gz → 4.1.0__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.1
2
2
  Name: richvalues
3
- Version: 4.0.6
3
+ Version: 4.1.0
4
4
  Summary: Python library 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.0.6"
7
+ version = "4.1.0"
8
8
  description = "Python library for working with uncertainties and upper/lower limits"
9
9
  license = {file="LICENSE"}
10
10
  authors = [{name="Andrés Megías Toledano"}]
@@ -4,7 +4,7 @@
4
4
  """
5
5
  Rich Values Library
6
6
  -------------------
7
- Version 4.0
7
+ Version 4.1
8
8
 
9
9
  Copyright (C) 2024 - Andrés Megías Toledano
10
10
 
@@ -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.0.6'
40
+ __version__ = '4.1.0'
41
41
  __author__ = 'Andrés Megías Toledano'
42
42
 
43
43
  import copy
@@ -57,6 +57,7 @@ defaultparams = {
57
57
  'size of samples': int(8e3),
58
58
  'number of significant figures': 1,
59
59
  'minimum exponent for scientific notation': 4,
60
+ 'maximum number of decimals': 5,
60
61
  'limit for extra significant figure': 2.5,
61
62
  'use extra significant figure for exact values': True,
62
63
  'use extra significant figure for finite intervals': True,
@@ -174,7 +175,7 @@ def round_sf(x, n=None, min_exp=None, extra_sf_lim=None):
174
175
  y = y.replace('e+','e').replace('e00','e0')
175
176
  return y
176
177
 
177
- def round_sf_unc(x, dx, n=None, min_exp=None, extra_sf_lim=None):
178
+ def round_sf_unc(x, dx, n=None, min_exp=None, max_dec=None, extra_sf_lim=None):
178
179
  """
179
180
  Round a value and its uncertainty depending on their significant figures.
180
181
 
@@ -189,6 +190,9 @@ def round_sf_unc(x, dx, n=None, min_exp=None, extra_sf_lim=None):
189
190
  min_exp : int, optional
190
191
  Minimum decimal exponent, in absolute value, to display the values in
191
192
  scientific notation. The default is 4.
193
+ max_dec : int, optional
194
+ Maximum number of decimals, to use the notation with parenthesis.
195
+ The default is 5.
192
196
  extra_sf_lim : float, optional
193
197
  If the number expressed in scientific notation has a base that is lower
194
198
  than this value, an additional significant figure will be used.
@@ -202,10 +206,9 @@ def round_sf_unc(x, dx, n=None, min_exp=None, extra_sf_lim=None):
202
206
  Rounded uncertainty.
203
207
  """
204
208
  n = set_default_value(n, 'number of significant figures')
205
- min_exp = set_default_value(min_exp,
206
- 'minimum exponent for scientific notation')
207
- extra_sf_lim = set_default_value(extra_sf_lim,
208
- 'limit for extra significant figure')
209
+ min_exp = set_default_value(min_exp, 'minimum exponent for scientific notation')
210
+ max_dec = set_default_value(max_dec, 'maximum number of decimals')
211
+ extra_sf_lim = set_default_value(extra_sf_lim, 'limit for extra significant figure')
209
212
  use_exp = True
210
213
  if ((float(x) > float(dx)
211
214
  and all(abs(np.floor(_log10(abs(np.array([x, dx]))))) < min_exp))
@@ -269,7 +272,7 @@ def round_sf_unc(x, dx, n=None, min_exp=None, extra_sf_lim=None):
269
272
  y = '{}e{}'.format(base_y, exp_dy)
270
273
  else:
271
274
  f = 10**(-int(exp_y))
272
- base_y, dy = round_sf_unc(x*f, dx*f, n, np.inf, extra_sf_lim)
275
+ base_y, dy = round_sf_unc(x*f, dx*f, n, np.inf, max_dec, extra_sf_lim)
273
276
  y = '{}e{}'.format(base_y, exp_y)
274
277
  elif dx == 0:
275
278
  y = round_sf(x, n, min_exp, extra_sf_lim)
@@ -290,12 +293,24 @@ def round_sf_unc(x, dx, n=None, min_exp=None, extra_sf_lim=None):
290
293
  if abs(exp) < min_exp:
291
294
  x = float(sign + str(x))
292
295
  min_exp = np.inf
293
- y, dy = round_sf_unc(x, dx, n, min_exp, extra_sf_lim)
296
+ y, dy = round_sf_unc(x, dx, n, min_exp, max_dec, extra_sf_lim)
294
297
  y = y.replace('e+', 'e').replace('e00', 'e0')
295
298
  dy = dy.replace('e+','e').replace('e00', 'e0')
299
+ d = len(y.split('e')[0].split('.')[-1])
300
+ if d > max_dec and ')' not in dy:
301
+ if 'e' in y:
302
+ y, exp = y.split('e')
303
+ dy, _ = dy.split('e')
304
+ else:
305
+ exp = None
306
+ d = len(y.split('.')[-1])
307
+ dy_ = round_sf(float(dy)*10**d, n, min_exp=np.inf, extra_sf_lim=0.)
308
+ dy = '(' + dy_.split('e')[0] + ')'
309
+ if exp is not None:
310
+ y = '{}e{}'.format(y, exp)
296
311
  return y, dy
297
312
 
298
- def round_sf_uncs(x, dx, n=None, min_exp=None, extra_sf_lim=None):
313
+ def round_sf_uncs(x, dx, n=None, min_exp=None, max_dec=None, extra_sf_lim=None):
299
314
  """
300
315
  Round a value and its uncertainties depending on their significant figures.
301
316
 
@@ -310,6 +325,9 @@ def round_sf_uncs(x, dx, n=None, min_exp=None, extra_sf_lim=None):
310
325
  min_exp : int, optional
311
326
  Minimum decimal exponent, in absolute value, to apply scientific
312
327
  notation. The default is 4.
328
+ max_dec : int, optional
329
+ Maximum number of decimals, to apply notation with parenthesis.
330
+ The default is 5.
313
331
  extra_sf_lim : float, optional
314
332
  If the number expressed in scientific notation has a base that is lower
315
333
  than this value, an additional significant figure will be used.
@@ -323,19 +341,18 @@ def round_sf_uncs(x, dx, n=None, min_exp=None, extra_sf_lim=None):
323
341
  Rounded uncertainties.
324
342
  """
325
343
  n = set_default_value(n, 'number of significant figures')
326
- min_exp = set_default_value(min_exp,
327
- 'minimum exponent for scientific notation')
328
- extra_sf_lim = set_default_value(extra_sf_lim,
329
- 'limit for extra significant figure')
344
+ min_exp = set_default_value(min_exp, 'minimum exponent for scientific notation')
345
+ max_dec = set_default_value(max_dec, 'maximum number of decimals')
346
+ extra_sf_lim = set_default_value(extra_sf_lim, 'limit for extra significant figure')
330
347
  dx1, dx2 = dx
331
- y1, dy1 = round_sf_unc(x, dx1, n, min_exp, extra_sf_lim)
332
- y2, dy2 = round_sf_unc(x, dx2, n, min_exp, extra_sf_lim)
348
+ y1, dy1 = round_sf_unc(x, dx1, n, min_exp, max_dec, extra_sf_lim)
349
+ y2, dy2 = round_sf_unc(x, dx2, n, min_exp, max_dec, extra_sf_lim)
333
350
  num_dec_1 = len(y1.split('e')[0].split('.')[1]) if '.' in y1 else 0
334
351
  num_dec_2 = len(y2.split('e')[0].split('.')[1]) if '.' in y2 else 0
335
352
  if num_dec_2 > num_dec_1:
336
353
  diff = num_dec_2 - num_dec_1
337
- y1, dy1 = round_sf_unc(x, dx1, n+diff, min_exp, extra_sf_lim)
338
- y2, dy2 = round_sf_unc(x, dx2, n, min_exp, extra_sf_lim)
354
+ y1, dy1 = round_sf_unc(x, dx1, n+diff, min_exp, max_dec, extra_sf_lim)
355
+ y2, dy2 = round_sf_unc(x, dx2, n, min_exp, max_dec, extra_sf_lim)
339
356
  else:
340
357
  diff = num_dec_1 - num_dec_2
341
358
  off1, off2 = 0, 0
@@ -348,9 +365,23 @@ def round_sf_uncs(x, dx, n=None, min_exp=None, extra_sf_lim=None):
348
365
  off2 = 1
349
366
  if dx1 > dx2 and b2 <= extra_sf_lim and b1 > extra_sf_lim:
350
367
  off1 = 1
351
- y1, dy1 = round_sf_unc(x, dx1, n+off1, min_exp, extra_sf_lim)
352
- y2, dy2 = round_sf_unc(x, dx2, n+diff+off2, min_exp, extra_sf_lim)
368
+ y1, dy1 = round_sf_unc(x, dx1, n+off1, min_exp, max_dec, extra_sf_lim)
369
+ y2, dy2 = round_sf_unc(x, dx2, n+diff+off2, min_exp, max_dec, extra_sf_lim)
353
370
  y = y1 if dx2 > dx1 else y2
371
+ if dy1 != dy2 and (')' in dy1 or ')' in dy2):
372
+ dy1 = dy1.replace('(', '(-')
373
+ dy2 = dy2.replace('(', '(+')
374
+ if ')' in dy1 and ')' not in dy2 or ')' in dy2 and '.' in dy2:
375
+ _, dy2 = round_sf_unc(x, dx[1], n, min_exp, max_dec-1, extra_sf_lim)
376
+ dy2 = '(' + dy2[1:-1] + '0' + ')'
377
+ elif ')' in dy2 and ')' not in dy1 or ')' in dy1 and '.' in dy1:
378
+ _, dy1 = round_sf_unc(x, dx[0], n, min_exp, max_dec-1, extra_sf_lim)
379
+ dy1 = '(' + dy1[1:-1] + '0' + ')'
380
+ if ')' in dy1 or ')' in dy2:
381
+ if not dy1.startswith('(-'):
382
+ dy1 = '(-' + dy1[1:]
383
+ if not dy2.startswith('(+'):
384
+ dy2 = '(+' + dy2[1:]
354
385
  dy = [dy1, dy2]
355
386
  return y, dy
356
387
 
@@ -533,6 +564,7 @@ class RichValue():
533
564
  self.domain = domain
534
565
  self.num_sf = defaultparams['number of significant figures']
535
566
  self.min_exp = defaultparams['minimum exponent for scientific notation']
567
+ self.max_dec = defaultparams['maximum number of decimals']
536
568
  self.extra_sf_lim = defaultparams['limit for extra significant figure']
537
569
  self.pdf_info = 'default'
538
570
  self.variables = variables
@@ -719,10 +751,10 @@ class RichValue():
719
751
  domain = copy.copy(self.domain)
720
752
  is_int = self.is_int
721
753
  min_exp = abs(self.min_exp)
754
+ max_dec = abs(self.max_dec)
722
755
  extra_sf_lim = self.extra_sf_lim
723
756
  show_domain = defaultparams['show domain']
724
- show_asterisk = defaultparams['show asterisk for rich values with'
725
- ' custom PDF']
757
+ show_asterisk = defaultparams['show asterisk for rich values with custom PDF']
726
758
  use_extra_sf_in_exacts = \
727
759
  defaultparams['use extra significant figure for exact values']
728
760
  use_extra_sf_in_ranges = \
@@ -739,7 +771,7 @@ class RichValue():
739
771
  else False)
740
772
  use_exp = True
741
773
  if ((self.is_centr and 'e' not in
742
- str(round_sf_uncs(x, dx, n, min_exp, extra_sf_lim)))
774
+ str(round_sf_uncs(x, dx, n, min_exp, max_dec, extra_sf_lim)))
743
775
  or self.is_lim and abs(np.floor(_log10(abs(float(x))) < min_exp))
744
776
  or np.isinf(min_exp)):
745
777
  use_exp = False
@@ -750,8 +782,8 @@ class RichValue():
750
782
  if (not range_bound and use_extra_sf_in_exacts
751
783
  or range_bound and use_extra_sf_in_ranges):
752
784
  n += 1
753
- y, (dy1, dy2) = round_sf_uncs(x, [dx1, dx2], n,
754
- min_exp, extra_sf_lim)
785
+ y, (dy1, dy2) = round_sf_uncs(x, [dx1, dx2], n, min_exp,
786
+ max_dec, extra_sf_lim)
755
787
  if 'e' in y:
756
788
  y, a = y.split('e')
757
789
  a = int(a)
@@ -761,14 +793,20 @@ class RichValue():
761
793
  dy1, _ = dy1.split('e')
762
794
  if 'e' in dy2:
763
795
  dy2, _ = dy2.split('e')
764
- if dy1 == dy2:
765
- if float(dy1) != 0:
766
- text = '{}+/-{} e{}'.format(y, dy1, a)
796
+ if ')' not in dy1:
797
+ if dy1 == dy2:
798
+ if float(dy1) != 0:
799
+ text = '{}+/-{} e{}'.format(y, dy1, a)
800
+ else:
801
+ y = int(round(float(y))) if is_int else y
802
+ text = '{} e{}'.format(y, a)
767
803
  else:
768
- y = int(round(float(y))) if is_int else y
769
- text = '{} e{}'.format(y, a)
804
+ text = '{}-{}+{} e{}'.format(y, dy1, dy2, a)
770
805
  else:
771
- text = '{}-{}+{} e{}'.format(y, dy1, dy2, a)
806
+ if dy1 == dy2:
807
+ text = '{}{} e{}'.format(y, dy1, a)
808
+ else:
809
+ text = '{}{}{} e{}'.format(y, dy1, dy2, a)
772
810
  if not use_exp:
773
811
  text = text.replace(' e0','')
774
812
  else:
@@ -851,6 +889,7 @@ class RichValue():
851
889
  is_range = self.is_range
852
890
  is_int = self.is_int
853
891
  min_exp = abs(self.min_exp)
892
+ max_dec = abs(self.max_dec)
854
893
  extra_sf_lim = self.extra_sf_lim
855
894
  show_domain = defaultparams['show domain']
856
895
  use_exp = True
@@ -874,7 +913,7 @@ class RichValue():
874
913
  else False)
875
914
  if is_numeric:
876
915
  if not is_range:
877
- _, unc_r = round_sf_uncs(x, dx, n, min_exp, extra_sf_lim)
916
+ _, unc_r = round_sf_uncs(x, dx, n, min_exp, np.inf, extra_sf_lim)
878
917
  unc_r = np.array(unc_r, float)
879
918
  if not is_range and not use_exp:
880
919
  if not self.is_lim:
@@ -886,11 +925,11 @@ class RichValue():
886
925
  round_sf(x, n, np.inf, extra_sf_lim))
887
926
  text = '${}$'.format(y)
888
927
  else:
889
- y, dy = round_sf_unc(x, dx[0], n,
890
- min_exp, extra_sf_lim)
928
+ y, dy = round_sf_unc(x, dx[0], n, min_exp,
929
+ max_dec, extra_sf_lim)
891
930
  text = '${} \pm {}$'.format(y, dy)
892
931
  else:
893
- y, dy = round_sf_uncs(x, dx, n, min_exp, extra_sf_lim)
932
+ y, dy = round_sf_uncs(x, dx, n, min_exp, max_dec, extra_sf_lim)
894
933
  text = '$'+y + '_{-'+dy[0]+'}^{+'+dy[1]+'}$'
895
934
  else:
896
935
  if is_lolim:
@@ -918,8 +957,8 @@ class RichValue():
918
957
  text = ('${} {}'.format(y, mult_symbol)
919
958
  + ' 10^{'+a+'}$')
920
959
  else:
921
- y, dy = round_sf_unc(x, dx[0], n,
922
- min_exp, extra_sf_lim)
960
+ y, dy = round_sf_unc(x, dx[0], n, min_exp,
961
+ max_dec, extra_sf_lim)
923
962
  if 'e' in y:
924
963
  y, a = y.split('e')
925
964
  dy, a = dy.split('e')
@@ -929,8 +968,8 @@ class RichValue():
929
968
  text = ('$({} \pm {}) '.format(y, dy)
930
969
  + mult_symbol + ' 10^{'+a+'}$')
931
970
  else:
932
- y, dy = round_sf_uncs(x, [dx[0], dx[1]], n,
933
- min_exp, extra_sf_lim)
971
+ y, dy = round_sf_uncs(x, [dx[0], dx[1]], n, min_exp,
972
+ max_dec, extra_sf_lim)
934
973
  if 'e' in y:
935
974
  y, a = y.split('e')
936
975
  dy1, a = dy[0].split('e')
@@ -1456,6 +1495,11 @@ class RichValue():
1456
1495
  @minimum_exponent_for_scientific_notation.setter
1457
1496
  def minimum_exponent_for_scientific_notation(self, x): self.min_exp = x
1458
1497
 
1498
+ @property
1499
+ def maximum_number_of_decimals(self): return self.max_dec
1500
+ @maximum_number_of_decimals.setter
1501
+ def maximum_number_of_decimals(self, x): self.max_dec = x
1502
+
1459
1503
  @property
1460
1504
  def limit_for_extra_significant_figure(self): return self.extra_sf_lim
1461
1505
  @limit_for_extra_significant_figure.setter
@@ -1646,6 +1690,9 @@ class RichArray(np.ndarray):
1646
1690
  def min_exps(self):
1647
1691
  return np.array([x.min_exp for x in self.flat]).reshape(self.shape)
1648
1692
  @property
1693
+ def max_decs(self):
1694
+ return np.array([x.max_dec for x in self.flat]).reshape(self.shape)
1695
+ @property
1649
1696
  def extra_sf_lims(self):
1650
1697
  return np.array([x.extra_sf_lim for x in self.flat]).reshape(self.shape)
1651
1698
  @property
@@ -1664,6 +1711,12 @@ class RichArray(np.ndarray):
1664
1711
  def are_consts(self):
1665
1712
  return np.array([x.is_const for x in self.flat]).reshape(self.shape)
1666
1713
  @property
1714
+ def are_nans(self):
1715
+ return np.array([x.is_nan for x in self.flat]).reshape(self.shape)
1716
+ @property
1717
+ def are_infs(self):
1718
+ return np.array([x.is_inf for x in self.flat]).reshape(self.shape)
1719
+ @property
1667
1720
  def centers(self):
1668
1721
  return np.array([x.center for x in self.flat]).reshape(self.shape)
1669
1722
  @property
@@ -1742,6 +1795,7 @@ class RichArray(np.ndarray):
1742
1795
  abbreviations = {'is integer': 'is_int',
1743
1796
  'number of significant figures': 'num_sf',
1744
1797
  'minimum exponent for scientific notation': 'min_exp',
1798
+ 'maximum number of decimals': 'max_dec',
1745
1799
  'limit for extra significant figure': 'extra_sf_lim'}
1746
1800
  attributes = ['domain'] + list(abbreviations.values())
1747
1801
  for entry in abbreviations:
@@ -1761,6 +1815,8 @@ class RichArray(np.ndarray):
1761
1815
  x.num_sf = params['num_sf']
1762
1816
  if 'min_exp' in params:
1763
1817
  x.min_exp = params['min_exp']
1818
+ if 'max_dec' in params:
1819
+ x.max_dec = params['max_dec']
1764
1820
  if 'extra_sf_lim' in params:
1765
1821
  x.extra_sf_lim = params['extra_sf_lim']
1766
1822
 
@@ -1803,8 +1859,7 @@ class RichArray(np.ndarray):
1803
1859
  return np.array(self).mean()
1804
1860
 
1805
1861
  def std(self):
1806
- std_function = lambda u: (np.sum((u - u.mean())**2)
1807
- / len(self - 1))**0.5
1862
+ std_function = lambda u: (np.sum((u-u.mean())**2)/(len(self)-1))**0.5
1808
1863
  return self.function(std_function)
1809
1864
 
1810
1865
  # Attribute acronyms.
@@ -1816,11 +1871,14 @@ class RichArray(np.ndarray):
1816
1871
  are_integers = are_ints
1817
1872
  numbers_of_significant_figures = nums_sf
1818
1873
  minimum_exponents_for_scientific_notation = min_exps
1874
+ maximum_number_of_decimals = max_decs
1819
1875
  limits_for_extra_significant_figure = extra_sf_lims
1820
1876
  are_limits = are_lims
1821
1877
  are_intervals = are_intervs
1822
1878
  are_centereds = are_centrs
1823
1879
  are_constants = are_consts
1880
+ are_not_a_number = are_nans
1881
+ are_infinites = are_infs
1824
1882
  relative_uncertainties = rel_uncs
1825
1883
  signals_to_noises = signals_noises
1826
1884
  amplitudes = ampls
@@ -1909,6 +1967,8 @@ class RichDataFrame(pd.DataFrame):
1909
1967
  @property
1910
1968
  def min_exps(self): return self._property('min_exps')
1911
1969
  @property
1970
+ def max_decs(self): return self._property('max_decs')
1971
+ @property
1912
1972
  def extra_sf_lims(self): return self._property('extra_sf_lims')
1913
1973
  @property
1914
1974
  def are_lims(self): return self._property('are_lims')
@@ -1921,6 +1981,10 @@ class RichDataFrame(pd.DataFrame):
1921
1981
  @property
1922
1982
  def are_consts(self): return self._property('are_consts')
1923
1983
  @property
1984
+ def are_nans(self): return self._property('are_nans')
1985
+ @property
1986
+ def are_infs(self): return self._property('are_infs')
1987
+ @property
1924
1988
  def centers(self): return self._property('centers')
1925
1989
  @property
1926
1990
  def rel_uncs(self): return self._property2('rel_uncs')
@@ -1968,6 +2032,7 @@ class RichDataFrame(pd.DataFrame):
1968
2032
  abbreviations = {'is integer': 'is_int',
1969
2033
  'number of significant figures': 'num_sf',
1970
2034
  'minimum exponent for scientific notation': 'min_exp',
2035
+ 'maximum number of decimals': 'max_dec',
1971
2036
  'limit for extra significant figure': 'extra_sf_lim'}
1972
2037
  attributes = ['domain'] + list(abbreviations.values())
1973
2038
  for entry in abbreviations:
@@ -1988,6 +2053,7 @@ class RichDataFrame(pd.DataFrame):
1988
2053
  set_is_int = 'is_int' in params
1989
2054
  set_num_sf = 'num_sf' in params
1990
2055
  set_min_exp = 'min_exp' in params
2056
+ set_max_dec = 'max_dec' in params
1991
2057
  set_extra_sf_lim = 'extra_sf_lim' in params
1992
2058
  row_inds = self.index
1993
2059
  for col in self:
@@ -2006,6 +2072,9 @@ class RichDataFrame(pd.DataFrame):
2006
2072
  if set_min_exp and col in params['min_exp']:
2007
2073
  for i in row_inds:
2008
2074
  self[col][i].min_exp = params['min_exp'][col]
2075
+ if set_max_dec and col in params['max_dec']:
2076
+ for i in row_inds:
2077
+ self[col][i].max_dec = params['max_dec'][col]
2009
2078
  if set_extra_sf_lim and col in params['extra_sf_lim']:
2010
2079
  for i in row_inds:
2011
2080
  self[col][i].extra_sf_lim = params['extra_sf_lim'][col]
@@ -2064,7 +2133,8 @@ class RichDataFrame(pd.DataFrame):
2064
2133
  new_row = pd.DataFrame(new_row, idex=[0])
2065
2134
  return new_row
2066
2135
 
2067
- def latex(self, return_df=False, row_sep='\\tabularnewline', **kwargs):
2136
+ def latex(self, return_df=False, export_frame=True, export_index=True,
2137
+ row_sep='\\tabularnewline', **kwargs):
2068
2138
  """Return the content of the dataframe as a table in LaTeX format."""
2069
2139
  row_sep = ' ' + row_sep + ' \n'
2070
2140
  df = copy.copy(self)
@@ -2079,17 +2149,35 @@ class RichDataFrame(pd.DataFrame):
2079
2149
  if return_df:
2080
2150
  output = df
2081
2151
  else:
2152
+ columns = list(df.columns)
2153
+ num_columns = len(columns)
2082
2154
  text = ''
2155
+ if export_frame:
2156
+ text += '\\renewcommand*{\\arraystretch}{1.4}' + ' \n'
2157
+ text += ('\\begin{tabular}{' + 'l'*export_index
2158
+ + num_columns*'c' + '}' + ' \n')
2159
+ text += '\\hline \n'
2160
+ columns = ['{\\bf ' + column + '}' for column in columns]
2161
+ index_name = df.index.name if df.index.name is not None else ' '
2162
+ if export_index:
2163
+ columns = ['{\\bf ' + index_name + '}'] + columns
2164
+ if export_frame:
2165
+ text += ' & '.join(columns) + row_sep + '\\hline \n'
2083
2166
  rows = []
2084
- for (i,row) in df.iterrows():
2085
- cols = []
2167
+ for (ind,row) in df.iterrows():
2168
+ cols = [str(ind)] if export_index else []
2086
2169
  for (j,column) in enumerate(df):
2087
2170
  entry = str(row[column])
2088
2171
  if entry == 'nan':
2089
2172
  entry = '...'
2090
2173
  cols += [entry]
2091
2174
  rows += [' & '.join(cols)]
2092
- text = row_sep.join(rows)
2175
+ text += row_sep.join(rows)
2176
+ if export_frame:
2177
+ text += row_sep
2178
+ text += '\\hline \n'
2179
+ text += '\\end{tabular}' + ' \n'
2180
+ text += '\\renewcommand*{\\arraystretch}{1.0}' + ' \n'
2093
2181
  output = text
2094
2182
  return output
2095
2183
 
@@ -2122,6 +2210,8 @@ class RichDataFrame(pd.DataFrame):
2122
2210
  are_intervals = are_intervs
2123
2211
  are_centereds = are_centrs
2124
2212
  are_constants = are_consts
2213
+ are_not_a_number = are_nans
2214
+ are_infinites = are_infs
2125
2215
  relative_uncertainties = rel_uncs
2126
2216
  signals_to_noises = signals_noises
2127
2217
  amplitudes = ampls
@@ -2175,6 +2265,9 @@ class RichSeries(pd.Series):
2175
2265
  def min_exps(self):
2176
2266
  return pd.Series(self.values.view(RichArray).min_exps, self.index)
2177
2267
  @property
2268
+ def max_decs(self):
2269
+ return pd.Series(self.values.view(RichArray).max_decs, self.index)
2270
+ @property
2178
2271
  def extra_sf_lims(self):
2179
2272
  return pd.Series(self.values.view(RichArray).extra_sf_lim, self.index)
2180
2273
  @property
@@ -2193,6 +2286,12 @@ class RichSeries(pd.Series):
2193
2286
  def are_consts(self):
2194
2287
  return pd.Series(self.values.view(RichArray).are_consts, self.index)
2195
2288
  @property
2289
+ def are_nans(self):
2290
+ return pd.Series(self.values.view(RichArray).are_nans, self.index)
2291
+ @property
2292
+ def are_infs(self):
2293
+ return pd.Series(self.values.view(RichArray).are_infs, self.index)
2294
+ @property
2196
2295
  def centers(self):
2197
2296
  return pd.Series(self.values.view(RichArray).centers, self.index)
2198
2297
  @property
@@ -2254,10 +2353,13 @@ class RichSeries(pd.Series):
2254
2353
  are_integers = are_ints
2255
2354
  numbers_of_scientific_figures = nums_sf
2256
2355
  minimum_exponents_for_scientific_notation = min_exps
2356
+ maximum_numbers_of_decimals = max_decs
2257
2357
  are_limits = are_lims
2258
2358
  are_intervals = are_intervs
2259
2359
  are_centereds = are_centrs
2260
2360
  are_constants = are_consts
2361
+ are_not_a_number = are_nans
2362
+ are_infinites = are_infs
2261
2363
  relative_uncertainties = rel_uncs
2262
2364
  signals_to_noises = signals_noises
2263
2365
  amplitudes = ampls
@@ -2309,11 +2411,14 @@ class ComplexRichValue():
2309
2411
 
2310
2412
  num_sf = min(real.num_sf, imag.num_sf)
2311
2413
  min_exp = round(np.mean([real.min_exp, imag.min_exp]))
2414
+ max_dec = min(real.max_dec, imag.max_dec)
2312
2415
  extra_sf_lim = min(real.extra_sf_lim, imag.extra_sf_lim)
2313
2416
  real.num_sf = num_sf
2314
2417
  imag.num_sf = num_sf
2315
2418
  real.min_exp = min_exp
2316
2419
  imag.min_exp = min_exp
2420
+ real.max_dec = max_dec
2421
+ real.max_dec = max_dec
2317
2422
  real.extra_sf_lim = extra_sf_lim
2318
2423
  imag.extra_sf_lim = extra_sf_lim
2319
2424
 
@@ -2356,16 +2461,21 @@ class ComplexRichValue():
2356
2461
  self.imag.num_sf = x
2357
2462
 
2358
2463
  @property
2359
- def min_exp(self): return round(np.mean([self.real.min_exp,
2360
- self.imag.min_exp]))
2464
+ def min_exp(self): return round(np.mean([self.real.min_exp, self.imag.min_exp]))
2361
2465
  @min_exp.setter
2362
2466
  def min_exp(self, x):
2363
2467
  self.real.min_exp = x
2364
2468
  self.imag.min_exp = x
2469
+
2470
+ @property
2471
+ def max_dec(self): return min(self.real.max_dec, self.imag.max_dec)
2472
+ @max_dec.setter
2473
+ def max_dec(self, x):
2474
+ self.real.max_dec = x
2475
+ self.imag.max_dec = x
2365
2476
 
2366
2477
  @property
2367
- def extra_sf_lim(self): return max(self.real.extra_sf_lim,
2368
- self.imag.extra_sf_lim)
2478
+ def extra_sf_lim(self): return max(self.real.extra_sf_lim, self.imag.extra_sf_lim)
2369
2479
  @extra_sf_lim.setter
2370
2480
  def extra_sf_lim(self, x):
2371
2481
  self.real.extra_sf_lim = x
@@ -2937,7 +3047,7 @@ def rich_value(text=None, domain=None, is_int=None, pdf=None,
2937
3047
  def parse_as_rich_value(text):
2938
3048
  """Obtain the properties of the input text as a rich value."""
2939
3049
  def parse_value(text):
2940
- """Parse input text as a value."""
3050
+ """Parse input text as a numeric value."""
2941
3051
  text = str(text)
2942
3052
  if any([char.isalpha() for char in text.replace(' e','')]):
2943
3053
  for short_name in abbreviations:
@@ -2996,8 +3106,24 @@ def rich_value(text=None, domain=None, is_int=None, pdf=None,
2996
3106
  is_uplim, is_lolim = False, False
2997
3107
  text = (text.replace('+-', '+/-').replace(' -', '-')
2998
3108
  .replace(' +/-', '+/-').replace('+/- ', '+/-'))
2999
- if '+/-' in text:
3000
- x_dx, e = text.split(' ')
3109
+ x_dx, e = text.split(' ')
3110
+ if ')' in text:
3111
+ if text.count(')') == 1:
3112
+ x, dx = x_dx.split('(')
3113
+ dx = dx[:-1]
3114
+ dx1, dx2 = dx
3115
+ else:
3116
+ x, dx1, dx2 = x_dx.split('(')
3117
+ dx1 = dx1[:-1]
3118
+ dx2 = dx2[:-1]
3119
+ if dx1.startswith('+'):
3120
+ dx1, dx2 = dx2, dx1
3121
+ dx1 = dx1[1:]
3122
+ dx2 = dx2[1:]
3123
+ d = len(x.split('.')[1]) if '.' in x else 0
3124
+ dx1 = '{:f}'.format(float(dx1)*10**(-d))
3125
+ dx2 = '{:f}'.format(float(dx2)*10**(-d))
3126
+ elif '+/-' in text:
3001
3127
  x, dx = x_dx.split('+/-')
3002
3128
  text = '{}-{}+{} {}'.format(x, dx, dx, e)
3003
3129
  dx1, dx2 = dx, dx
@@ -3018,10 +3144,9 @@ def rich_value(text=None, domain=None, is_int=None, pdf=None,
3018
3144
  else:
3019
3145
  x = text.split(' ')[0]
3020
3146
  dx1, dx2 = '0', '0'
3021
- e = text.split(' ')[1]
3022
- x = '{} {}'.format(x ,e)
3023
- dx1 = '{} {}'.format(dx1 ,e)
3024
- dx2 = '{} {}'.format(dx2 ,e)
3147
+ x = '{} {}'.format(x, e)
3148
+ dx1 = '{} {}'.format(dx1, e)
3149
+ dx2 = '{} {}'.format(dx2, e)
3025
3150
  x = parse_value(x)
3026
3151
  dx1 = parse_value(dx1)
3027
3152
  dx2 = parse_value(dx2)
@@ -3276,8 +3401,10 @@ def rich_dataframe(df, domains=None, are_ints=None,
3276
3401
  domain = defaultparams['domain']
3277
3402
  if is_int is None:
3278
3403
  is_int = defaultparams['assume integers']
3279
- entry = rich_value(text, domain, is_int,
3280
- use_default_extra_sf_lim)
3404
+ try:
3405
+ entry = rich_value(text, domain, is_int, use_default_extra_sf_lim)
3406
+ except:
3407
+ entry = text
3281
3408
  if is_rich_value or is_number:
3282
3409
  df.at[i,col] = entry
3283
3410
  rdf = RichDataFrame(df)
@@ -3778,6 +3905,8 @@ def evaluate_distr(distr, domain=None, function=None, args=None,
3778
3905
  return tuple(all_vars)
3779
3906
 
3780
3907
  if args is not None:
3908
+ if type(args) is np.ndarray:
3909
+ args = [rich_value(arg) for arg in args]
3781
3910
  if type(args) not in (tuple, list):
3782
3911
  args = [args]
3783
3912
  if type(args[0]) is not RichArray:
@@ -4357,13 +4486,19 @@ def function_with_rich_arrays(function, args, elementwise=False, **kwargs):
4357
4486
  output = []
4358
4487
  for k in range(output_size):
4359
4488
  function_k = lambda *args: function(args)[k]
4360
- rval_k = evaluate_distr(distr[:,k], domain, function_k, args,
4361
- **kwargs)
4489
+ rval_k = evaluate_distr(distr[:,k], domain, function_k, **kwargs)
4362
4490
  output += [rval_k]
4363
- args_main = np.array([arg.main for arg in args])
4364
- with np.errstate(divide='ignore', invalid='ignore'):
4365
- main = function(*args_main)
4366
- output_size = np.array(main).size
4491
+ if type(args) not in (tuple, list):
4492
+ args = [args]
4493
+ for (i,arg) in enumerate(args):
4494
+ if type(arg) is not RichArray:
4495
+ args[i] = RichArray(arg)
4496
+ args_mains = [arg.mains for arg in args]
4497
+ if type(function) is not str:
4498
+ with np.errstate(divide='ignore', invalid='ignore'):
4499
+ main = function(*args_mains)
4500
+ else:
4501
+ main = distr[0,:]
4367
4502
  output_type = RichArray if type(main) is np.ndarray else type(main)
4368
4503
  if output_type is tuple and output_size > 1:
4369
4504
  output = tuple(output)
@@ -4400,15 +4535,14 @@ def distr_with_rich_arrays(function, args, elementwise=False, **kwargs):
4400
4535
  break
4401
4536
  if not same_shapes:
4402
4537
  raise Exception('Input arrays have different shapes.')
4403
- array = np.empty(0, float)
4538
+ distr = []
4404
4539
  args_flat = np.array([arg.flatten() for arg in args])
4405
4540
  for i in range(args[0].size):
4406
4541
  args_i = np.array(args_flat)[:,i].tolist()
4407
- rvalue = distr_with_rich_values(function, args_i, **kwargs)
4408
- array = np.append(array, rvalue)
4409
- if shape == ():
4410
- array = np.array(array[0])
4411
- output = array
4542
+ distr_i = distr_with_rich_values(function, args_i, **kwargs)
4543
+ distr += [distr_i]
4544
+ distr = np.array(distr).T
4545
+ output = distr
4412
4546
  else:
4413
4547
  if type(function) is str:
4414
4548
  variables = list(np.concatenate(tuple(arg.variables for arg in args)))
@@ -4924,6 +5058,10 @@ def _log10(x):
4924
5058
  y = np.log10(x)
4925
5059
  return y
4926
5060
 
5061
+ # Abbreviations from NumPy.
5062
+ inf = np.inf
5063
+ nan = np.nan
5064
+
4927
5065
  # Functions for masking arrays.
4928
5066
  def isnan(x):
4929
5067
  x = rich_array(x) if type(x) is not RichArray else x
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: richvalues
3
- Version: 4.0.6
3
+ Version: 4.1.0
4
4
  Summary: Python library 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.0.6',
8
+ version = '4.1.0',
9
9
  license = 'BSD-3-Clause',
10
10
  author = 'Andrés Megías Toledano',
11
11
  description = 'Python library for working with uncertainties and upper/lower limits',
File without changes
File without changes
File without changes