Matplotlib在Axes3d上无法散点。

huangapple go评论71阅读模式
英文:

Matplotlib won't scatter on Axes3d

问题

在Jupyter Notebook中,当我在另一个单元格中创建的3D坐标轴对象上调用.scatter时,似乎它不会在上面散点任何东西。

具体而言,考虑以下导入语句,

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

假设以下代码块位于Jupyter Notebook的一个单元格中,

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection="3d")
ax.scatter([0], [0], [0], s=5)

然后不需要询问,图像会出现,我可以看到一个点在上面。

然而,如果

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection="3d")

ax.scatter([0], [0], [0], s=5)

在不同的单元格中,那么第二个返回<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0xblablabla>,没有图像出现,即使我后来调用plt.show()也是如此。如果我稍后调用plt.savefig("test.png"),那么将保存一个空的图像(没有绘制空轴框)。

我不理解这种行为。我希望能够连续向一个坐标轴对象添加东西,可能在不同的单元格中。

英文:

In Jupyter Notebook, it appears that when I call .scatter on a 3d axes object created in another cell, it doesn't scatter anything on it.

Concretely, consider the following import statements,

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

assuming the following block of code is in one cell of Jupyter Notebook,

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection = &quot;3d&quot;)
ax.scatter([0], [0], [0], s = 5)  

then without asking, an image appears and I can see a point on it.

However, if

 fig = plt.figure()
 ax = fig.add_subplot(1, 1, 1, projection = &quot;3d&quot;)

and

ax.scatter([0], [0], [0], s = 5) 

are in different cells, then it looks like the second returns &lt;mpl_toolkits.mplot3d.art3d.Path3DCollection at 0xblablabla&gt;, and no image appears, even if I later call plt.show(). If I later call plt.savefig(&quot;test.png&quot;), then an empty image (with no empty axis box drawn) is saved.

It don't understand this behavior. I would like to be able to successively add things to an axes object, possibly in different cells.

答案1

得分: 2

在运行一个单元格后,现代的 Jupyter 会关闭绘图对象,并在检测到单元格中存在正在构建的绘图对象时,显示这个绘图对象。这就解释了为什么在你的第二个代码块之后,你提到了这个语句:"然后不用询问,图像就出现了,我可以看到上面有一个点。" 在大多数情况下,plt.show() 不再需要,是多余的。Jupyter 检测到正在构建的 matplotlib axes 对象并将其显示出来。

接下来解释一下这两个问题:

  • "我在另一个单元格中调用了 .scatter,但它不在图上散点任何东西。"
  • "……位于不同的单元格中,看起来第二个返回 <mpl_toolkits.mplot3d.art3d.Path3DCollection at 0xblablabla>,并且没有图像显示。"

所以,如果你想像你所说的那样开始构建一个图,但没有完成它,正如你在这段代码中建议的那样:

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection="3d")

虽然你还没有在其中添加任何数据,但你已经创建了一个绘图轴对象,如果这个单元格已经导入了必要的库,Jupyter 将看到绘图轴并显示它们(如上所述)。处理这个的最简单方法是在单元格结束之前告诉 Matplotlib/Jupyter,你想关闭绘图对象。这样,该单元格中不会存在正在构建的绘图对象,也不会显示任何输出。所以,将所有这些与导入一起放在一起,你可以在一个单元格中运行一些用于绘图的初始代码,但不显示轴,只需将以下代码放在你的第一个单元格中:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection="3d")
plt.close()

(我在 回答 '如何在 Jupyter 笔记本中显示图像数据并控制其位置?' 中以类似的方式使用了 plt.close()这里 使用它允许你将绘图作为函数。)

现在,你可以在下一个单元格中添加数据到你的轴对象,并告诉 Jupyter 在下一个单元格的最后一个对象中显示它(ax.figurefig.figure 对你的示例都有效)。因此,第二个单元格将是:

ax.scatter([0], [0], [0], s=5)
ax.figure

注意,第二个单元格中仅包含 ax.scatter([0], [0], [0], s=5) 是不起作用的。你会看到类似你在帖子中报告的情况,因为返回的是一个对 scatter 的调用,如 <mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x7f9216de2860>,并且在笔记本中返回和显示的是最后一个评估的表达式的结果。相反,要将图像显示为第二个单元格的输出,你需要调用关联的图像。由于你在这里没有构建新的图像,Jupyter 不会自动显示关联的轴(查看 这里这里 了解更多信息)。


稍后将绘图保存为图像。

你提到你也在这方面有困难:

"如果稍后调用 plt.savefig("test.png"),那么将保存一个空的图像(没有空的轴框绘制)。"

你只是没有引用正确的东西。在那一点上,不再有 plt. 方法默认会操作的对象。为了更好地解释这一点,让我们首先将情况简化一下。想象一下,你将你帖子中的前两个代码块作为一个单元格运行。如上所述,与该单元格关联的对象在运行该单元格后被关闭,并且在该单元格运行后不存在。但是,你可以通过引用你的代码中分配给它们的变量来引用代码中的对象,因为你幸运地在制作它们时为它们分配了句柄。所以,你可以通过引用并添加 savefig() 方法来在事后保存关联的图形图像,像这样:

fig.savefig("test.png")

只要在之前的单元格中没有创建新的 fig 对象,也没有清除命名空间或重新启动内核,这将在许多单元格之后仍然起作用。

英文:

At conclusion of running a cell, modern Jupyter closes the plotting object and displays the plot object if it detects one being built in a cell and existing when the end of the cell is encountered.
That explains why after your second code block, this statement by you "then without asking, an image appears and I can see a point on it." plt.show() in most cases is no longer needed and is superfluous. Jupyter senses an matplotlib axes object being built and displays it.

Next addressing these two things:

  • "I call .scatter on a 3d axes object created in another cell, it doesn't scatter anything on it."
  • "...are in different cells, then it looks like the second returns <mpl_toolkits.mplot3d.art3d.Path3DCollection at 0xblablabla>, and no image appears"

So if you want to start building a plot like you say, using in an earlier cell but not complete it, as you suggest with this code:

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection = &quot;3d&quot;)

While you not adding any data in there yet, you have created a plot axis object, and so if that is a cell with the imports already previously done, the Jupyter sees the plot axes and displays them (as discussed above).
The easiest way to handle this is to tell Matplotlib/Jupyter before the end of the cell is reached that you want to close the plot object. That way there doesn't exist a plot object that had been being built in that cell and nothing is displayed as output from that cell.
So putting all that together with the imports, you can run some of the initial code for a plot in a single cell but have no axes be displayed by putting the following as your first cell:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D 
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection = &quot;3d&quot;)
plt.close()

(I use plt.close() in a similar manner in an answer to 'How can I display image data in Jupyter notebook AND control its position?
'
. Here uses it to allow you to make a plot as a function.)

Now you can put the next cell to add the data to your axes object and tell Jupyter to display it by invoking it as the last object in next cell. (ax.figure or fig.figure will both work for your example.) So the second cell would be:

ax.scatter([0], [0], [0], s = 5)
ax.figure

Note ax.scatter([0], [0], [0], s = 5) alone in the second cell doesn't work. You'll see something like you report in your post because what is returned is a call to scatter, like &lt;mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x7f9216de2860&gt;, and the result of the last expression evaluated in a notebook is returned and displayed. Instead, to show the plot as output of the second cell, you need to invoke the figure associated. Since you aren't building a new one there, Jupyter doesn't automatically display the associated axes.
(See here and here for more about that.)


Saving a plot as an image later.

You brought up you were struggling with that, too:

>" If I later call plt.savefig("test.png"), then an empty image (with no empty axis box drawn) is saved."

You just aren't referencing the right thing. There's no longer the object that plt. methods would act on, by default, at that point.
To explain this better, let's make the situation simpler first. Imagine you ran the first two code blocks in your post as a single cell. As discussed above the object associated with that cell that plt.() methods would act on got closed and doesn't exist after that cell was run. However, you can reference the assigned object from your code because fortunately you put handles on those by assigning them to variables when you made them. So you can save the associated plot figure after-the-fact by referencing it and adding the savefig() method, like so:

fig.savefig(&quot;test.png&quot;)

That will work many cells away from the earlier one as long as you didn't make a new fig object or didn't clear the namespace or restart your kernel.

huangapple
  • 本文由 发表于 2023年7月17日 23:36:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76706050.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定