2013-12-08 6 views
6

Ich habe eine numpy Array, in dem jede Zahl eine bestimmte benannte Präzision hat (mit der Umgebung (x, 1).Präzision der numpy Array verloren, nachdem ToList

[[  3. 15294.7 32977.7 4419.5 978.4 504.4 123.6] 
[  4. 14173.8 31487.2 3853.9 967.8 410.2 107.1] 
[  5. 15323.5 34754.5 3738.7 1034.7 376.1 105.5] 
[  6. 17396.7 41164.5 3787.4 1103.2 363.9 109.4] 
[  7. 19665.5 48967.6 3900.9 1161.  362.1 115.8] 
[  8. 21839.8 56922.5 4037.4 1208.2 365.9 123.5] 
[  9. 23840.6 64573.8 4178.1 1247.  373.2 131.9] 
[ 10. 25659.9 71800.2 4314.8 1279.5 382.7 140.5] 
[ 11. 27310.3 78577.7 4444.3 1307.1 393.7 149.1] 
[ 12. 28809.1 84910.4 4565.8 1331.  405.5 157.4]] 

Ich versuche, jede Zahl in eine umwandeln String, so dass ich sie in eine Worttabelle mit python-docx schreiben kann. Aber das Ergebnis ToList() Funktion ist ein totales Durcheinander. die Genauigkeit der Zahlen verloren, was sehr lange Ausgabe.

[['3.0', 
    '15294.7001953', 
    '32977.6992188', 
    '4419.5', 
    '978.400024414', 
    '504.399993896', 
    '123.599998474'], 
['4.0', 
    '14173.7998047', 
    '31487.1992188', 
    '3853.89990234', 
    '967.799987793', 
    '410.200012207', 
    '107.099998474'], 
....... 

Neben Die tolist() - Funktion, ich versuchte auch [[str (e) für e in a] für a in m]. Das Ergebnis ist das gleiche. Das ist sehr ärgerlich ng. Wie kann ich unter Beibehaltung der Genauigkeit leicht in eine Zeichenkette konvertieren? Vielen Dank!

+0

Ist Ihr Array einfach genau ('np.float32')? –

+0

Ja, es ist float32. Ist das ein Problem? – sanqiang

+0

Siehe meine Antwort oder die Antwort von @HenryGomersall –

Antwort

3

Bei der Umwandlung in Strings geht etwas schief. Mit nur Zahlen:

>>> import numpy as np 
>>> a = np.random.random(10)*30 
>>> a 
array([ 27.30713434, 10.25895255, 19.65843272, 23.93161555, 
     29.08479175, 25.69713898, 11.90236158, 5.41050686, 
     18.16481691, 14.12808414]) 
>>> 
>>> b = np.round(a, decimals=1) 
>>> b 
array([ 27.3, 10.3, 19.7, 23.9, 29.1, 25.7, 11.9, 5.4, 18.2, 14.1]) 
>>> b.tolist() 
[27.3, 10.3, 19.7, 23.9, 29.1, 25.7, 11.9, 5.4, 18.2, 14.1] 

Beachten Sie, dass np.round funktioniert nicht an Ort und Stelle:

>>> a 
array([ 27.30713434, 10.25895255, 19.65843272, 23.93161555, 
     29.08479175, 25.69713898, 11.90236158, 5.41050686, 
     18.16481691, 14.12808414]) 

Wenn alle notwendigen Zahlen in Strings konvertieren:

>>> " ".join(str(_) for _ in np.round(a, 1)) 
'27.3 10.3 19.7 23.9 29.1 25.7 11.9 5.4 18.2 14.1' 

EDIT: Anscheinend np.round spielt nicht nett mit float32 (andere Antworten geben Gründe dafür). Eine einfache Lösung wäre es, Ihr Array explizit entweder np.float oder np.float64 oder nur float würfe

>>> # prepare an array of float32 values 
>>> a32 = (np.random.random(10) * 30).astype(np.float32) 
>>> a32.dtype 
dtype('float32') 
>>> 
>>> # notice the use of .astype(np.float32) 
>>> np.round(a32.astype(np.float64), 1) 
array([ 5.5, 8.2, 29.8, 8.6, 15.5, 28.3, 2. , 24.5, 18.4, 8.3]) 
>>> 

EDIT2: Wie gezeigt durch Warren in seiner Antwort Zeichenfolge Formatierung tatsächlich rundet Dinge richtig (versuchen "%.1f" % (4.79,)). Daher müssen Sie nicht zwischen Float-Typen umwandeln. Ich werde meine Antwort hauptsächlich als Erinnerung daran hinterlassen, dass die Verwendung von np.around unter diesen Umständen nicht richtig ist.

+0

Danke für Ihre Antwort, aber ich kann immer noch nicht richtig. Ich benutzte einfach np.around (x, 1). Aber ich bekomme einen sehr langen Schwanz auf jeder Zahl. Wie: Array ([448,3999939, 521,59997559, 581,70001221, 635,40002441, 688,79998779, 746., 808., 872,40002441, 935,90002441, 996,40002441], dtype = float32) – sanqiang

+0

@sanqiang Bitte die bearbeitete Antwort. –

1

Schwimmer sind sehr gut in der Lage, einen großen Bereich mit einer genau definierten relativen Genauigkeit zu speichern. Bei 32-Bit-Floats sind dies etwa 7 signifikante Zahlen. Wie Sie bemerkt haben, ist die tatsächliche Zahl, die Sie beim Runden erhalten, nicht genau die Zahl, auf die Sie gehofft haben, sondern liegt bei ungefähr 7 signifikanten Zahlen.

Ein Weg, um zu bekommen, was Sie wollen, kann die decimal.Decimal type sein. Sie können eine numpy Array von diesen konstruieren, indem die dtype Einstellung dieser Art zu sein:

import decimal 
a = numpy.array(original_array, dtype=decimal.Decimal) 

Hinweis ist das resultierende Array nur eine Reihe von Python-Objekten, sondern als ein „richtiger“ numpy Array, so dass Sie‘ Wahrscheinlich müssen Sie Ihre eigene Rundungsfunktion und vielleicht auch einige andere Dinge, die nicht funktionieren, rollen.

Es könnte besser sein, nur mit eingebauten Python-Strukturen umzugehen, um zu bekommen, was Sie wollen.

3

Die Präzision wird nicht "verloren"; Sie hatten nie die Präzision an erster Stelle. Der Wert 15294.7 kann nicht genau mit einfacher Genauigkeit dargestellt werden (d. H. Np.float32); die beste Annäherung ist 15294.70019 ...:

In [1]: x = np.array([15294.7], dtype=np.float32) 

In [2]: x 
Out[2]: array([ 15294.70019531], dtype=float32) 

Siehe http://floating-point-gui.de/

np.float64 Verwendung gibt Ihnen eine bessere Annäherung, aber es kann immer noch nicht 15.294,7 genau darstellen.

Wenn Sie Text ausgegeben werden soll, die mit einem einzigen Nachkommastelle, verwenden Sie eine Funktion entwickelt, für formatierte Textausgabe, wie np.savetxt formatiert ist:

In [56]: x = np.array([[15294.7, 32977.7],[14173.8, 31487.2]], dtype=np.float32) 

In [57]: x 
Out[57]: 
array([[ 15294.70019531, 32977.69921875], 
     [ 14173.79980469, 31487.19921875]], dtype=float32) 

In [58]: np.savetxt("data.txt", x, fmt="%.1f", delimiter=",") 

In [59]: !cat data.txt 
15294.7,32977.7 
14173.8,31487.2 

Wenn Sie wirklich eine numpy Reihe von schön formatierte Strings benötigen, Sie könnte so etwas tun:

In [63]: def myfmt(r): 
    ....:  return "%.1f" % (r,) 
    ....: 

In [64]: vecfmt = np.vectorize(myfmt) 

In [65]: vecfmt(x) 
Out[65]: 
array([['15294.7', '32977.7'], 
     ['14173.8', '31487.2']], 
     dtype='|S64') 

Wenn Sie eine dieser Methoden verwenden, gibt es keine Notwendigkeit, die Daten durch around zuerst zu passieren; Das Runden wird als Teil des Formatierungsprozesses auftreten.

+0

(+1) Ich arbeitete unter der Eindruck, dass die Formatierung von Zeichenfolgen abgeschnitten, nicht Runden. Vielen Dank! –

+0

Vielen Dank für die Erklärung. Meine Matrix wird aus mehreren 1-D-Arrays zusammengeführt und jede von ihnen kann unterschiedliche Genauigkeitsanforderungen haben. Deshalb kann "% .1f"% (r,) nicht verwendet werden, um sie im endgültigen Anzeigeprozess zu trunkeln. Ich bin jetzt auf float64 umgestellt, was gut funktioniert, aber ich fürchte, es könnte mehr Speicher erforderlich sein als float32, da die Daten groß sein könnten. – sanqiang