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 werf auch mal eine Version ins Rennen ;

Code: Alles auswählen

def commify(number, comma=",", thousand="."):
    n = str(number).split(comma)
    n[0] = list(str(n[0]))[::-1]
    n[0] = thousand.join(
            ["".join(n[0][i:i+3][::-1]) for i in     
             range(0,len(n[0]),3)][::-1])
    return comma.join(n)

print commify(100000000)
print commify(1000.58,comma=".",thousand=",")
print commify("2,45")
print commify(1234567)
print commify("22343435455423423,2344324234")
Wie's mit der Performance aussieht, weiß ich nicht (ist das überhaupt wichtig?). Von den vielen [::-1] wird mir zwar schlecht, dafür kann man sich die Zeichen für Komma und Tausendertrennung aussuchen (es soll ja Leute geben, bei denen das Komma ein Punkt ist ;)).

Gruß, mawe
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

mawe hat geschrieben:Wie's mit der Performance aussieht, weiß ich nicht (ist das überhaupt wichtig?).
Kommt IMHO drauf an... Wenn man eine große Tabelle "umformen" will, ist es schon nicht schlecht, das es recht schnell geht...

Der größte Knackpunkt ist wohl, wenn Python's [::-1] zum umdrehen nicht vorliegt!

Aber wie schon geschrieben, was ist mit der Idee den Zahl-String auf zu füllen, das es gerade teilbar durch drei ist? ich hab gerade andere Sachen um das mal zu testen...

@cime: Sorry, hatte deinen Code glatt übersehen... Mir scheint es IMHO nur sehr lang ;)

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

jens, ich weiß nicht ob ich verstanden habe was Du meinst, aber ich hab mal das zusammengestoppelt:

Code: Alles auswählen

def commify(number, comma=",", thousand="."):
    n = str(number).split(comma)
    x = len(n[0]) % 3
    n[0] = "%s%s" % ("0" * (3-x), n[0])
    n[0] = thousand.join([n[0][i:i+3] 
              for i in range(0,len(n[0]),3)]).lstrip("0%s" % thousand)
    return comma.join(n)
Sieht auch recht performant aus :)
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

cime hat geschrieben: wollt ma fragen, warum ihr meinen code einfach ma so ignoriert. Er hat schließlich ein paar Vorteile:
1. er nutzt keine nicht automatisch schon importierten Funktionen (/Module)
2. er ist mit abstand am schnellsten (sieher unten)
Hi crime!

Ich habe deinen Code deshalb nicht getestet, da es mir wichtig ist, mindestens das Format, das Dezimaltrennzeichen und das Zeichen für die Gruppierung übergeben zu können. Das Einbauen dieser Variablen schien mir bei deinem Code zu kompliert.

Das Format ist wichtig, um auch einfache Listen realisieren zu können. Z.B. mit %15.2f. Oder auch die Rundung besser im Griff zu haben.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

gerold hat geschrieben: da es mir wichtig ist, mindestens das Format, das Dezimaltrennzeichen und das Zeichen für die Gruppierung übergeben zu können.
Formatieren kannst Du's doch auch bevor die Zahl an die Funktion übergeben wird.
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hi,

Habe noch eine Lösung gefunden. Sie ist der von cime ähnlich und etwas langsamer aber ca. 20% schneller als die regex-Lösung. Dafür kann man defChar, groupChar, groupRange und das Ausgabeformat übergeben.
Das Format gilt allerdings nur für die Nachkommastellen. Möchte man den ganzen Wert formatiert darstellen, muss man die Rückgabe der Funktion nochmals formatieren (z.B. mit %20s").
Außerdem ist sie auch für Pythonversionen < python 2.4 einsetzbar.
So sieht die Lösung aus:

Code: Alles auswählen

def __formatter(number, format = "%.2f", decChar = ",", groupChar = ".", groupRange = 3):
    """
    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) < 1:
        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:]
Den regEx der vorherigen Lösungen habe ich auch nochmals überarbeitet. Zahlen kleiner als Tausend wurden bei z.B. %20.2f nicht korrekt dargestellt. Bei negativen Zahlen größer als 1000 wurde das Minuszeichen entfernt.
Das ist der korrigierte regEx:

Code: Alles auswählen

re.compile("^ *\d*\D\d{1,%s}[ -]*|\d{1,%s}[ -]*" % (3, 3))
Ein weiteres Problem der regEx-Lösung ist, das die Formatierung nicht richtig dargestellt wird. Z.B. wird bei "%20.2f" jede Zahl pro Tausenderpotenz um ein Zeichen (nämlich das Gruppierungszeichen) länger. Das Ergebnis sieht dann folgendermaßen aus:

Code: Alles auswählen

             -0,234
              66,090
               1,000
               2,000
               3,400
              -5,678
          612.345,000
          612.345,555
      9.876.543.210,067
Um dieses Problem zu beheben müßte man den Code noch erweitern. Dadurch wird er aber noch langsamer.

Am besten wäre es natürlich wenn man über den Formatierungsoperator (%) Zahlen auch mit Gruppierungszeichen darstellen könnte. Eine gute Anregung an die Python-Programmierer.
Gruß, Harry
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

HarryH hat geschrieben:Eine gute Anregung an die Python-Programmierer.
Ich habe es schon mal eingetragen: http://www.pythonwiki.de/PythonWunschliste

Wie man aus den http://www.pythonwiki.de/Aktuelle%C3%84nderungen entnehmen kann ist die Seite allerdings gelöscht worden :evil:

Man kann sich aber noch die die alte Version anschauen mit: http://www.pythonwiki.de/PythonWunschli ... iff&rev2=8

Weiß jemand was da passiert ist???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jens hat geschrieben:Weiß jemand was da passiert ist???
Ja, ich vermute mal, dass es irgendein Fehler in MoinMoin ist, denn die Seite kann nicht mehr wiederhergestellt werden und auch nicht wieder angelegt werden. Ich habe mal dem Admin geschrieben, aber noch keine Antwort bekommen (es ist ja Wochenende).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
joe

jens hat geschrieben:Weiß jemand was da passiert ist???
Ich nehme an, gelöscht wegen "thema verfehlt". Bei wünschen für python 3000 geht es ja um sachen, die vorher nicht realisierbar sind, weil sie die rückwärtskompatibilität gefährden. Und auf der gelöschten seite war ja nichts (außer vielleicht der print-geschichte), was nicht auch in Version 2.5 oder 2.99 aufgenommen werden könnte. Oder wieso wolltest du den zahlenformatierer erst in der 3.0-version?
joe
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Also ich habe die Seite ehr als allgemeine Wunschliste verstanden und nicht unbedingt eine zweite PEP 3000 ;)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

joe hat geschrieben:Ich nehme an, gelöscht wegen "thema verfehlt".
Nein garantiert nicht, denn die einzigen die daran editiert haben, waren jens, gerold und ich. Außerdem würde bei einer Themaverfehlung vorgewarnt werden, PythonWiki ist ein sehr liberales Wiki im Gegensatz zu Wikibooks (das mag ich daran so gerne).
jens hat geschrieben:Also ich habe die Seite ehr als allgemeine Wunschliste verstanden und nicht unbedingt eine zweite PEP 3000 ;)
Ja, genau.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Ich habs auch mal versucht. Ob's schnell ist weiss ich nicht. Selbst gegebene Bedingung: Sollte ohne `reverse` auskommen:

Code: Alles auswählen

import re

# See also: Python-Docs-2.4/lib/decimal-recipes.html

def fp_format(num, thousands_separator=',', fraction_separator='.',
              format='%.2f'):
    """Formats a floating point number.
    
    Replaces the floating point with the given `fraction_separator` and groups
    thousands with the given `thousands_separator`.  By default thousands are
    grouped with ``','`` and the fraction is separated by ``'.'``.  The
    fractional part is two digits long.
    
    :bug: Wrong results if `num` is formatted in engeneering notation, i.e.
          with an exponent, and no floating point is present.
    """
    assert thousands_separator != fraction_separator
    
    numstr = format % num
    
    try:
        integer_part, fractional_part = numstr.split('.')
    except ValueError:
        integer_part, fractional_part = numstr, ''
    # 
    # Extract leading non-digit characters like spaces or ``-``/``+``.
    # 
    match = re.match(r'[\D]*', integer_part)
    if match:
        leading_non_digits = match.group()
        integer_part = integer_part[len(leading_non_digits):]
    else:
        leading_non_digits = ''
    # 
    # Split thousands groups and insert separator into `integer_part`.
    # 
    remainder = len(integer_part) % 3
    thousand_groups = list()
    if remainder:
        thousand_groups.append(integer_part[0:remainder])
    for i in xrange(remainder, len(integer_part), 3):
        thousand_groups.append(integer_part[i:i+3])
    integer_part = thousands_separator.join(thousand_groups)
    
    result = [leading_non_digits, integer_part]
    if fractional_part:
        result.append(fraction_separator)
        result.append(fractional_part)
    return ''.join(result)
cime
User
Beiträge: 152
Registriert: Dienstag 24. Mai 2005, 15:49

eine neue plus meine umgeänderte version:

Code: Alles auswählen

import string

def format_number_0(number,format="%0.2f",comma=",",thousand=".",grouplength=3):
    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 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
cime
User
Beiträge: 152
Registriert: Dienstag 24. Mai 2005, 15:49

Ich hab nochma ein geschwindigkeitscheck durchgeführt (hoffe hab keine version vergessen):

Code: Alles auswählen

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

def format_number_0(number,format="%0.2f",comma=",",thousand=".",grouplength=3):
    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 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

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

import re 

#---------------------------------------------------------------------- 
def format_number_1( 
   number, 
   format = "%0.2f", 
   decimal_char = ",", 
   group_char = ".", 
   group_range = 3 
): 
   """
   Nach einer Idee von Voges, HarryH und Jens. 
   http://www.python-forum.de/viewtopic.php?t=371 
   """
    
   def reverse(string): 
      # ersatz für string[::-1] welches erst ab v2.3 gibt :( 
      list = re.findall(".",string) 
      list.reverse() 
      return "".join(list) 
    
   regex = re.compile( 
      "^ *\d*\D\d{1,%s}|\d{1,%s} *" % (group_range, group_range) 
   ) 
    
   return reverse( 
      group_char.join( 
         regex.findall( 
            reverse( (format % number).replace( 
               ".", decimal_char) 
            ) 
         ) 
      ) 
   ) 


#---------------------------------------------------------------------- 
def format_number_2( 
   number, 
   format = "%0.2f", 
   decimal_char = ",", 
   group_char = ".", 
   group_range = 3 
): 
   """
   Nach einer Idee von Milan. 
   http://www.python-forum.de/viewtopic.php?t=371 
    """

   def split(s, size = group_range): 
      return map(lambda i: s[i:i + size], xrange(0, len(s), size)) 

   def reverse(s): 
      l = map(None, s) 
      l.reverse() 
      return ('').join(l) 

   num = (format % number).replace('.', decimal_char) 
   int_part = num[:num.find(decimal_char)] 
   return ( 
      reverse( 
         group_char.join( 
            split(reverse(int_part)) 
         ) 
      ) + num[num.find(decimal_char):] 
   ).replace(" %s" % group_char, "  ")

#---------------------------------------------------------------------- 
def format_number_3(num, thousands_separator=',', fraction_separator='.', 
              format='%.2f'): 
    """Formats a floating point number. 
    
    Replaces the floating point with the given `fraction_separator` and groups 
    thousands with the given `thousands_separator`.  By default thousands are 
    grouped with ``','`` and the fraction is separated by ``'.'``.  The 
    fractional part is two digits long. 
    
    :bug: Wrong results if `num` is formatted in engeneering notation, i.e. 
          with an exponent, and no floating point is present. 
    """ 
    assert thousands_separator != fraction_separator 
    
    numstr = format % num 
    
    try: 
        integer_part, fractional_part = numstr.split('.') 
    except ValueError: 
        integer_part, fractional_part = numstr, '' 
    # 
    # Extract leading non-digit characters like spaces or ``-``/``+``. 
    # 
    match = re.match(r'[\D]*', integer_part) 
    if match: 
        leading_non_digits = match.group() 
        integer_part = integer_part[len(leading_non_digits):] 
    else: 
        leading_non_digits = '' 
    # 
    # Split thousands groups and insert separator into `integer_part`. 
    # 
    remainder = len(integer_part) % 3 
    thousand_groups = list() 
    if remainder: 
        thousand_groups.append(integer_part[0:remainder]) 
    for i in xrange(remainder, len(integer_part), 3): 
        thousand_groups.append(integer_part[i:i+3]) 
    integer_part = thousands_separator.join(thousand_groups) 
    
    result = [leading_non_digits, integer_part] 
    if fractional_part: 
        result.append(fraction_separator) 
        result.append(fractional_part) 
    return ''.join(result) 

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

def format_number_4(number, comma=",", thousand="."): 
    n = str(number).split(comma) 
    n[0] = list(str(n[0]))[::-1] 
    n[0] = thousand.join( 
            ["".join(n[0][i:i+3][::-1]) for i in      
             range(0,len(n[0]),3)][::-1]) 
    return comma.join(n) 

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

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

    import time 

    zahlen = ( 
      66, 
      1, 
      2, 
      3.4, 
      -5.678, 
      612345, 
      612345.555, 
      987654321.067 
    )
    for x in range(2):
        begin = time.time()

        for i in range(10000): 
          for zahl in zahlen: 
             x = format_number_0(zahl)


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

        begin = time.time()

        for i in range(10000): 
          for zahl in zahlen: 
             x = format_number_0(zahl)

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

        begin = time.time() 

        for i in range(10000): 
          for zahl in zahlen: 
             x = format_number_1(zahl) 
              
        print "1:", time.time() - begin 
        print

        begin = time.time() 

        for i in range(10000): 
          for zahl in zahlen: 
             x = format_number_2(zahl) 
              
        print "2:", time.time() - begin 
        print 

        begin = time.time() 

        for i in range(10000): 
          for zahl in zahlen: 
             x = format_number_3(zahl) 
              
        print "3:", time.time() - begin 
        print 

        begin = time.time() 

        for i in range(10000): 
          for zahl in zahlen: 
             x = format_number_4(zahl) 
              
        print "4:", time.time() - begin 
        print 
Ergebnis:

Code: Alles auswählen

0: 1.6099998951

0b: 1.57800006866

1: 3.85900020599

2: 2.93700003624

3: 2.93700003624

4: 2.04699993134

0: 1.7650001049

0b: 1.875

1: 4.45399999619

2: 2.89099979401

3: 3.42199993134

4: 1.89100003242
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Da ist ein Fehler in dem Test drin... Du testes zweimal _0 und nicht _0b ;)

So ist's richtig:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: UTF-8 -*-

#format_number.py
import string

def format_number_0(number,format="%0.2f",comma=",",thousand=".",grouplength=3):
    """
    by cime
    http://www.python-forum.de/viewtopic.php?p=20935#20935
    """
    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):
    """
    by cime
    http://www.python-forum.de/viewtopic.php?p=20935#20935
    """
    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

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

import re

#----------------------------------------------------------------------
format_number_1_regex = re.compile("^ *\d*\D\d{1,3}[ -]*|\d{1,3}[ -]*" )
def format_number_1(
   number,
   format = "%0.2f",
   decimal_char = ",",
   group_char = ".",
   group_range = 3
):
    """
    Nach einer Idee von Voges, HarryH, Milan und Jens.
    http://www.python-forum.de/viewtopic.php?t=371
    """
    def reverse(s):
        # ersatz für string[::-1] welches erst ab v2.3 gibt :(
        l = map(None, s)
        l.reverse()
        return ('').join(l)


    return reverse(
        group_char.join(
            format_number_1_regex.findall(
                reverse( (format % number).replace( ".", decimal_char)
                )
            )
        )
    )


#----------------------------------------------------------------------
def format_number_2(
   number,
   format = "%0.2f",
   decimal_char = ",",
   group_char = ".",
   group_range = 3
):
   """
   Nach einer Idee von Milan.
   http://www.python-forum.de/viewtopic.php?t=371
    """

   def split(s, size = group_range):
      return map(lambda i: s[i:i + size], xrange(0, len(s), size))

   def reverse(s):
      l = map(None, s)
      l.reverse()
      return ('').join(l)

   num = (format % number).replace('.', decimal_char)
   int_part = num[:num.find(decimal_char)]
   return (
      reverse(
         group_char.join(
            split(reverse(int_part))
         )
      ) + num[num.find(decimal_char):]
   ).replace(" %s" % group_char, "  ")

#----------------------------------------------------------------------
def format_number_3(num, thousands_separator=',', fraction_separator='.',
              format='%.2f'):
    """
    by BlackJack
    http://www.python-forum.de/viewtopic.php?p=20917#20917

    Formats a floating point number.

    Replaces the floating point with the given `fraction_separator` and groups
    thousands with the given `thousands_separator`.  By default thousands are
    grouped with ``','`` and the fraction is separated by ``'.'``.  The
    fractional part is two digits long.

    :bug: Wrong results if `num` is formatted in engeneering notation, i.e.
          with an exponent, and no floating point is present.
    """
    assert thousands_separator != fraction_separator

    numstr = format % num

    try:
        integer_part, fractional_part = numstr.split('.')
    except ValueError:
        integer_part, fractional_part = numstr, ''
    #
    # Extract leading non-digit characters like spaces or ``-``/``+``.
    #
    match = re.match(r'[\D]*', integer_part)
    if match:
        leading_non_digits = match.group()
        integer_part = integer_part[len(leading_non_digits):]
    else:
        leading_non_digits = ''
    #
    # Split thousands groups and insert separator into `integer_part`.
    #
    remainder = len(integer_part) % 3
    thousand_groups = list()
    if remainder:
        thousand_groups.append(integer_part[0:remainder])
    for i in xrange(remainder, len(integer_part), 3):
        thousand_groups.append(integer_part[i:i+3])
    integer_part = thousands_separator.join(thousand_groups)

    result = [leading_non_digits, integer_part]
    if fractional_part:
        result.append(fraction_separator)
        result.append(fractional_part)
    return ''.join(result)

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

def format_number_4(number, comma=",", thousand="."):
    n = str(number).split(comma)
    n[0] = list(str(n[0]))[::-1]
    n[0] = thousand.join(
            ["".join(n[0][i:i+3][::-1]) for i in
             range(0,len(n[0]),3)][::-1])
    return comma.join(n)

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

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

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

def format_number_6(number, comma=",", thousand="."):
    """
    by mawe
    http://www.python-forum.de/viewtopic.php?p=20850#20850
    """
    n = str(number).split(comma)
    x = len(n[0]) % 3
    n[0] = "%s%s" % ("0" * (3-x), n[0])
    n[0] = thousand.join([n[0][i:i+3]
              for i in range(0,len(n[0]),3)]).lstrip("0%s" % thousand)
    return comma.join(n)

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

import locale
def format_numer_locale( number ):
    #~ locale.setlocale(locale.LC_ALL, "de_DE.UTF-8")
    return locale.format("%0.1f", number, True)

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

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

    import time

    zahlen = (
        (66, "66,00"),
        (1, "1,00"),
        (2, "2,00"),
        (3.4, "3,40"),
        (-5.678, "-5,68"),
        (612345, "612.345,00"),
        (612345.555, "612.345,56"),
        (987654321.067, "987.654.321,07"),
    )
    defs = [
        ("0", format_number_0),
        ("0b", format_number_0b),
        ("1", format_number_1),
        ("2", format_number_2),
        ("3", format_number_3),
        ("4", format_number_4),
        ("5", format_number_5),
        ("6", format_number_6),
        ("format_numer_locale", format_numer_locale )

    ]
    for i in defs:
        print "_"*80
        print ">>>",i[0]
        for z in zahlen:
            result = i[1](z[0])
            print "%s -> %s " % (z[0],result),
            if str(result) == z[1]:
                print "OK"
            else:
                print "error!"

    print "="*80

    loops = 10000
    for i in defs:
        print i[0],
        begin = time.time()
        for x in range( loops ):
            for z in zahlen:
                i[1](z[0])
        print "%0.3fs" % (time.time() - begin)
Ergebnis:

Code: Alles auswählen

________________________________________________________________________________
>>> 0
66 -> 66,00  OK
1 -> 1,00  OK
2 -> 2,00  OK
3.4 -> 3,40  OK
-5.678 -> -5,68  OK
612345 -> 612.345,00  OK
612345.555 -> 612.345,56  OK
987654321.067 -> 987.654.321,07  OK
________________________________________________________________________________
>>> 0b
66 -> 66,00  OK
1 -> 1,00  OK
2 -> 2,00  OK
3.4 -> 3,40  OK
-5.678 -> 5,68  error!
612345 -> 612.345,00  OK
612345.555 -> 612.345,56  OK
987654321.067 -> 9876.54.321,07  error!
________________________________________________________________________________
>>> 1
66 -> 66,00  OK
1 -> 1,00  OK
2 -> 2,00  OK
3.4 -> 3,40  OK
-5.678 -> -5,68  OK
612345 -> 612.345,00  OK
612345.555 -> 612.345,56  OK
987654321.067 -> 987.654.321,07  OK
________________________________________________________________________________
>>> 2
66 -> 66,00  OK
1 -> 1,00  OK
2 -> 2,00  OK
3.4 -> 3,40  OK
-5.678 -> -5,68  OK
612345 -> 612.345,00  OK
612345.555 -> 612.345,56  OK
987654321.067 -> 987.654.321,07  OK
________________________________________________________________________________
>>> 3
66 -> 66.00  error!
1 -> 1.00  error!
2 -> 2.00  error!
3.4 -> 3.40  error!
-5.678 -> -5.68  error!
612345 -> 612,345.00  error!
612345.555 -> 612,345.56  error!
987654321.067 -> 987,654,321.07  error!
________________________________________________________________________________
>>> 4
66 -> 66  error!
1 -> 1  error!
2 -> 2  error!
3.4 -> 3.4  error!
-5.678 -> -5..678  error!
612345 -> 612.345  error!
612345.555 -> 6.123.45..555  error!
987654321.067 -> 9.876.543.21..067  error!
________________________________________________________________________________
>>> 5
66 -> 66,00  OK
1 -> 1,00  OK
2 -> 2,00  OK
3.4 -> 3,40  OK
-5.678 -> -5,68  OK
612345 -> 612.345,00  OK
612345.555 -> 612.345,56  OK
987654321.067 -> 987.654.321,07  OK
________________________________________________________________________________
>>> 6
66 -> 66  error!
1 -> 1  error!
2 -> 2  error!
3.4 -> 3.4  error!
-5.678 -> -5..678  error!
612345 -> 612.345  error!
612345.555 -> 6.123.45..555  error!
987654321.067 -> 9.876.543.21..067  error!
________________________________________________________________________________
>>> format_numer_locale
66 -> 66.0  error!
1 -> 1.0  error!
2 -> 2.0  error!
3.4 -> 3.4  error!
-5.678 -> -5.7  error!
612345 -> 612345.0  error!
612345.555 -> 612345.6  error!
987654321.067 -> 987654321.1  error!
================================================================================
0 0.953s
0b 0.703s
1 1.234s
2 1.500s
3 1.407s
4 1.156s
5 0.765s
6 0.875s
format_numer_locale 1.797s
EDIT: Ich hab normal nachgesehen, welche Variante von wem war. Außerdem hab ich die jeweils letzte Version rausgesucht. Zu guter letzt hab ich auch noch eine einfache locale.format() Geschichte aufgenommen...

EDIT2: So, hab auch noch ein Test eigebaut, ob die Ergebnisse denn überhaupt stimmen!

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!

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
Antworten