Seite 3 von 3

Re: re:

Verfasst: Samstag 9. Juli 2005, 14:21
von Leonidas
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.

Verfasst: Sonntag 10. Juli 2005, 01:48
von 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)

Verfasst: Montag 11. Juli 2005, 07:49
von cime
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

Verfasst: Montag 11. Juli 2005, 07:55
von cime
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

Verfasst: Montag 11. Juli 2005, 08:26
von jens
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!

Verfasst: Montag 11. Juli 2005, 09:15
von mawe
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)

Verfasst: Montag 11. Juli 2005, 09:57
von jens
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

Verfasst: Montag 11. Juli 2005, 10:39
von mawe
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

re:

Verfasst: Montag 11. Juli 2005, 10:57
von HarryH
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.

Verfasst: Montag 11. Juli 2005, 11:00
von jens
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???

Verfasst: Montag 11. Juli 2005, 11:23
von cime
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 ...

Verfasst: Montag 11. Juli 2005, 11:32
von cime
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

re:

Verfasst: Montag 11. Juli 2005, 11:51
von HarryH
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.

Verfasst: Montag 11. Juli 2005, 15:42
von cime
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 ;) )

Verfasst: Montag 11. Juli 2005, 22:09
von Leonidas
Für alle die es interessiert: PythonWunschliste wurde repariert und funktioniert wieder einwandfrei.

Verfasst: Dienstag 12. Juli 2005, 05:52
von HarryH
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!

Verfasst: Dienstag 12. Juli 2005, 10:35
von cime
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:] 

re:

Verfasst: Dienstag 12. Juli 2005, 13:00
von HarryH
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)

Verfasst: Mittwoch 13. Juli 2005, 07:54
von cime

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.

Verfasst: Freitag 15. Juli 2005, 15:08
von jens
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: