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])

Beitragvon mawe » Montag 11. Juli 2005, 09:15

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
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Montag 11. Juli 2005, 09:57

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

CMS in Python: http://www.pylucid.org
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])

Beitragvon mawe » Montag 11. Juli 2005, 10:39

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

re:

Beitragvon HarryH » Montag 11. Juli 2005, 10:57

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
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Montag 11. Juli 2005, 11:00

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???

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
cime
User
Beiträge: 152
Registriert: Dienstag 24. Mai 2005, 15:49

Beitragvon cime » Montag 11. Juli 2005, 11:23

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

Beitragvon cime » Montag 11. Juli 2005, 11:32

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

re:

Beitragvon HarryH » Montag 11. Juli 2005, 11:51

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

Beitragvon cime » Montag 11. Juli 2005, 15:42

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 ;) )
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Montag 11. Juli 2005, 22:09

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

Beitragvon HarryH » Dienstag 12. Juli 2005, 05:52

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

Beitragvon cime » Dienstag 12. Juli 2005, 10:35

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

re:

Beitragvon HarryH » Dienstag 12. Juli 2005, 13:00

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

Beitragvon cime » Mittwoch 13. Juli 2005, 07:54

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
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Freitag 15. Juli 2005, 15:08

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:

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd

Wer ist online?

Mitglieder in diesem Forum: Bing [Bot]