Zahlen formatieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Hi!

Ich hab mal meine beiden Beiträge (4 und 6) benchmarktauglich gemacht ;)

Code: Alles auswählen

def format_number_4(number, comma=".", thousand=",", format="%.2f"):
    number = format % number
    minus = 0
    if number < 0:
        minus = 1
        number = abs(number)
    n = str(number).split(comma)
    n[0] = list(str(n[0]))[::-1]
    n[0] = comma.join(
            ["".join(n[0][i:i+3][::-1]) for i in
             range(0,len(n[0]),3)][::-1])
    if minus:
        return "%s%s" % ('-', thousand.join(n))
    else: return thousand.join(n)

def format_number_6(number, comma=".", thousand=",", format="%.2f"):
    """
    by mawe
    http://www.python-forum.de/viewtopic.php?p=20850#20850
    """
    number = format % number
    minus = 0
    if number < 0:
        minus = 1
        number = abs(number)
    n = str(number).split(comma)
    x = len(n[0]) % 3
    n[0] = "%s%s" % ("0" * (3-x), n[0])
    n[0] = comma.join([n[0][i:i+3]
              for i in range(0,len(n[0]),3)]).lstrip("0%s" % comma)
    if minus: return "%s%s" % ('-', thousand.join(n))
    else: return thousand.join(n)
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab es aktualisiert. Nun ist der Test immer OK.

Hier die neuen Werte:
0 0.953s
0b 0.687s
1 1.235s
2 1.500s
3 1.406s
4 1.281s
5 0.766s
6 1.015s
format_numer_locale 1.828s

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Hi!

Harry's Version kann man noch etwas beschleunigen (eigentlich jede Version), wenn man alles < 1000 gleich zurückgibt. Ich versteh auch die Zeile

Code: Alles auswählen

s = s and decChar + s or s 
nicht wirklich. Kann man nicht gleich

Code: Alles auswählen

s = decChar + (format % (abs(number) % 1))[2:]
schreiben?
Naja, hier mal das ganze:

Code: Alles auswählen

def format_number_5(number, format = "%.2f", decChar = ",", groupChar = ".", groupRange = 3):
    """
    by HarryH
    http://www.python-forum.de/viewtopic.php?p=20888#20888

    Convert to required format
    number =      number to convert
    format =      python string formatting (%),but only for the decimals
    decChar =     decimal char for the converted format
    groupChar =   group char for the converted format
    groupRange =  group range for the converted format

    For example:
    __formatter(1234567890.987, "%.2f", ",", ".", 3)
    ==> 1.234.567.890,99

    """
    if abs(number) < 1000:
        return (format % (number)).replace(".", decChar)
    else:
        base = int(abs(number))
        s = decChar + (format % (abs(number) % 1))[2:]
        while base:
            s = groupChar + str(base)[-groupRange:] + s
            base = int(base / 10**groupRange)
        return number < 0 and "-" + s[1:] or s[1:]
Gruß, mawe
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hi mawe,

Danke für deine Anregungen!

Erst alles > 1000 zurückzugeben ist bestimmt eine Verbesserung. Ich würde es aber, passend zu meinem Code, so machen:

Code: Alles auswählen

if abs(number) < 10**groupRange:
Es ist nämlich wichtig die Gruppierungsgröße mit einzubeziehen.

Der Sinn folgender der Zeile

Code: Alles auswählen

s = s and decChar + s or s
läßt sich leicht herausfinden wenn man 'format="%i"' an die Funktion übergibt. Ohne diese Zeile würde das Trennzeichen nämlich unnötigerweise hinzugefügt werden.
Gruß, Harry
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab's jetzt in dieser Version getestet:

Code: Alles auswählen

def format_number_5(number, format = "%.2f", decChar = ",", groupChar = ".", groupRange = 3):
    """
    by HarryH & mawe
    http://www.python-forum.de/viewtopic.php?p=20943#20943

    Convert to required format
    number =      number to convert
    format =      python string formatting (%),but only for the decimals
    decChar =     decimal char for the converted format
    groupChar =   group char for the converted format
    groupRange =  group range for the converted format

    For example:
    __formatter(1234567890.987, "%.2f", ",", ".", 3)
    ==> 1.234.567.890,99

    """
    if abs(number) < 10**groupRange:
        return (format % (number)).replace(".", decChar)
    else:
        base = int(abs(number))
        s = (format % (abs(number) % 1))[2:]
        s = s and decChar + s or s
        while base:
            s = groupChar + str(base)[-groupRange:] + s
            base = int(base / 10**groupRange)
        return number < 0 and "-" + s[1:] or s[1:]

Code: Alles auswählen

0 0.953s
0b 0.703s
1 1.266s
2 1.500s
3 1.422s
4 1.297s
5 0.687s
6 1.032s
format_numer_locale 1.843s
Wobei ich ja denke, das die Angabe von groupRange sinnlos ist, denn welche andere zahl außer drei wäre sinnvoll???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
cime
User
Beiträge: 152
Registriert: Dienstag 24. Mai 2005, 15:49

mawe hat geschrieben:Hi!

Harry's Version kann man noch etwas beschleunigen (eigentlich jede Version), wenn man alles < 1000 gleich zurückgibt. Ich versteh auch die Zeile
gute idee, hab meine gleich ma umgeändert ...

Code: Alles auswählen

import string

def format_number_0(number,format="%0.2f",comma=",",thousand=".",grouplength=3):
    if abs(number) < 10**grouplength: 
        return (format % (number)).replace(".", comma) 
    if number<0:
         vorzeichen="-"
         number=-number
    else:
        vorzeichen=""
    if format.endswith("f"):
        hinter_komma=string.split(format % number,".")[1]
        full_string=comma+hinter_komma
    elif format.endswith("i"):
        full_string=""
    number=int(number)
    x=1
    while (10**grouplength)**x<=number:
        full_string=thousand+str(number%((10**grouplength)**x))[0:grouplength]+full_string
        x+=1
    full_string=vorzeichen+str(number/((10**grouplength)**(x-1)))+full_string
    return full_string

#----------------------------------------------------------------------

def format_number_0b(number,format="%0.2f",comma=",",thousand=".",grouplength=3):
    if abs(number) < 10**grouplength: 
        return (format % (number)).replace(".", comma) 
    if number<0:
         vorzeichen="-"
         number=-number
    else:
        vorzeichen=""

    vor_komma,hinter_komma=string.split(format % number,".")
    add=0
    for i in range(grouplength,len(vor_komma),grouplength):
        vor_komma=vor_komma[0:-(i+add)]+thousand+vor_komma[-(i+add):]
    return vor_komma+comma+hinter_komma

wär übrigens ma dafür, dass wir uns auf eine Möglichkeit einigen und die optimieren bzw. uns auf mehrere einigen und diese verbinden und optimieren ...
cime
User
Beiträge: 152
Registriert: Dienstag 24. Mai 2005, 15:49

cime hat geschrieben:wär übrigens ma dafür, dass wir uns auf eine Möglichkeit einigen und die optimieren bzw. uns auf mehrere einigen und diese verbinden und optimieren ...
um ma nen anfang damit zu machen hab ich gleich ma unsere zwei schnellsten getestet:

Code: Alles auswählen

import string


#----------------------------------------------------------------------

def format_number_0b(number,format="%0.2f",comma=",",thousand=".",grouplength=3):
    if abs(number) < 10**grouplength: 
        return (format % (number)).replace(".", comma) 
    if number<0:
         vorzeichen="-"
         number=-number
    else:
        vorzeichen=""

    vor_komma,hinter_komma=string.split(format % number,".")
    add=0
    for i in range(grouplength,len(vor_komma),grouplength):
        vor_komma=vor_komma[0:-(i+add)]+thousand+vor_komma[-(i+add):]
    return vor_komma+comma+hinter_komma


#----------------------------------------------------------------------


def format_number_5(number, format = "%.2f", decChar = ",", groupChar = ".", groupRange = 3): 
    """ 
    by HarryH & mawe 
    http://www.python-forum.de/viewtopic.php?p=20943#20943 

    Convert to required format 
    number =      number to convert 
    format =      python string formatting (%),but only for the decimals 
    decChar =     decimal char for the converted format 
    groupChar =   group char for the converted format 
    groupRange =  group range for the converted format 

    For example: 
    __formatter(1234567890.987, "%.2f", ",", ".", 3) 
    ==> 1.234.567.890,99 

    """ 
    if abs(number) < 10**groupRange: 
        return (format % (number)).replace(".", decChar) 
    else: 
        base = int(abs(number)) 
        s = (format % (abs(number) % 1))[2:] 
        s = s and decChar + s or s 
        while base: 
            s = groupChar + str(base)[-groupRange:] + s 
            base = int(base / 10**groupRange) 
        return number < 0 and "-" + s[1:] or s[1:] 

if __name__ == "__main__": 
    # Testen welche Funktion schneller ist 

    import time 

    zahlen = ( 
      66, 
      1, 
      2, 
      3.4, 
      -5.678, 
      612345, 
      612345.555, 
      987654321.067 
    )

    anz=100000
    for x in range(2):

        begin = time.time()

        for i in range(anz): 
          for zahl in zahlen: 
             x = format_number_0b(zahl)

        print "0b:", time.time() - begin 
        print 

        begin = time.time() 

        for i in range(anz): 
          for zahl in zahlen: 
             x = format_number_5(zahl) 
              
        print "5:", time.time() - begin 
        print 
Ergebnis:

Code: Alles auswählen

0b: 9.78099989891

5: 10.390999794

0b: 9.81299996376

5: 10.375
EDIT by mawe: Python tags korrigiert
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hi,

cime's Version müßte nochmals überarbeitet werden. Sie funktioniert z.B. nicht bei folgenden Zahlen: -612345.555, 9876543210.067 und ebenfalls nicht bei natürlichen Zahlen >= 10**grouplength.
Gruß, Harry
cime
User
Beiträge: 152
Registriert: Dienstag 24. Mai 2005, 15:49

thx, harry: prob gelöst:

hier auch gleich nochma ein geschwindigkeitsvergleich:

Code: Alles auswählen

# -*- coding: cp1252 -*-
#format_number.py
import string


#----------------------------------------------------------------------

def format_number_0b(number,format="%0.2f",comma=",",thousand=".",grouplength=3):
    if abs(number) < 10**grouplength: 
        return (format % (number)).replace(".", comma)
    if format[-1]=="f":
        vor_komma,hinter_komma=string.split(format % number,".")
    else:
        vor_komma=format % number
        comma=""
        hinter_komma=""
    #Hier
    anz_leer=0
    for i in vor_komma:
        if i==" ":
            anz_leer+=1
        else:
            break
    vor_komma=vor_komma[anz_leer:]
    #bis hier
        
    len_vor_komma=len(vor_komma)
    for i in range(grouplength,len_vor_komma+(len_vor_komma-1)/(grouplength+1)-(number<0),grouplength+1):
        vor_komma=vor_komma[0:-(i)]+thousand+vor_komma[-(i):]
    return anz_leer*" "+vor_komma+comma+hinter_komma


#----------------------------------------------------------------------


def format_number_5(number, format = "%.2f", decChar = ",", groupChar = ".", groupRange = 3): 
    """ 
    by HarryH & mawe 
    http://www.python-forum.de/viewtopic.php?p=20943#20943 

    Convert to required format 
    number =      number to convert 
    format =      python string formatting (%),but only for the decimals 
    decChar =     decimal char for the converted format 
    groupChar =   group char for the converted format 
    groupRange =  group range for the converted format 

    For example: 
    __formatter(1234567890.987, "%.2f", ",", ".", 3) 
    ==> 1.234.567.890,99 

    """ 
    if abs(number) < 10**groupRange: 
        return (format % (number)).replace(".", decChar) 
    else: 
        base = int(abs(number)) 
        s = (format % (abs(number) % 1))[2:]
        s = s and decChar + s or s
        while base:
            s = groupChar + str(base)[-groupRange:] + s 
            base = int(base / 10**groupRange)
        return number < 0 and "-" + s[1:] or s[1:] 

if __name__ == "__main__": 
    # Testen welche Funktion schneller ist 

    import time 

    zahlen = ( 
      66, 
      1, 
      2, 
      3.4, 
      -5.678,
      -123456789.348,
      612345, 
      612345.555, 
      987654321.067,
      8756832640,
      354,
      816523
    )

    formate = (
        "%.2f",
        "%30.3f",
        "%10i"
        )

    groups = (
        2,
        3,
        4,
        5
        )
    
    anz=100000
    for x in range(2):

        begin = time.time()

        for i in range(1000):
            for format in formate: 
              for zahl in zahlen:
                  for group in groups:
                     x = format_number_0b(zahl,format=format,grouplength=group)

        print "0b:", time.time() - begin 
        print 

        begin = time.time() 

        for i in range(1000):
            for format in formate: 
              for zahl in zahlen:
                 for group in groups:
                     x = format_number_5(zahl,format=format,groupRange=group)
              
        print "5:", time.time() - begin 
        print 
Ergebnis:

Code: Alles auswählen

0b: 2.5150001049

5: 2.03099989891

0b: 2.48399996758

5: 2.03100013733


PS: @harry: bei den Formaten "%30.3f","%10i" macht deins probs ...

EDIT by mawe: Smilies deaktiviert (cime, bitte schau Dir Deine Beiträge an bevor Du sie absendest ;) )
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Für alle die es interessiert: PythonWunschliste wurde repariert und funktioniert wieder einwandfrei.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hi cime,
cime hat geschrieben: PS: @harry: bei den Formaten "%30.3f","%10i" macht deins probs ...
Ich weiß. Deswegen habe ich in den docstring zu meiner Funktion geschrieben, das die Formatierung nur für die Dezimalstellen gilt.

Code: Alles auswählen

format =      python string formatting (%),but only for the decimals 
Wer z.B. eine Einrückung vornehmen möchte kann ja die Rückgabe der Funktion zusätzlich mit "%20s" formatieren!
Gruß, Harry
cime
User
Beiträge: 152
Registriert: Dienstag 24. Mai 2005, 15:49

HarryH hat geschrieben:

Code: Alles auswählen

format =      python string formatting (%),but only for the decimals 
Wer z.B. eine Einrückung vornehmen möchte kann ja die Rückgabe der Funktion zusätzlich mit "%20s" formatieren!
wär vielleicht gut, wenn man so was noch einbaut (etwa so:)

Code: Alles auswählen

def format_number_5(number, format = "%.2f", decChar = ",", groupChar = ".", groupRange = 3): 
    """ 
    by HarryH & mawe 
    http://www.python-forum.de/viewtopic.php?p=20943#20943 

    Convert to required format 
    number =      number to convert 
    format =      python string formatting (%),but only for the decimals 
    decChar =     decimal char for the converted format 
    groupChar =   group char for the converted format 
    groupRange =  group range for the converted format 

    For example: 
    __formatter(1234567890.987, "%.2f", ",", ".", 3) 
    ==> 1.234.567.890,99 

    """ 
    if abs(number) < 10**groupRange: 
        return (format % (number)).replace(".", decChar) 
    else: 
        base = int(abs(number)) 
        s = (format % (abs(number) % 1))[2:]
        s = s and decChar + s or s
        while base:
            s = groupChar + str(base)[-groupRange:] + s 
            base = int(base / 10**groupRange)
        if format[1]!="." or format[1]!="0":
            if format.endswith("f"):
                new_format=string.split(format,".")[0]+"s"
            else:
                new_format=format[0:-1]+"s"
            return new_format%(number < 0 and "-" + s[1:] or s[1:])
        else:
            return number < 0 and "-" + s[1:] or s[1:] 
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hallo,

Un hier meine Lösung mit kompletter Formatierung. Ist nur minimal langsamer als ohne.
Funktioniert hervorragend, zumindest nach meiner Feststellung. :D

Code: Alles auswählen

def __formatter(number, format = "%.2f", decChar = ",", groupChar = ".", groupRange = 3, format_divider = ["^%(0?)([-+# ]*\d*)(.*)"]):
    """
    Convert to required format
    number =         number to convert
    format =         python string formatting (%)
    decChar =        decimal char for the converted format
    groupChar =      group char for the converted format
    groupRange =     group range for the converted format
    format_divider = split the format for exact formatting ( => do not change!)

    For example:
    __formatter(1234567890.987, "%020.2f", ",", ".", 3)
    ==> 00001.234.567.890,99

    """
    if abs(number) < 10**groupRange:
        return (format % (number)).replace(".", decChar)
    else:
        # set formatting strings by first calling of this function
        if type(format_divider[0]) == str:
            result = re.findall(format_divider[0], format)[0]
            format_divider[0] = result[0], "%0" + result[1] + "s", "%" + result[2]
        zeros, base_format, tail_format = format_divider[0]
        # formatting the number
        base = int(abs(number))
        tail = (tail_format % (abs(number) % 1))[2:]
        tail = tail and decChar + tail or tail
        s = ""
        while base:
            s = groupChar + str(base)[-groupRange:] + s
            base = int(base / 10**groupRange)
        # return with left filled zeros
        if zeros == "0":
            return number < 0 and base_format % ("-" + (s[1:] + tail).zfill(int(base_format[1:-1])-1)) \
                    or base_format % ((s[1:] + tail).zfill(int(base_format[1:-1])))
        # return without left filled zeros
        else:
            return number < 0 and base_format % ("-" + s[1:] + tail) or base_format % (s[1:] + tail)
Gruß, Harry
cime
User
Beiträge: 152
Registriert: Dienstag 24. Mai 2005, 15:49

Code: Alles auswählen

def format_number_0b(number,format="%0.2f",comma=",",thousand=".",grouplength=3):
    if abs(number) < 10**grouplength: 
        return (format % (number)).replace(".", comma)
    if format[-1]=="f":
        vor_komma,hinter_komma=(format % number).split(".",-1)
    else:
        vor_komma=format % number
        comma=""
        hinter_komma=""
    #Hier
    anz_leer=0
    for i in vor_komma:
        if i==" ":
            anz_leer+=1
        else:
            break
    vor_komma=vor_komma[anz_leer:]
    #bis hier

    len_vor_komma=len(vor_komma)
    for i in range(grouplength,len_vor_komma+(len_vor_komma-1)/(grouplength+1)-(number<0),grouplength+1):
        vor_komma=vor_komma[0:-(i)]+thousand+vor_komma[-(i):]
    return anz_leer*" "+vor_komma+comma+hinter_komma
hab meine noch ein wenig beschleunigt ... *g*

Edit (Leonidas): Smiley im Code.. weg damit.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Bin etwas verwirrt... Deine Funktion geht eigentlich... Doch wenn ich es in einer Klasse packe, dann nicht mehr:

Code: Alles auswählen

class test:
    def formatter( number, format="%0.2f", comma=",", thousand=".", grouplength=3):
        """
        Formatierung für Zahlen
        s. http://www.python-forum.de/viewtopic.php?t=371
        """
        if abs(number) < 10**grouplength:
            return (format % (number)).replace(".", comma)
        if format[-1]=="f":
            vor_komma,hinter_komma=(format % number).split(".",-1)
        else:
            vor_komma=format % number
            comma=""
            hinter_komma=""
        #Hier
        anz_leer=0
        for i in vor_komma:
            if i==" ":
                anz_leer+=1
            else:
                break
        vor_komma=vor_komma[anz_leer:]
        #bis hier

        len_vor_komma=len(vor_komma)
        for i in range(grouplength,len_vor_komma+(len_vor_komma-1)/(grouplength+1)-(number<0),grouplength+1):
            vor_komma=vor_komma[0:-(i)]+thousand+vor_komma[-(i):]
        return anz_leer*" "+vor_komma+comma+hinter_komma

print test().formatter( 1028.328 )
Traceback (most recent call last):
File "number_formatter.py", line 33, in ?
print test().formatter( 1028.328 )
File "number_formatter.py", line 10, in formatter
if abs(number) < 10**grouplength:
AttributeError: test instance has no attribute '__abs__'

EDIT: Hat sich erledigt... Es fehlt natürlich das self :oops:

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten