source: sasview/sansutil/formatnum.py @ d62f422

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since d62f422 was 4025019, checked in by Mathieu Doucet <doucetm@…>, 13 years ago

Adding util and park

  • Property mode set to 100644
File size: 18.6 KB
Line 
1# This program is public domain
2# Author: Paul Kienzle
3"""
4Format values and uncertainties nicely for printing.
5
6:func:`format_uncertainty_pm`(v,err) produces the expanded format v +/- err.
7
8:func:`format_uncertainty_compact`(v,err) produces the compact format v(##),
9where the number in parenthesis is the uncertainty in the last two digits of v.
10
11:func:`format_uncertainty`(v,err) uses the compact format by default, but this
12can be changed to use the expanded +/- format by setting
13format_uncertainty.compact to False.
14   
15The formatted string uses only the number of digits warranted by
16the uncertainty in the measurement.
17
18If the uncertainty is 0 or not otherwise provided, the simple
19%g floating point format option is used.
20
21Infinite and indefinite numbers are represented as inf and NaN.
22
23Example::
24
25    >>> v,dv = 757.2356,0.01032
26    >>> print format_uncertainty_pm(v,dv)
27    757.236 +/- 0.010
28    >>> print format_uncertainty_compact(v,dv)
29    757.236(10)
30    >>> print format_uncertainty(v,dv)
31    757.236(10)
32    >>> format_uncertainty.compact = False
33    >>> print format_uncertainty(v,dv)
34    757.236 +/- 0.010
35
36UncertaintyFormatter() returns a private formatter with its own
37formatter.compact flag.
38"""
39from __future__ import division
40
41import math
42import numpy
43__all__ = ['format_uncertainty', 'format_uncertainty_pm',
44           'format_uncertainty_compact']
45
46# Coordinating scales across a set of numbers is not supported.  For easy
47# comparison a set of numbers should be shown in the same scale.  One could
48# force this from the outside by adding scale parameter (either 10**n, n, or
49# a string representing the desired SI prefix) and having a separate routine
50# which computes the scale given a set of values.
51
52# Coordinating scales with units offers its own problems.  Again, the user
53# may want to force particular units.  This can be done by outside of the
54# formatting routines by scaling the numbers to the appropriate units then
55# forcing them to print with scale 10**0.  If this is a common operation,
56# however, it may want to happen inside.
57
58# The value e<n> is currently formatted into the number.  Alternatively this
59# scale factor could be returned so that the user can choose the appropriate
60# SI prefix when printing the units.  This gets tricky when talking about
61# composite units such as 2.3e-3 m**2 -> 2300 mm**2, and with volumes
62# such as 1 g/cm**3 -> 1 kg/L.
63
64def format_uncertainty_pm(value, uncertainty):
65    """
66    Given *value* v and *uncertainty* dv, return a string v +/- dv.
67    """
68    return _format_uncertainty(value, uncertainty, compact=False)
69
70def format_uncertainty_compact(value, uncertainty):
71    """
72    Given *value* v and *uncertainty* dv, return the compact
73    representation v(##), where ## are the first two digits of
74    the uncertainty.
75    """
76    return _format_uncertainty(value, uncertainty, compact=True)
77
78class UncertaintyFormatter:
79    """
80    Value and uncertainty formatter.
81
82    The *formatter* instance will use either the expanded v +/- dv form
83    or the compact v(##) form depending on whether *formatter.compact* is
84    True or False.  The default is True.
85    """
86    compact = True
87    def __call__(self, value, uncertainty):
88        """
89        Given *value* and *uncertainty*, return a string representation.
90        """
91        return _format_uncertainty(value, uncertainty, self.compact)
92format_uncertainty = UncertaintyFormatter()
93
94def _format_uncertainty(value, uncertainty, compact):
95    """
96    Implementation of both the compact and the +/- formats.
97    """
98    # Handle indefinite value
99    if numpy.isinf(value):
100        return "inf" if value > 0 else "-inf"
101    if numpy.isnan(value):
102        return "NaN"
103
104    # Handle indefinite uncertainty
105    if uncertainty is None or uncertainty<=0 or numpy.isnan(uncertainty):
106        return "%g"%value
107    if numpy.isinf(uncertainty):
108        if compact:
109            return "%.2g(inf)"%value
110        else:
111            return "%.2g +/- inf"%value
112
113    # Handle zero and negative values
114    sign = "-" if value < 0 else ""
115    value = abs(value)
116
117    # Determine scale of value and error
118    err_place = int(math.floor(math.log10(uncertainty)))
119    if value == 0:
120        val_place = err_place - 1
121    else:
122        val_place = int(math.floor(math.log10(value)))
123
124    if err_place > val_place:
125        # Degenerate case: error bigger than value
126        # The mantissa is 0.#(##)e#, 0.0#(##)e# or 0.00#(##)e#
127        val_place = err_place+2
128    elif err_place == val_place:
129        # Degenerate case: error and value the same order of magnitude
130        # The value is ##(##)e#, #.#(##)e# or 0.##(##)e#
131        val_place = err_place+1
132    elif err_place <= 1 and val_place >= -3:
133        # Normal case: nice numbers and errors
134        # The value is ###.###(##)
135        val_place = 0
136    else:
137        # Extreme cases: zeros before value or after error
138        # The value is ###.###(##)e#, ##.####(##)e# or #.#####(##)e#
139        pass
140   
141    # Force engineering notation, with exponent a multiple of 3
142    val_place = int(math.floor(val_place/3.))*3
143
144    # Format the result
145    digits_after_decimal = abs(val_place - err_place + 1)
146    val_str = "%.*f"%(digits_after_decimal,value/10.**val_place)
147    exp_str = "e%d"%val_place if val_place != 0 else ""
148    if compact:
149        err_str = "(%2d)"%int(uncertainty/10.**(err_place-1)+0.5)
150        result = "".join((sign,val_str,err_str,exp_str))
151    else:
152        err_str = "%.*f"%(digits_after_decimal,uncertainty/10.**val_place)
153        result = "".join((sign,val_str,exp_str+" +/- ",err_str,exp_str))
154    #print sign,value, uncertainty, "=>", result
155    return result
156
157
158def test_compact():
159    # Oops... renamed function after writing tests
160    value_str = format_uncertainty_compact
161
162    # val_place > err_place
163    assert value_str(1235670,766000) == "1.24(77)e6"
164    assert value_str(123567.,76600) == "124(77)e3"
165    assert value_str(12356.7,7660) == "12.4(77)e3"
166    assert value_str(1235.67,766) == "1.24(77)e3"
167    assert value_str(123.567,76.6) == "124(77)"
168    assert value_str(12.3567,7.66) == "12.4(77)"
169    assert value_str(1.23567,.766) == "1.24(77)"
170    assert value_str(.123567,.0766) == "0.124(77)"
171    assert value_str(.0123567,.00766) == "0.0124(77)"
172    assert value_str(.00123567,.000766) == "0.00124(77)"
173    assert value_str(.000123567,.0000766) == "124(77)e-6"
174    assert value_str(.0000123567,.00000766) == "12.4(77)e-6"
175    assert value_str(.00000123567,.000000766) == "1.24(77)e-6"
176    assert value_str(.000000123567,.0000000766) == "124(77)e-9"
177    assert value_str(.00000123567,.0000000766) == "1.236(77)e-6"
178    assert value_str(.0000123567,.0000000766) == "12.357(77)e-6"
179    assert value_str(.000123567,.0000000766) == "123.567(77)e-6"
180    assert value_str(.00123567,.000000766) == "0.00123567(77)"
181    assert value_str(.0123567,.00000766) == "0.0123567(77)"
182    assert value_str(.123567,.0000766) == "0.123567(77)"
183    assert value_str(1.23567,.000766) == "1.23567(77)"
184    assert value_str(12.3567,.00766) == "12.3567(77)"
185    assert value_str(123.567,.0764) == "123.567(76)"
186    assert value_str(1235.67,.764) == "1235.67(76)"
187    assert value_str(12356.7,7.64) == "12356.7(76)"
188    assert value_str(123567,76.4) == "123567(76)"
189    assert value_str(1235670,764) == "1.23567(76)e6"
190    assert value_str(12356700,764) == "12.35670(76)e6"
191    assert value_str(123567000,764) == "123.56700(76)e6"
192    assert value_str(123567000,7640) == "123.5670(76)e6"
193    assert value_str(1235670000,76400) == "1.235670(76)e9"
194
195    # val_place == err_place
196    assert value_str(123567,764000) == "0.12(76)e6"
197    assert value_str(12356.7,76400) == "12(76)e3"
198    assert value_str(1235.67,7640) == "1.2(76)e3"
199    assert value_str(123.567,764) == "0.12(76)e3"
200    assert value_str(12.3567,76.4) == "12(76)"
201    assert value_str(1.23567,7.64) == "1.2(76)"
202    assert value_str(.123567,.764) == "0.12(76)"
203    assert value_str(.0123567,.0764) == "12(76)e-3"
204    assert value_str(.00123567,.00764) == "1.2(76)e-3"
205    assert value_str(.000123567,.000764) == "0.12(76)e-3"
206
207    # val_place == err_place-1
208    assert value_str(123567,7640000) == "0.1(76)e6"
209    assert value_str(12356.7,764000) == "0.01(76)e6"
210    assert value_str(1235.67,76400) == "0.001(76)e6"
211    assert value_str(123.567,7640) == "0.1(76)e3"
212    assert value_str(12.3567,764) == "0.01(76)e3"
213    assert value_str(1.23567,76.4) == "0.001(76)e3"
214    assert value_str(.123567,7.64) == "0.1(76)"
215    assert value_str(.0123567,.764) == "0.01(76)"
216    assert value_str(.00123567,.0764) == "0.001(76)"
217    assert value_str(.000123567,.00764) == "0.1(76)e-3"
218
219    # val_place == err_place-2
220    assert value_str(12356700,7640000000) == "0.0(76)e9"
221    assert value_str(1235670,764000000) == "0.00(76)e9"
222    assert value_str(123567,76400000) == "0.000(76)e9"
223    assert value_str(12356,7640000) == "0.0(76)e6"
224    assert value_str(1235,764000) == "0.00(76)e6"
225    assert value_str(123,76400) == "0.000(76)e6"
226    assert value_str(12,7640) == "0.0(76)e3"
227    assert value_str(1,764) == "0.00(76)e3"
228    assert value_str(0.1,76.4) == "0.000(76)e3"
229    assert value_str(0.01,7.64) == "0.0(76)"
230    assert value_str(0.001,0.764) == "0.00(76)"
231    assert value_str(0.0001,0.0764) == "0.000(76)"
232    assert value_str(0.00001,0.00764) == "0.0(76)e-3"
233
234    # val_place == err_place-3
235    assert value_str(12356700,76400000000) == "0.000(76)e12"
236    assert value_str(1235670,7640000000) == "0.0(76)e9"
237    assert value_str(123567,764000000) == "0.00(76)e9"
238    assert value_str(12356,76400000) == "0.000(76)e9"
239    assert value_str(1235,7640000) == "0.0(76)e6"
240    assert value_str(123,764000) == "0.00(76)e6"
241    assert value_str(12,76400) == "0.000(76)e6"
242    assert value_str(1,7640) == "0.0(76)e3"
243    assert value_str(0.1,764) == "0.00(76)e3"
244    assert value_str(0.01,76.4) == "0.000(76)e3"
245    assert value_str(0.001,7.64) == "0.0(76)"
246    assert value_str(0.0001,0.764) == "0.00(76)"
247    assert value_str(0.00001,0.0764) == "0.000(76)"
248    assert value_str(0.000001,0.00764) == "0.0(76)e-3"
249
250    # Zero values
251    assert value_str(0,7640000) == "0.0(76)e6"
252    assert value_str(0, 764000) == "0.00(76)e6"
253    assert value_str(0,  76400) == "0.000(76)e6"
254    assert value_str(0,   7640) == "0.0(76)e3"
255    assert value_str(0,    764) == "0.00(76)e3"
256    assert value_str(0,     76.4) == "0.000(76)e3"
257    assert value_str(0,      7.64) == "0.0(76)"
258    assert value_str(0,      0.764) == "0.00(76)"
259    assert value_str(0,      0.0764) == "0.000(76)"
260    assert value_str(0,      0.00764) == "0.0(76)e-3"
261    assert value_str(0,      0.000764) == "0.00(76)e-3"
262    assert value_str(0,      0.0000764) == "0.000(76)e-3"
263
264    # negative values
265    assert value_str(-1235670,765000) == "-1.24(77)e6"
266    assert value_str(-1.23567,.766) == "-1.24(77)"
267    assert value_str(-.00000123567,.0000000766) == "-1.236(77)e-6"
268    assert value_str(-12356.7,7.64) == "-12356.7(76)"
269    assert value_str(-123.567,764) == "-0.12(76)e3"
270    assert value_str(-1235.67,76400) == "-0.001(76)e6"
271    assert value_str(-.000123567,.00764) == "-0.1(76)e-3"
272    assert value_str(-12356,7640000) == "-0.0(76)e6"
273    assert value_str(-12,76400) == "-0.000(76)e6"
274    assert value_str(-0.0001,0.764) == "-0.00(76)"
275
276    # non-finite values
277    assert value_str(-numpy.inf,None) == "-inf"
278    assert value_str(numpy.inf,None) == "inf"
279    assert value_str(numpy.NaN,None) == "NaN"
280   
281    # bad or missing uncertainty
282    assert value_str(-1.23567,numpy.NaN) == "-1.23567"
283    assert value_str(-1.23567,-numpy.inf) == "-1.23567"
284    assert value_str(-1.23567,-0.1) == "-1.23567"
285    assert value_str(-1.23567,0) == "-1.23567"
286    assert value_str(-1.23567,None) == "-1.23567"
287    assert value_str(-1.23567,numpy.inf) == "-1.2(inf)"
288
289def test_pm():
290    # Oops... renamed function after writing tests
291    value_str = format_uncertainty_pm
292
293    # val_place > err_place
294    assert value_str(1235670,766000) == "1.24e6 +/- 0.77e6"
295    assert value_str(123567., 76600) == "124e3 +/- 77e3"
296    assert value_str(12356.7,  7660) == "12.4e3 +/- 7.7e3"
297    assert value_str(1235.67,   766) == "1.24e3 +/- 0.77e3"
298    assert value_str(123.567,    76.6) == "124 +/- 77"
299    assert value_str(12.3567,     7.66) == "12.4 +/- 7.7"
300    assert value_str(1.23567,      .766) == "1.24 +/- 0.77"
301    assert value_str(.123567,      .0766) == "0.124 +/- 0.077"
302    assert value_str(.0123567,     .00766) == "0.0124 +/- 0.0077"
303    assert value_str(.00123567,    .000766) == "0.00124 +/- 0.00077"
304    assert value_str(.000123567,   .0000766) == "124e-6 +/- 77e-6"
305    assert value_str(.0000123567,  .00000766) == "12.4e-6 +/- 7.7e-6"
306    assert value_str(.00000123567, .000000766) == "1.24e-6 +/- 0.77e-6"
307    assert value_str(.000000123567,.0000000766) == "124e-9 +/- 77e-9"
308    assert value_str(.00000123567, .0000000766) == "1.236e-6 +/- 0.077e-6"
309    assert value_str(.0000123567,  .0000000766) == "12.357e-6 +/- 0.077e-6"
310    assert value_str(.000123567,   .0000000766) == "123.567e-6 +/- 0.077e-6"
311    assert value_str(.00123567,    .000000766) == "0.00123567 +/- 0.00000077"
312    assert value_str(.0123567,     .00000766) == "0.0123567 +/- 0.0000077"
313    assert value_str(.123567,      .0000766) == "0.123567 +/- 0.000077"
314    assert value_str(1.23567,      .000766) == "1.23567 +/- 0.00077"
315    assert value_str(12.3567,      .00766) == "12.3567 +/- 0.0077"
316    assert value_str(123.567,      .0764) == "123.567 +/- 0.076"
317    assert value_str(1235.67,      .764) == "1235.67 +/- 0.76"
318    assert value_str(12356.7,     7.64) == "12356.7 +/- 7.6"
319    assert value_str(123567,     76.4) == "123567 +/- 76"
320    assert value_str(1235670,   764) == "1.23567e6 +/- 0.00076e6"
321    assert value_str(12356700,  764) == "12.35670e6 +/- 0.00076e6"
322    assert value_str(123567000, 764) == "123.56700e6 +/- 0.00076e6"
323    assert value_str(123567000,7640) == "123.5670e6 +/- 0.0076e6"
324    assert value_str(1235670000,76400) == "1.235670e9 +/- 0.000076e9"
325
326    # val_place == err_place
327    assert value_str(123567,764000) == "0.12e6 +/- 0.76e6"
328    assert value_str(12356.7,76400) == "12e3 +/- 76e3"
329    assert value_str(1235.67,7640) == "1.2e3 +/- 7.6e3"
330    assert value_str(123.567,764) == "0.12e3 +/- 0.76e3"
331    assert value_str(12.3567,76.4) == "12 +/- 76"
332    assert value_str(1.23567,7.64) == "1.2 +/- 7.6"
333    assert value_str(.123567,.764) == "0.12 +/- 0.76"
334    assert value_str(.0123567,.0764) == "12e-3 +/- 76e-3"
335    assert value_str(.00123567,.00764) == "1.2e-3 +/- 7.6e-3"
336    assert value_str(.000123567,.000764) == "0.12e-3 +/- 0.76e-3"
337
338    # val_place == err_place-1
339    assert value_str(123567,7640000) == "0.1e6 +/- 7.6e6"
340    assert value_str(12356.7,764000) == "0.01e6 +/- 0.76e6"
341    assert value_str(1235.67,76400) == "0.001e6 +/- 0.076e6"
342    assert value_str(123.567,7640) == "0.1e3 +/- 7.6e3"
343    assert value_str(12.3567,764) == "0.01e3 +/- 0.76e3"
344    assert value_str(1.23567,76.4) == "0.001e3 +/- 0.076e3"
345    assert value_str(.123567,7.64) == "0.1 +/- 7.6"
346    assert value_str(.0123567,.764) == "0.01 +/- 0.76"
347    assert value_str(.00123567,.0764) == "0.001 +/- 0.076"
348    assert value_str(.000123567,.00764) == "0.1e-3 +/- 7.6e-3"
349
350    # val_place == err_place-2
351    assert value_str(12356700,7640000000) == "0.0e9 +/- 7.6e9"
352    assert value_str(1235670,764000000) == "0.00e9 +/- 0.76e9"
353    assert value_str(123567,76400000) == "0.000e9 +/- 0.076e9"
354    assert value_str(12356,7640000) == "0.0e6 +/- 7.6e6"
355    assert value_str(1235,764000) == "0.00e6 +/- 0.76e6"
356    assert value_str(123,76400) == "0.000e6 +/- 0.076e6"
357    assert value_str(12,7640) == "0.0e3 +/- 7.6e3"
358    assert value_str(1,764) == "0.00e3 +/- 0.76e3"
359    assert value_str(0.1,76.4) == "0.000e3 +/- 0.076e3"
360    assert value_str(0.01,7.64) == "0.0 +/- 7.6"
361    assert value_str(0.001,0.764) == "0.00 +/- 0.76"
362    assert value_str(0.0001,0.0764) == "0.000 +/- 0.076"
363    assert value_str(0.00001,0.00764) == "0.0e-3 +/- 7.6e-3"
364
365    # val_place == err_place-3
366    assert value_str(12356700,76400000000) == "0.000e12 +/- 0.076e12"
367    assert value_str(1235670,7640000000) == "0.0e9 +/- 7.6e9"
368    assert value_str(123567,764000000) == "0.00e9 +/- 0.76e9"
369    assert value_str(12356,76400000) == "0.000e9 +/- 0.076e9"
370    assert value_str(1235,7640000) == "0.0e6 +/- 7.6e6"
371    assert value_str(123,764000) == "0.00e6 +/- 0.76e6"
372    assert value_str(12,76400) == "0.000e6 +/- 0.076e6"
373    assert value_str(1,7640) == "0.0e3 +/- 7.6e3"
374    assert value_str(0.1,764) == "0.00e3 +/- 0.76e3"
375    assert value_str(0.01,76.4) == "0.000e3 +/- 0.076e3"
376    assert value_str(0.001,7.64) == "0.0 +/- 7.6"
377    assert value_str(0.0001,0.764) == "0.00 +/- 0.76"
378    assert value_str(0.00001,0.0764) == "0.000 +/- 0.076"
379    assert value_str(0.000001,0.00764) == "0.0e-3 +/- 7.6e-3"
380
381    # Zero values
382    assert value_str(0,7640000) == "0.0e6 +/- 7.6e6"
383    assert value_str(0, 764000) == "0.00e6 +/- 0.76e6"
384    assert value_str(0,  76400) == "0.000e6 +/- 0.076e6"
385    assert value_str(0,   7640) == "0.0e3 +/- 7.6e3"
386    assert value_str(0,    764) == "0.00e3 +/- 0.76e3"
387    assert value_str(0,     76.4) == "0.000e3 +/- 0.076e3"
388    assert value_str(0,      7.64) == "0.0 +/- 7.6"
389    assert value_str(0,      0.764) == "0.00 +/- 0.76"
390    assert value_str(0,      0.0764) == "0.000 +/- 0.076"
391    assert value_str(0,      0.00764) == "0.0e-3 +/- 7.6e-3"
392    assert value_str(0,      0.000764) == "0.00e-3 +/- 0.76e-3"
393    assert value_str(0,      0.0000764) == "0.000e-3 +/- 0.076e-3"
394
395    # negative values
396    assert value_str(-1235670,766000) == "-1.24e6 +/- 0.77e6"
397    assert value_str(-1.23567,.766) == "-1.24 +/- 0.77"
398    assert value_str(-.00000123567,.0000000766) == "-1.236e-6 +/- 0.077e-6"
399    assert value_str(-12356.7,7.64) == "-12356.7 +/- 7.6"
400    assert value_str(-123.567,764) == "-0.12e3 +/- 0.76e3"
401    assert value_str(-1235.67,76400) == "-0.001e6 +/- 0.076e6"
402    assert value_str(-.000123567,.00764) == "-0.1e-3 +/- 7.6e-3"
403    assert value_str(-12356,7640000) == "-0.0e6 +/- 7.6e6"
404    assert value_str(-12,76400) == "-0.000e6 +/- 0.076e6"
405    assert value_str(-0.0001,0.764) == "-0.00 +/- 0.76"
406
407    # non-finite values
408    assert value_str(-numpy.inf,None) == "-inf"
409    assert value_str(numpy.inf,None) == "inf"
410    assert value_str(numpy.NaN,None) == "NaN"
411   
412    # bad or missing uncertainty
413    assert value_str(-1.23567,numpy.NaN) == "-1.23567"
414    assert value_str(-1.23567,-numpy.inf) == "-1.23567"
415    assert value_str(-1.23567,-0.1) == "-1.23567"
416    assert value_str(-1.23567,0) == "-1.23567"
417    assert value_str(-1.23567,None) == "-1.23567"
418    assert value_str(-1.23567,numpy.inf) == "-1.2 +/- inf"
419
420def test_default():
421    # Check that the default is the compact format
422    assert format_uncertainty(-1.23567,0.766) == "-1.24(77)"
423
424def main():
425    """
426    Run all tests.
427
428    This is equivalent to "nosetests --with-doctest"
429    """
430    test_compact()
431    test_pm()
432    test_default()
433   
434    import doctest
435    doctest.testmod()
436
437if __name__ == "__main__": main()
Note: See TracBrowser for help on using the repository browser.