在收费后丢失了numpy阵列的精度

时间:2013-12-08 14:08:48

标签: numpy

我有一个numpy数组,其中每个数字都有一定的指定精度(使用around(x,1)。

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

我正在尝试将每个数字转换为字符串,以便我可以使用python-docx将它们写入字表。但是tolist()函数的结果是一团糟。数字的精度会丢失,导致输出很长。

[['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'],
.......

除了tolist()函数之外,我还尝试了[[str(e)for a in a] for a m]。结果是一样的。这非常烦人。如何在保持精度的同时轻松转换为字符串?谢谢!

5 个答案:

答案 0 :(得分:5)

精度没有“丢失”;你从来没有精确到第一位。 值15294.7不能用单精度精确表示(即np.float32);最好的近似值 15294.70019 ......:

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

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

请参阅http://floating-point-gui.de/

使用np.float64可以获得更好的近似值,但它仍然不能完全代表15294.7。

如果您想要使用单个十进制数字格式化的文本输出,请使用专为格式化文本输出设计的函数,例如np.savetxt

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

如果你真的需要一个格式很好的字符串的numpy数组,你可以这样做:

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

如果您使用其中任何一种方法,则无需先通过around传递数据;舍入将作为格式化过程的一部分。

答案 1 :(得分:4)

转换为字符串时出现问题。只有数字:

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

请注意np.round无法就地工作:

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

如果您只需要将数字转换为字符串:

>>> " ".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'

编辑:显然,np.roundfloat32不相称(其他答案说明理由)。一个简单的解决方法是将您的数组明确地转换为np.floatnp.float64float

>>> # 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 :正如Warren在他的回答中所证明的那样,字符串格式化实际上是对事物进行了正确处理(尝试"%.1f" % (4.79,))。因此,不需要在float类型之间进行转换。我将留下我的回答主要是为了提醒我在这些情况下使用np.around不是正确的做法。

答案 2 :(得分:2)

Floats非常擅长存储具有良好定义的相对精度的大范围。在32位浮点数的情况下,这是大约7个有效数字。正如您所注意到的,进行舍入练习时获得的实际数字并不完全是您希望的数字,但接近于大约7位有效数字。

获得所需内容的一种方法可能是使用decimal.Decimal type。您可以通过将dtype设置为该类型来构造这些numpy数组:

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

注意,结果数组只是一个python对象的数组,而不是一个“正确的”numpy数组,所以你可能需要滚动自己的舍入函数,也许还有其他一些不起作用的东西。

处理内置的python结构以获得你想要的东西可能更好。

答案 3 :(得分:1)

即使您对开头的numpy float32数组中的数据没有任何控制,也可以将类型更改为更高的精度,然后在调用tolist之前舍入。实际上,您甚至可以使用astype进行字符串转换。例如:

>>> import numpy as np
>>> a = np.array([[    3.0, 15294.7, 32977.7],
                  [ 4419.5,   978.4,   504.4]])
>>> a.astype(float).round(1).astype(str).tolist()
[['3.0', '15294.7', '32977.7'], ['4419.5', '978.4', '504.4']]

答案 4 :(得分:0)

所有答案都正确地讨论了浮点精度和输出,但我想补充一点,您首先不需要使用np.arraytolist转换到列表。实际上,您很少需要执行该操作,因为numpy数组通常表现得非常相似,如下例所示:

import docx
import numpy as np

# Your values from above
raw_data = np.array([[ 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.0, 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.0, 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.0, 405.5, 157.4]],
                    dtype=np.float32)

# This conversion is just for comparison purposes, both tables will be printed.
pyt_data = raw_data.tolist()

def create_table(document, values, heading):
    """Creates a docx table inside the document.

    This function takes a docx.Document, a two-dimensional data structure, e.g.
    numpy arrays or a list of lists, and fills the table with it.
    The table is also prefixed with a heading.
    """
    document.add_heading(heading)
    table = document.add_table(rows=0, cols=len(values[0]))
    for row in values:
        cells = table.add_row().cells
        for i, value in enumerate(row):
            # Use `str` for any types, but the format string 
            # only if you expect numerical types exclusively
            cells[i].text = str(value)  # f'{value:.1f}'

document = docx.Document()
create_table(document, raw_data, 'Raw table')
create_table(document, pyt_data, 'tolist table')
document.save('table_demo.docx')

如果您将注释行cells[i].text = str(value)更改为cells[i].text = f'{value:.1f'}(或使用Python< 3.6 cells[i].text = '{:.1f}'.format(value)),那么两个表都正常工作,因为您使用您的浮点值格式化自定义格式。如果您只使用字符串表示,则numpy值已经正确。

请注意,如果您使用np.float64,则两个版本都是正确的!

使用字符串表示形式,生成的docx呈现如下:

The two tables using str

使用格式字符串/格式化字符串文字,生成的docx如下所示:

enter image description here