提问人:D.R 提问时间:11/15/2023 最后编辑:D.R 更新时间:11/18/2023 访问量:107
imshow 绘制非常大的整数,但“dtype 对象无法转换为浮点数”
imshow plotting very large integers, but "dtype object cannot be converted to float"
问:
我有以下代码,在网格上绘制一个函数,其中该函数恰好具有非常大的整数值:
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter, FuncFormatter
import numpy as np # thanks to user @simon pointing out I had forgotten this
p = 13
counts = [[0 for x in range(p)] for y in range(p)]
counts[0][0] = 1000000000
unique_counts = np.unique(counts)
plt.imshow(counts, cmap='viridis', origin='lower', extent=[0, p-1, 0, p-1])
cbar = plt.colorbar(ticks=unique_counts, format=ScalarFormatter(useOffset=False))
cbar.ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: format(int(x), ','))) # Format tick labels with commas
plt.show()
在 GoogleColab 中运行它,它运行良好并给出了漂亮的绘图
但是,如果我碰到,那么我会收到以下错误:counts[0][0] = 1000000000000000000000
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-0ec4c2551685> in <cell line: 8>()
6 counts[0][0] = 100000000000000000000
7 unique_counts = np.unique(counts)
----> 8 plt.imshow(counts, cmap='viridis', origin='lower', extent=[0, p-1, 0, p-1])
9 cbar = plt.colorbar(ticks=unique_counts, format=ScalarFormatter(useOffset=False))
10 cbar.ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: format(int(x), ','))) # Format tick labels with commas
3 frames
/usr/local/lib/python3.10/dist-packages/matplotlib/image.py in set_data(self, A)
699 if (self._A.dtype != np.uint8 and
700 not np.can_cast(self._A.dtype, float, "same_kind")):
--> 701 raise TypeError("Image data of dtype {} cannot be converted to "
702 "float".format(self._A.dtype))
703
TypeError: Image data of dtype object cannot be converted to float
我非常希望能够以精确的精度绘制采用非常大整数值的函数(因此舍入/使用浮点数不好)。这可能吗?
编辑:可以理解的是,有人对情节中这种看似无用的精确度感到困惑;我澄清说,对我来说真正重要的是能够从颜色条标签上读取确切的值(对于数论应用程序,我需要对某些品种 mod p 上的点数进行精确计数)。所以我可以接受情节稍微偏离,但我真的希望颜色条标签准确无误。
答:
Float 在处理大整数时出现问题。以下代码对我有用,我希望这是您想要的结果。 在科学记数法中使用可读的数字总是比拥有非常大的数字要好一些。
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
import numpy as np
p = 13
counts = [[0 for x in range(p)] for y in range(p)]
counts[0][0] = 100000000000000000000000
# Convert counts to a NumPy array
counts_array = np.array(counts, dtype=float)
# Create the plot
plt.imshow(counts_array, cmap='viridis', origin='lower', extent=[0, p-1, 0, p-1])
cbar = plt.colorbar()
cbar.set_ticks([np.min(counts_array), np.max(counts_array)])
cbar.ax.yaxis.set_major_formatter(ScalarFormatter(useOffset=False, useMathText=True))
cbar.update_ticks()
plt.show()
在上面的代码中,即使 imshow 和绘图有效,大数字仍然没有显示在色条中。我在这里添加了一个新的片段,这将有助于打印数字。我无法让 matplotlib 打印数字,所以现在我使用字符串显式格式化颜色条。
import matplotlib.pyplot as plt
import numpy as np
p = 13
counts = [[0 for x in range(p)] for y in range(p)]
counts[0][0] = 1000000000000000000000
# Convert counts to a NumPy array
counts_array = np.array(counts, dtype=float)
# Create the plot
plt.imshow(counts_array, cmap='viridis', origin='lower', extent=[0, p-1, 0, p-1])
cbar = plt.colorbar()
# Set custom tick labels for the colorbar
tick_labels = [f'{int(x):,}' for x in cbar.get_ticks()]
cbar.set_ticklabels(tick_labels)
plt.show()
评论
counts[0][0] = 1000000000000000000001
新答案
(有关我的原始答案,请参阅以下部分。
根据问题的更新,从中可以清楚地看出,应该保留的基本信息是颜色条刻度标签上的精确整数值,这是我更新的答案。其关键思想是:
- 对于颜色条刻度位置和图像数据,请使用浮点值(因为这些是 Matplotlib 唯一可以在内部处理的值;请参阅下面的原始答案)。
- 对于颜色条刻度标签,请使用给定的整数值:将它们作为已格式化字符串的列表提供给 Matplotlib(遵循此方法)。
以下是相应的代码:
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
import numpy as np
p = 13
counts = [[0 for x in range(p)] for y in range(p)]
# Provide some huge ints for demonstration purposes
counts[ 0][ 0] = 100000000000000000008
counts[ 0][-1] = counts[ 0][ 0] // 2
counts[-1][ 0] = counts[ 0][-1] // 2
counts[-1][-1] = counts[-1][ 0] // 2
# Get the unique values (without Numpy, just to be sure)
unique_counts = sorted(set(val for row in counts for val in row))
# Provide the image and tick *positions* as float values to avoid casting error
counts_img = np.array(counts, dtype=float)
counts_ticks = [float(val) for val in unique_counts]
# Provide the tick *labels* as strings generated from the original integer vals
counts_ticks_labels = [f'{val:,}' for val in unique_counts]
# Display everything
plt.imshow(counts_img, cmap='viridis', origin='lower', extent=[0, p-1, 0, p-1])
cbar = plt.colorbar(format=ScalarFormatter(useOffset=False))
cbar.set_ticks(ticks=counts_ticks, labels=counts_ticks_labels)
plt.show()
在旧版本的 Matplotlib 中,您可能需要按如下方式调整最后三行:
cbar = plt.colorbar(ticks=counts_ticks, format=ScalarFormatter(useOffset=False))
cbar.ax.set_yticklabels(counts_ticks_labels)
plt.show()
原始答案
简答
由于 Matplotlib 的内部工作依赖于 Numpy 数组来保存图像数据,我目前没有看到将巨大整数传递给 的方法。如果您可以接受近似值,请使用imshow()
counts[0][0] = float(100000000000000000000)
长答案
您看到错误的原因是,在显示之前,Matplotlib 在内部将嵌套的图像数据列表转换为 Numpy 数组。在 Matplotlib 的当前版本中,这发生在 cbook.safe_masked_invalid(
) 中,它由 _ImageBase._normalize_image_array() 调用,而 _ImageBase.set_data
() 由 Axes.imshow()
调用。
这里的问题链如下:
默认情况下,巨大的整数(即我假设不能用 Numpy 的 int_ 数据类型表示的整数)被转换为 Numpy
的
数据类型。对于使用 的数据,会发生这种情况,但不会发生这种情况。您可以按如下方式轻松检查相应的 Numpy 行为:object
counts[0][0] = 100000000000000000000
counts[0][0] = 1000000000
str(np.array([100000000000000000000]).dtype) # >>> 'object' str(np.array([1000000000]).dtype) # >>> 'int64'
如前所述,在 Matplotlib 中,这发生在
cbook.safe_masked_invalid()
中;更准确地说,它发生在 行 中,其中指的是您的嵌套列表。x = np.array(x, subok=True, copy=copy)
x
counts
之后,
_ImageBase._normalize_image_array()
检查生成的数组的数据类型是否为 either,或者是否可以转换为数据类型。对于 Numpy 的数据类型,两者都不是真的,因此会引发错误。uint8
float
object
为了避免这一连串的问题,我看到的唯一可能性是将数据转换为浮点值或浮点数组,一旦值变得太大,然后再将它们传递给 。imshow()
评论
上一个:如何替换字符串中的多行?
下一个:浮点值表示范围 [已关闭]
评论