source: sasview/park-1.2.1/park/formatnum.py @ 3540156

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 3540156 was 3570545, checked in by Mathieu Doucet <doucetm@…>, 13 years ago

Adding park Part 2

  • Property mode set to 100644
File size: 12.7 KB
Line 
1# This program is public domain
2# Author Paul Kienzle
3"""
4Format numbers nicely for printing.
5
6Usage::
7
8   >> from danse.common.util.formatnum import *
9   >> v,dv = 757.2356,0.01032
10   >> print format_uncertainty_pm(v,dv)
11   757.235 +/- 0.010
12   >> format_uncertainty_compact(v,dv)
13   757.235(10)
14   >> format_uncertainty(v,dv)
15   757.235(10)
16
17Set format_uncertainty.compact to False to use the +/-
18format by default, otherwise leave it at True for compact
19value(##) format.
20"""
21from __future__ import division
22
23import math
24import numpy
25__all__ = ['format_uncertainty', 'format_uncertainty_pm', 
26           'format_uncertainty_compact']
27
28# These routines need work for +/- formatting::
29# - pm formats are not rigorously tested
30# - pm formats do not try to align the scale to multiples of 1000
31# - pm formats do not try to align the value scale to uncertainty scale
32
33# Coordinating scales across a set of numbers is not supported.  For easy
34# comparison a set of numbers should be shown in the same scale.  One could
35# force this from the outside by adding scale parameter (either 10**n, n, or
36# a string representing the desired SI prefix) and having a separate routine
37# which computes the scale given a set of values.
38
39# Coordinating scales with units offers its own problems.  Again, the user
40# may want to force particular units.  This can be done by outside of the
41# formatting routines by scaling the numbers to the appropriate units then
42# forcing them to print with scale 10**0.  If this is a common operation,
43# however, it may want to happen inside.
44
45# The value e<n> is currently formatted into the number.  Alternatively this
46# scale factor could be returned so that the user can choose the appriate
47# SI prefix when printing the units.  This gets tricky when talking about
48# composite units such as 2.3e-3 m**2 -> 2300 mm**2, and with volumes
49# such as 1 g/cm**3 -> 1 kg/L.
50
51def format_uncertainty_pm(value, uncertainty=None):
52    """
53    Given *value* v and *uncertainty* dv, return a string v +/- dv.
54   
55    The returned string uses only the number of digits warranted by
56    the uncertainty in the measurement.
57
58    If the uncertainty is 0 or not otherwise provided, the simple
59    %g floating point format option is used.
60
61    Infinite and indefinite numbers are represented as inf and NaN.
62    """
63    return _format_uncertainty(value, uncertainty, compact=False)
64
65def format_uncertainty_compact(value, uncertainty=None):
66    """
67    Given *value* v and *uncertainty* dv, return the compact
68    representation v(##), where ## are the first two digits of
69    the uncertainty.
70   
71    The returned string uses only the number of digits warranted by
72    the uncertainty in the measurement.
73
74    If the uncertainty is 0 or not otherwise provided, the simple
75    %g floating point format option is used.
76
77    Infinite and indefinite numbers are represented as inf and NaN.
78    """
79    return _format_uncertainty(value, uncertainty, compact=True)
80
81class FormatUncertainty:
82    """
83    Given *value* and *uncertainty*, return a concise string representation.
84
85    This will either by the +/- form of :func:`format_uncertainty_pm` or
86    the compact form of :func:`format_uncertainty_compact` depending on
87    whether *compact* is specified or whether *format_uncertainty.compact*
88    is True or False.
89    """
90    compact = True
91    def __call__(self, value, uncertainty):
92        return _format_uncertainty(value, uncertainty, self.compact)
93format_uncertainty = FormatUncertainty()
94
95def _format_uncertainty(value,uncertainty,compact):
96    """
97    Implementation of both the compact and the +/- formats.
98    """
99    if numpy.isinf(value):
100        return "inf" if value > 0 else "-inf"
101   
102    if numpy.isnan(value):
103        return "NaN"
104
105    # Uncertainty check must come after indefinite check since the %g
106    # format string doesn't handle indefinite numbers consistently
107    # across platforms.
108    if uncertainty == None or uncertainty <= 0 or numpy.isnan(uncertainty):
109        return "%g"%value
110    if numpy.isinf(uncertainty):
111        if compact:
112            return "%g(inf)"%value
113        else:
114            return "%g +/- inf"%value
115   
116    # Process sign
117    sign = "-" if value < 0 else ""
118    value = abs(value)
119   
120    # Determine the number of digits in the value and the error
121    # Note that uncertainty <= 0 is handled above
122    err_place = int(math.floor(math.log10(uncertainty)))
123    if value == 0:
124        val_place = err_place-1
125    else:
126        val_place = int(math.floor(math.log10(value)))
127
128    # If pm, return a simple v +/- dv
129    if not compact:
130        scale = 10**(err_place-1)
131        val_digits = val_place-err_place+2
132        return "%s%.*g +/- %.2g"%(sign,val_digits,value,uncertainty)
133
134    # The remainder is for the v(dv) case
135    err_str = "(%2d)"%int(uncertainty/10.**(err_place-1)+0.5)
136    if err_place > val_place:
137        # Degenerate case: error bigger than value
138        # The mantissa is 0.#(##)e#, 0.0#(##)e# or 0.00#(##)e#
139        if err_place - val_place > 2: value = 0
140        val_place = int(math.floor((err_place+2)/3.))*3
141        digits_after_decimal = val_place - err_place + 1
142        val_str = "%.*f%s"%(digits_after_decimal,value/10.**val_place,err_str)
143        if val_place != 0: val_str += "e%d"%val_place
144    elif err_place == val_place:
145        # Degenerate case: error and value the same order of magnitude
146        # The value is ##(##)e#, #.#(##)e# or 0.##(##)e#
147        val_place = int(math.floor((err_place+1)/3.))*3
148        digits_after_decimal = val_place - err_place + 1
149        val_str = "%.*f%s"%(digits_after_decimal,value/10.**val_place,err_str)
150        if val_place != 0: val_str += "e%d"%val_place
151    elif err_place <= 1 and val_place >= -3:
152        # Normal case: nice numbers and errors
153        # The value is ###.###(##)
154        digits_after_decimal = abs(err_place-1)
155        val_str = "%.*f%s"%(digits_after_decimal,value,err_str)
156    else:
157        # Extreme cases: zeros before value or after error
158        # The value is ###.###(##)e#, ##.####(##)e# or #.#####(##)e#
159        total_digits = val_place - err_place + 2
160        val_place = int(math.floor(val_place/3.))*3
161        val_str = "%.*g%se%d"%(total_digits,
162                               value/10.**val_place,
163                               err_str,val_place)
164
165    return sign+val_str
166
167
168
169def test():
170    # Oops... renamed function after writing tests
171    value_str = format_uncertainty_compact
172   
173    # val_place > err_place
174    assert value_str(1235670,766000) == "1.24(77)e6"
175    assert value_str(123567.,76600) == "124(77)e3"
176    assert value_str(12356.7,7660) == "12.4(77)e3"
177    assert value_str(1235.67,766) == "1.24(77)e3"
178    assert value_str(123.567,76.6) == "124(77)"
179    assert value_str(12.3567,7.66) == "12.4(77)"
180    assert value_str(1.23567,.766) == "1.24(77)"
181    assert value_str(.123567,.0766) == "0.124(77)"
182    assert value_str(.0123567,.00766) == "0.0124(77)"
183    assert value_str(.00123567,.000766) == "0.00124(77)"
184    assert value_str(.000123567,.0000766) == "124(77)e-6"
185    assert value_str(.0000123567,.00000766) == "12.4(77)e-6"
186    assert value_str(.00000123567,.000000766) == "1.24(77)e-6"
187    assert value_str(.000000123567,.0000000766) == "124(77)e-9"
188    assert value_str(.00000123567,.0000000766) == "1.236(77)e-6"
189    assert value_str(.0000123567,.0000000766) == "12.357(77)e-6"
190    assert value_str(.000123567,.0000000766) == "123.567(77)e-6"
191    assert value_str(.00123567,.000000766) == "0.00123567(77)"
192    assert value_str(.0123567,.00000766) == "0.0123567(77)"
193    assert value_str(.123567,.0000766) == "0.123567(77)"
194    assert value_str(1.23567,.000766) == "1.23567(77)"
195    assert value_str(12.3567,.00766) == "12.3567(77)"
196    assert value_str(123.567,.0764) == "123.567(76)"
197    assert value_str(1235.67,.764) == "1235.67(76)"
198    assert value_str(12356.7,7.64) == "12356.7(76)"
199    assert value_str(123567,76.4) == "123567(76)"
200    assert value_str(1235670,764) == "1.23567(76)e6"
201    assert value_str(12356700,764) == "12.3567(76)e6"
202    assert value_str(123567000,7640) == "123.567(76)e6"
203    assert value_str(1235670000,76400) == "1.23567(76)e9"
204   
205    # val_place == err_place
206    assert value_str(123567,764000) == "0.12(76)e6"
207    assert value_str(12356.7,76400) == "12(76)e3"
208    assert value_str(1235.67,7640) == "1.2(76)e3"
209    assert value_str(123.567,764) == "0.12(76)e3"
210    assert value_str(12.3567,76.4) == "12(76)"
211    assert value_str(1.23567,7.64) == "1.2(76)"
212    assert value_str(.123567,.764) == "0.12(76)"
213    assert value_str(.0123567,.0764) == "12(76)e-3"
214    assert value_str(.00123567,.00764) == "1.2(76)e-3"
215    assert value_str(.000123567,.000764) == "0.12(76)e-3"
216
217    # val_place == err_place-1
218    assert value_str(123567,7640000) == "0.1(76)e6"
219    assert value_str(12356.7,764000) == "0.01(76)e6"
220    assert value_str(1235.67,76400) == "0.001(76)e6"
221    assert value_str(123.567,7640) == "0.1(76)e3"
222    assert value_str(12.3567,764) == "0.01(76)e3"
223    assert value_str(1.23567,76.4) == "0.001(76)e3"
224    assert value_str(.123567,7.64) == "0.1(76)"
225    assert value_str(.0123567,.764) == "0.01(76)"
226    assert value_str(.00123567,.0764) == "0.001(76)"
227    assert value_str(.000123567,.00764) == "0.1(76)e-3"
228
229    # val_place == err_place-2
230    assert value_str(12356700,7640000000) == "0.0(76)e9"
231    assert value_str(1235670,764000000) == "0.00(76)e9"
232    assert value_str(123567,76400000) == "0.000(76)e9"
233    assert value_str(12356,7640000) == "0.0(76)e6"
234    assert value_str(1235,764000) == "0.00(76)e6"
235    assert value_str(123,76400) == "0.000(76)e6"
236    assert value_str(12,7640) == "0.0(76)e3"
237    assert value_str(1,764) == "0.00(76)e3"
238    assert value_str(0.1,76.4) == "0.000(76)e3"
239    assert value_str(0.01,7.64) == "0.0(76)"
240    assert value_str(0.001,0.764) == "0.00(76)"
241    assert value_str(0.0001,0.0764) == "0.000(76)"
242    assert value_str(0.00001,0.00764) == "0.0(76)e-3"
243
244    # val_place == err_place-3
245    assert value_str(12356700,76400000000) == "0.000(76)e12"
246    assert value_str(1235670,7640000000) == "0.0(76)e9"
247    assert value_str(123567,764000000) == "0.00(76)e9"
248    assert value_str(12356,76400000) == "0.000(76)e9"
249    assert value_str(1235,7640000) == "0.0(76)e6"
250    assert value_str(123,764000) == "0.00(76)e6"
251    assert value_str(12,76400) == "0.000(76)e6"
252    assert value_str(1,7640) == "0.0(76)e3"
253    assert value_str(0.1,764) == "0.00(76)e3"
254    assert value_str(0.01,76.4) == "0.000(76)e3"
255    assert value_str(0.001,7.64) == "0.0(76)"
256    assert value_str(0.0001,0.764) == "0.00(76)"
257    assert value_str(0.00001,0.0764) == "0.000(76)"
258    assert value_str(0.000001,0.00764) == "0.0(76)e-3"
259
260    # negative values
261    assert value_str(-1235670,765000) == "-1.24(77)e6"
262    assert value_str(-1.23567,.765) == "-1.24(77)"
263    assert value_str(-.00000123567,.0000000765) == "-1.236(77)e-6"
264    assert value_str(-12356.7,7.64) == "-12356.7(76)"
265    assert value_str(-123.567,764) == "-0.12(76)e3"
266    assert value_str(-1235.67,76400) == "-0.001(76)e6"
267    assert value_str(-.000123567,.00764) == "-0.1(76)e-3"
268    assert value_str(-12356,7640000) == "-0.0(76)e6"
269    assert value_str(-12,76400) == "-0.000(76)e6"
270    assert value_str(-0.0001,0.764) == "-0.00(76)"
271
272    # zero values
273    assert value_str(0,0) == "0"
274    assert value_str(-1.23567,0) == "-1.23567"
275    assert value_str(0,765000) == "0.00(77)e6"
276    assert value_str(0,.765) == "0.00(77)"
277    assert value_str(0,.000000765) == "0.00(77)e-6"
278    assert value_str(0,76400) == "0.000(76)e6"
279    assert value_str(0,7640) == "0.0(76)e3"
280
281    # marked values
282    assert value_str(-numpy.inf,None) == "-inf"
283    assert value_str(numpy.inf,None) == "inf"
284    assert value_str(numpy.NaN,None) == "NaN"
285   
286    # plus/minus form
287    assert format_uncertainty_pm(-1.23567,0.765) == "-1.24 +/- 0.77"
288    assert format_uncertainty_compact(-1.23567,0.765) == "-1.24(77)"
289    assert format_uncertainty_pm(752.3567,0.01) == "752.357 +/- 0.01"
290    assert format_uncertainty(-1.23567,0.765) == "-1.24(77)"
291
292    # bad uncertainty
293    assert format_uncertainty_pm(-1.23567,numpy.NaN) == "-1.23567"
294    assert format_uncertainty_pm(-1.23567,-numpy.inf) == "-1.23567"
295    assert format_uncertainty_pm(-1.23567,-0.1) == "-1.23567"
296    assert format_uncertainty_compact(-1.23567,numpy.NaN) == "-1.23567"
297    assert format_uncertainty_compact(-1.23567,-numpy.inf) == "-1.23567"
298    assert format_uncertainty_compact(-1.23567,-0.1) == "-1.23567"
299
300    # no uncertainty
301    assert format_uncertainty_pm(-1.23567,0) == "-1.23567"
302    assert format_uncertainty_compact(-1.23567,0) == "-1.23567"
303    assert format_uncertainty_pm(-1.23567,None) == "-1.23567"
304    assert format_uncertainty_compact(-1.23567,None) == "-1.23567"
305
306    # inf uncertainty
307    assert format_uncertainty_pm(-1.23567,numpy.inf) == "-1.23567 +/- inf"
308    assert format_uncertainty_compact(-1.23567,numpy.inf) == "-1.23567(inf)"
309
310
311if __name__ == "__main__": test()
312   
Note: See TracBrowser for help on using the repository browser.