Zahl zu String mit maximaler Zeichenlänge bzw. Digits

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.
Antworten
bastel-wastel
User
Beiträge: 28
Registriert: Samstag 20. Februar 2010, 15:14

Hallo miteinander,

ich suche eine Möglichkeit, um Zahlen in Strings zu wandeln. Nach dem Schema

Code: Alles auswählen

print( '%.2f' %123.456789)
kann ich die Typumwandlung ja steuern, bspw. die Anzahl der Nachkommastellen festlegen. Ich bräuchte jedoch eine Ausgabe, die stets eine konstante Länge hat, um die Daten schön formatiert auszugeben.
Bspw. Strings mit Länge = 5
Zahl -> String
123.456789 -> 123.4
1.23456 -> 1.234
2.345e2 -> 2.3e2

Da ich viel mit numpy rechne war mein erster Versuch:

Code: Alles auswählen

import numpy as np
a=np.pi*1e-5
a
>>> 3.1415926535897935e-05
b=np.array(a,dtype='|S32')             # Nach Stringarray mit 32 Zeichen
b
>>> array('3.14159265359e-05',  dtype='|S32')
c=np.array(a,dtype='|S8')             # Nach Stringarray mit 8 Zeichen
c
array('3.141592', dtype='|S8')
Diese Funktion ist hier unbrauchbar, da anscheinend zuerst eine Typumwandlung durchgeführt wird und die Strings anschließend einfach abgeschnitten werden.

Kennt jemand eine ordentliche Möglichkeit zu meinem Problem? Ich habe bislang noch nichts Passendes gefunden.

Gruß
bastel
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Da es dir ja nur auf die Stringlänge anzukommen scheint - sozusagen unabhängig vom Inhalt -, würde ich nicht mal die Möglichkeiten des String-Formattings bemühen (zumal ich da nichts passendes wüsste) und daher ganz stumpf sowas vorschlagen:

Code: Alles auswählen

>>> str(123.456789)[:5]
'123.4'
Ausdrücke wie 1e-5 würden bei einer Stringlänge von 5 auch noch darstellbar sein. Dass man aber durch diese Art von Abschneiden durchaus die Bedeutung einer Zahlenangabe verändern kann, wird dir wohl sowieso klar sein.
bastel-wastel
User
Beiträge: 28
Registriert: Samstag 20. Februar 2010, 15:14

Hi,

danke für Deine Antwort. Mir geht es jedoch genau um den Umstand, dass die Bedeutung der Zahl nicht verändert werden soll. Lediglich die Genauigkeit darf verringert werden. Aber gerade bei Zahlen in Exponentenschreibweise kommt es hier zu einem nicht zulässigen Abschneiden.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dann such dir halt mit `zahlstring.find('e')` die Position heraus und hol dir diesen Part mittels Slicing. Und dann setzt du diesen mit der restlichen Stellenzahl deiner Zahl zusammen. Eventuell wäre hier ein regulärer Ausdruck etwas eleganter. Falls du nicht selber drauf kommst: Ich muss kurz weg, könnte dir aber in nem Stündchen den passenden Code posten, falls mir keiner zuvor kommt. ^^
bastel-wastel
User
Beiträge: 28
Registriert: Samstag 20. Februar 2010, 15:14

Den String zu durchsuchen wäre natürlich ne Möglichkeit. Dann könnte ich den String auf Zeichenebene zusammen setzen.
Wenn es eine elegantere Lösung gibt, hätte ich die jedoch bevorzugt. Da später größere Array's mit bis zu 100k Elementen konvertiert werden sollen, sollte das ganze halbwegs performant sein.

Mit regulären Ausdrücken habe ich bisher noch nicht gearbeitet - zumindest nicht bewusst ;)
Für weiterführende Tipps wäre ich dankbar.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hab mal was gebastelt:

Code: Alles auswählen

import re

def get_numstring(number, maxlen=8):
    numstring = str(number)
    ematch = re.search('[eE].+', numstring)
    if ematch:
        # Zahl liegt in Exponentialschreibweise vor
        exp = ematch.group()
        length = min(len(numstring), maxlen) - len(exp)
        numstring = numstring[:length] + exp
    else:
        numstring = numstring[:maxlen]
    return numstring
Hinsichtlich der Performance solltest du erstmal testen, ob die befürchteten Einbußen damit tatsächlich bestehen. Falls ja, dann könnte man überlegen, ob man das Skript mit PyPy laufen lässt oder ob man vielleicht auf einen ganz schlauen Ausdruck für `re.sub()` oder so kommt, damit mehr Sachen durch die in C implementierte Regex-Engine übernommen werden (keine Ahnung, ob das klappen würde). Aber wie gesagt: Teste am besten erstmal die tatsächliche Laufzeit.
bastel-wastel
User
Beiträge: 28
Registriert: Samstag 20. Februar 2010, 15:14

Guten Morgen,

vielen Dank für den Code. Der funktioniert ja wunderbar. Sowohl mit Dezimal- als auch mit Exponentdarstellung. :D
Geschwindigkeit werde ich noch testen und prüfen, ob tiefergehende Modifikationen überhaupt notwendig sind.
Auf jeden Fall werde ich mich definitiv mit regulären Ausdrücken beschäftigen ;)

Danke nochmal!

Gruß
bastel
bastel-wastel
User
Beiträge: 28
Registriert: Samstag 20. Februar 2010, 15:14

Hab mal die Geschwindkeit getestet:

Eine Liste mit einer Million Zahlen wurde in etwas 4,5 Sekunden konvertiert (Core2Duo 3Ghz). Bei maximaler Zeichenlänge ergibt das eine ASCII-Datei von immerhin 25MB.
Es ist nicht zu erwarten, dass bei mir Daten mit mehr als 100.000 Elementen auftreten.
Performance ist für mich deshalb ausreichend!

Gruß
bastel
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Die Funktion kann übrigens auch mit Zahlen umgehen, die bereits als String vorliegen. Bei Exponentialschreibweise darf entweder das kleine "e" benutzt werden (wie es Python macht) oder das große "E". Vielleicht ist das ja wichtig, falls deine Daten aus einer externen Quelle kommen.
Antworten