我有一个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]。结果是一样的。这非常烦人。如何在保持精度的同时轻松转换为字符串?谢谢!
答案 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.round
与float32
不相称(其他答案说明理由)。一个简单的解决方法是将您的数组明确地转换为np.float
或np.float64
或float
:
>>> # 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.array
从tolist
转换到列表。实际上,您很少需要执行该操作,因为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呈现如下:
使用格式字符串/格式化字符串文字,生成的docx如下所示: