IPython Notebook如何水平排列图表

28

目前,在ipython笔记本中创建两个连续的绘图时,它们会一个接一个地显示:

enter image description here

我想知道是否有任何方法可以将它们显示在行中,直到窗口的空间用尽为止。因此,对于前两个绘图,输出将如下所示:

enter image description here

我意识到我可以通过在网格中排列子图来做类似的事情,但我想知道是否可能自动完成,以便在空间用尽时将绘图包装到下一行中?

5个回答

12

这在Python 3.5和Jupyter 4.4.0中适用。

当你调整浏览器窗口大小以填充水平空间时,图表会"随之流动"。

图表也可以是不同的大小(尝试替换figsize=(3+i/3,2+i/4) - 查看下面的第二张图片)

(刚刚意识到这个问题有多久了; 我定期寻找同样的东西。我承认代码是从其他网页示例拼凑而来的,但现在我已经找不到参考资料了)

import matplotlib.pyplot as plt
import numpy as np
from IPython.display import HTML
import io
import base64


class FlowLayout(object):
    ''' A class / object to display plots in a horizontal / flow layout below a cell '''
    def __init__(self):
        # string buffer for the HTML: initially some CSS; images to be appended
        self.sHtml =  """
        <style>
        .floating-box {
        display: inline-block;
        margin: 10px;
        border: 3px solid #888888;  
        }
        </style>
        """

    def add_plot(self, oAxes):
        ''' Saves a PNG representation of a Matplotlib Axes object '''
        Bio=io.BytesIO() # bytes buffer for the plot
        fig = oAxes.get_figure()
        fig.canvas.print_png(Bio) # make a png of the plot in the buffer

        # encode the bytes as string using base 64 
        sB64Img = base64.b64encode(Bio.getvalue()).decode()
        self.sHtml+= (
            '<div class="floating-box">'+ 
            '<img src="data:image/png;base64,{}\n">'.format(sB64Img)+
            '</div>')

    def PassHtmlToCell(self):
        ''' Final step - display the accumulated HTML '''
        display(HTML(self.sHtml))




oPlot = FlowLayout() # create an empty FlowLayout

# Some fairly regular plotting from Matplotlib
gX = np.linspace(-5,5,100) # just used in the plot example
for i in range(10): # plot 10 charts
    fig, ax = plt.subplots(1, 1, figsize=(3,2)) # same size plots
                           # figsize=(3+i/3,2+i/4)) # different size plots
    ax.plot(gX, np.sin(gX*i)) # make your plot here
    oPlot.add_plot(ax) # pass it to the FlowLayout to save as an image
    plt.close() # this gets rid of the plot so it doesn't appear in the cell


oPlot.PassHtmlToCell()

Charts in Flow Layout

Plots of varying size


10

是的,您可以这样做:

In [3]: import numpy as np
   ...: xs = np.linspace(0,100,100)
   ...: fig, axs = plt.subplots(nrows=1, ncols=2)
   ...: axs[0].plot(xs, xs * xs)
   ...: axs[1].plot(xs, np.sqrt(xs))
   ...: 

输出: enter image description here

5
这个方法确实可行,但我的目的是避免指定要将其放入哪一列。这是为了在调整窗口大小时可以重新排列图表。 - juniper-
1
此外,这会导致图形缩小,因为它们现在是一个更大的图形的子图,该图形具有任何默认大小。如果可以不考虑要放置多少个图形,就可以将它们排列在原地,那就更好了。 - Richard Rast

6

因为这个问题提出已经有一段时间了,所以我将为那些通过搜索引擎找到这个问题的人提供一个更新的解决方案。

由于Jupyter基本取代了IPython笔记本,而Jupyter Lab计划取代Jupyter,所以我的解决方案是在Jupyter Lab 0.35.4中实现的。

在这个环境中,我发现评估读取单元格的内容非常有用:

%%html

<style>
    .jp-OutputArea-child {
        display: inline-block;
    }
</style>

这样可以更好地利用垂直空间,通过允许输出区域(特别是绘图)并排浮动。我希望更精细的变体成为默认行为。
目前浏览器不支持CSS父选择器,但根据选择器4级工作草案,如果浏览器开始支持,最好使用以下内容:
%%html

<style>
    .jp-OutputArea-child:has(> .jp-RenderedImage) {
        display: inline-block;
    }
</style>

2
你能详细说明一下吗?我不太清楚它是怎么工作的。在笔记本的开始时候,我需要执行一次吗? - gosuto
是的,我的许多笔记本都在顶部附近有一个单独的独立单元格,其中包含指定的内容。每当我评估该单元格时,它会导致先前创建和未来的绘图并排浮动。 - Steven Noyce
这个提议的解决方案对Colab没有影响。图形仍然像以前一样垂直排列,而不是水平排列。 - sh37211
第二个代码片段在我的 Jupyter Lab 版本 3.0.16 下无法工作(再次?或者只是)。而第一个代码片段确实可以工作!在这里使用的是 Firefox 75.0(64 位)。 - Nikos Alexandris
1
@NikosAlexandris 谢谢你尝试!正如答案中所述,第一个片段现在应该可以工作(就像你看到的那样),而如果浏览器开始支持CSS Level 4选择器,则第二个片段可能会更好地工作。因此,不是第二个片段曾经可以工作,而是它最终应该可以工作。 :) - Steven Noyce
显示剩余2条评论

1
现在这个工作使用 ipywidgets
# Select ipywidget output for matplotlib
%matplotlib widget

import matplotlib.pyplot as plt
import ipywidgets
import numpy as np

xs = np.linspace(0, 100, 100)

# Capture each plot in an Output object instead of displaying outright
output1 = ipywidgets.Output()
with output1:
    # Restrict the size, otherwise the full horizontal space will be taken
    fig, axes = plt.subplots(constrained_layout=True, figsize=(4, 3))
    fig.canvas.toolbar_position = 'bottom'
    axes.plot(xs, xs * xs)
    
output2 = ipywidgets.Output()
with output2:
    fig, axes = plt.subplots(constrained_layout=True, figsize=(4, 3))
    fig.canvas.toolbar_position = 'bottom'
    axes.plot(xs, np.sqrt(xs))

output3 = ipywidgets.Output()
with output3:
    fig, axes = plt.subplots(constrained_layout=True, figsize=(4, 3))
    fig.canvas.toolbar_position = 'bottom'
    axes.plot(xs, xs)

现在您可以使用具有换行布局的HBox来将图形并排显示并换行:

Using HBox to display figures side by side

请查看https://kapernikov.com/ipywidgets-with-matplotlib/以获取更多与matplotlib相关的示例,以及https://stackoverflow.com/a/60174258/450068关于使用flex wrap的说明!


0

我相信没有办法在不使用subplot或grid plot方法的情况下实现。至于图表大小,您可以使用类似于ax[0].axes([.65, .6, .2, .2])plt.subplots(1, 2, figuresize=(12,4))的方式指定它们的大小。不幸的是,这就是matplotlib的设置方式。如果您使用解释器,您可以调整GUI的大小。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接