3

I read a lot of discussion about this on SE, but still can't find the right one.

I want to plot some numbers, of various lengths, with the same number of digits.

For example I have: 12.345678, 1.2345678. Now, since I have to plot them with their error, I want that each one has a different format, in order that they are significant.

So, I want to plot them with a variable number of decimals. In my case, it makes no sense to plot 23.45678+/-1.23456 but better is 23.4+/-1.2. On the other hand, I need that 1.234567+/-0.034567 becomes 1.23+/-0.03.

So, let's say, I want to plot all the numbers with a fixed width, could be 3 digits in total plus the comma. I should use something like '%1.1f' %num, but I can't find the right way. How can I do that?

Schorsch
  • 7,761
  • 6
  • 39
  • 65
Py-ser
  • 1,860
  • 9
  • 32
  • 58
  • @Schorsch, not exactly. The problem is that in my case, the number of decimals is variable, according to the integer part and the error on the value. I would need to fix only the total number of digits not the decimal ones. – Py-ser Aug 11 '14 at 02:06
  • @Schorsch, in your example I should know the number of decimals in advance, which is not the case, and also I have to add more variables (the `DECIMALS` list) in the code. Instead, I would prefer a "self-consistent" solution, a format that arrange itself according to its characteristics. – Py-ser Aug 22 '14 at 02:12

1 Answers1

3

I recommend defining a class that interprets a string formatter to give what you want.
Inside that class, you determine the length of the integer portion of your float and use that to define the appropriate string format.
In a nutshell, the class creates a formatter like '{:4.1f}' if your input is 12.345 (because you have two digits before the decimal separator) and {:4.2f} if your input it 1.2345 (because you have only one digit before the decimal separator). The total number of digits (4in this example) is provided as an input.
The new formatter is: {:nQ} where n is the total number of digits (so in the above example, you'd specify {:4Q}to get the output you want.
Here's the code:

import math

class IntegerBasedFloat(float):
    def __format__(self, spec):
        value = float(self)

        # apply the following only, if the specifier ends in Q
        # otherwise, you maintain the original float format
        if spec.endswith('Q'):
            # split the provided float into the decimal 
            # and integer portion (for this math is required):
            DEC, INT = math.modf(value)

            # determine the length of the integer portion:
            LEN = len(str(abs(int(INT))))

            # calculate the number of available decimals 
            # based on the overall length
            # the -1 is required because the separator 
            # requires one digit
            DECIMALS = int(spec[-2]) - LEN - 1

            if DECIMALS < 0:
                print 'Number too large for specified format'
            else:
                # create the corresponding float formatter
                # that can be evaluated as usual:
                spec = spec[-2] + '.' + str(DECIMALS) + 'f'

        return format(value, spec)

DATA = [12.345, 2.3456, 345.6789]

print '{:4Q}'.format(IntegerBasedFloat(DATA[0]))
print '{:4Q}'.format(IntegerBasedFloat(DATA[1]))
print '{:4Q}'.format(IntegerBasedFloat(DATA[2]))
print 'This is a "custom" float: {:5Q} and a "regular" float: {:5.3f}'.format(IntegerBasedFloat(12.3456),12.3456)

The output should be:

12.3
2.35
 346
This is a "custom" float: 12.35 and a "regular" float: 12.346

This answer is inspired by:
- splitting a number into the integer and decimal parts in python
- Add custom conversion types for string formatting

Community
  • 1
  • 1
Schorsch
  • 7,761
  • 6
  • 39
  • 65
  • 1
    @downvoter Would you care to explain why this answer is not helpful, how it does not answer the question or what may be wrong with it? I'm especially interested if this proposal has unwanted negative side-effects. – Schorsch Aug 28 '14 at 11:56