使用"plotnine"库绘制3D表面图

3

问题: 使用Python库'plotnine',能否绘制交互式的3D表面图?

备用说明

  1. What I'd like to do is, under python environment, creating an interactive 3D plot with R plot grammars like we do with ggplot2 library in R. It's because I have hard time remembering grammars of matplotlib and other libraries like seaborn.

  2. An interactive 3D plot means a 3D plot that you can zoom in, zoom out, and scroll up and down, etc.

  3. It seems like only Java supported plotting libraries scuh as bokeh or plotly can create interactive 3D plots. But I want to create it with the library 'plotnine' because the library supports ggplot-like grammar, which is easy to remember.

  4. For example, can I draw a 3D surface plot like the one below with the library 'plotnine'?

    import plotly.plotly as py
    import plotly.graph_objs as go
    import pandas as pd
    
    # Read data from a csv
    z_data =
    pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/
    master/api_docs/mt_bruno_elevation.csv')
    
     data = [
            go.Surface(
            z=z_data.as_matrix()
            )]
     layout = go.Layout(
     title='Mt Bruno Elevation',
     autosize=False,
     width=500,
     height=500,
     margin=dict(
     l=65,
     r=50,
     b=65,
     t=90
       )
     )
     fig = go.Figure(data=data, layout=layout)
     py.iplot(fig, filename='elevations-3d-surface')
    
上面的代码生成了如下图所示的图形。

Image 1

你可以查看链接中的完整交互式3D表面图。
附:如果我可以用类似ggplot语法绘制交互式3D图,我们不必使用'plotnine'库。
感谢您抽出时间阅读此问题!
1个回答

2

如果您愿意扩展plotnine,那么这是可能的,但需要注意。最终代码非常简单:

(
    ggplot_3d(mt_bruno_long)
    + aes(x='x', y='y', z='height')
    + geom_polygon_3d(size=0.01)
    + theme_minimal()
)

结果如下:

enter image description here

首先,您需要将数据转换为长格式:

z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv', index_col=0)
z = z_data.values
nrows, ncols = z.shape
x, y = np.linspace(0, 1, nrows), np.linspace(0, 1, ncols)
x, y = np.meshgrid(x, y)
mt_bruno_long = pd.DataFrame({'x': x.flatten(), 'y': y.flatten(), 'height': z.flatten()})

接下来,我们需要创建一个具有第三维意识的 ggplotgeom_polygon 的等效版本。

自从编写这个答案之后,代码现在已经在 plotnine3d 包中可用,因此您可以直接使用:

from plotnine3d import ggplot_3d, geom_polygon_3d

但为了完整起见,以下是相对简单的方法:

from plotnine import ggplot, geom_polygon
from plotnine.utils import to_rgba, SIZE_FACTOR


class ggplot_3d(ggplot):
    def _create_figure(self):
        figure = plt.figure()
        axs = [plt.axes(projection='3d')]
        
        figure._themeable = {}
        self.figure = figure
        self.axs = axs
        return figure, axs
    
    def _draw_labels(self):
        ax = self.axs[0]
        ax.set_xlabel(self.layout.xlabel(self.labels))
        ax.set_ylabel(self.layout.ylabel(self.labels))
        ax.set_zlabel(self.labels['z'])


class geom_polygon_3d(geom_polygon):
    REQUIRED_AES = {'x', 'y', 'z'}

    @staticmethod
    def draw_group(data, panel_params, coord, ax, **params):
        data = coord.transform(data, panel_params, munch=True)
        data['size'] *= SIZE_FACTOR

        grouper = data.groupby('group', sort=False)
        for i, (group, df) in enumerate(grouper):
            fill = to_rgba(df['fill'], df['alpha'])
            polyc = ax.plot_trisurf(
                df['x'].values,
                df['y'].values,
                df['z'].values,
                facecolors=fill if any(fill) else 'none',
                edgecolors=df['color'] if any(df['color']) else 'none',
                linestyles=df['linetype'],
                linewidths=df['size'],
                zorder=params['zorder'],
                rasterized=params['raster'],
            )
            # workaround for https://github.com/matplotlib/matplotlib/issues/9535
            if len(set(fill)) == 1:
                polyc.set_facecolors(fill[0])

如果你想要交互性,则可以使用 matplotlib 的任何后端,我选择了 ipympl(在 jupyter notebook 单元格中输入 pip install ipympl,然后输入 %matplotlib widget)。

需要注意的是:

编辑:如果数据集不可用,这里是一个基于 matplotlib 文档的自包含示例:

import numpy as np

n_radii = 8
n_angles = 36

radii = np.linspace(0.125, 1.0, n_radii)
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]

x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())

z = np.sin(-x*y)
df = pd.DataFrame(dict(x=x,y=y,z=z))

(
    ggplot_3d(df)
    + aes(x='x', y='y', z='z')
    + geom_polygon_3d(size=0.01)
    + theme_minimal()
)

enter image description here


1
我将它们放在一个适合打印的存储库中,这样任何人都可以交互地查看它们,而无需配置系统或安装任何东西。请前往此处,单击“启动绑定器”,当会话启动时,请从可用笔记本列表中选择“通过Plotnine进行3D表面绘图”。静态视图 - Wayne
1
感谢提供绑定器!要删除 <ggplot: (8742175882381)> 字符串,您可以将 ggplot 包装在 print 中或后缀为 ;(但是 ; 似乎只在 JupyterLab 中有效?)。 - krassowski
我刚刚尝试了仅添加“;”的方法,但在该会话中可用的JupyterLab中似乎不起作用。 - Wayne

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