使用 "plotnine" 库绘制表面 3D 图
Drawing a surface 3D plot using "plotnine" library
问题:使用python库'plotnine',我们可以绘制交互式3D表面图吗?
备份说明
我想做的是,在 python 环境下,使用 R 绘图语法创建交互式 3D 绘图,就像我们在 R 中使用 ggplot2 库一样。这是因为我很难记住 matplotlib 和其他库(如 seaborn)的语法的时间。
交互式 3D 绘图是指可以放大、缩小、上下滚动等的 3D 绘图。
似乎只有 Java 支持的绘图库 scuh as bokeh 或 plotly 可以创建交互式 3D 图。但是我想用库 'plotnine' 创建它,因为该库支持类似 ggplot 的语法,这很容易记住。
例如,我可以用库 'plotnine' 绘制像下面这样的 3D 曲面图吗?
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')
上面的代码生成如下图
您可以在此 link
中查看完整的交互式 3D 表面图
p.s。如果我可以使用类似 ggplot 的语法绘制交互式 3D 绘图,那么它不一定是我们应该使用的 'plotnine' 库。
感谢您花时间阅读这个问题!
如果您愿意稍微扩展 plotnine,这是可能的,并且需要注意。最终代码很简单:
(
ggplot_3d(mt_bruno_long)
+ aes(x='x', y='y', z='height')
+ geom_polygon_3d(size=0.01)
+ theme_minimal()
)
结果:
首先,您需要将数据转换为长格式:
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()})
然后,我们需要为 ggplot
和 geom_polygon
创建等同于第三维度的意识:
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
(pip install ipympl
,然后在 jupyter notebook 单元格中使用 %matplotlib widget
)。
注意事项是:
- 虽然着色效果很好,但
plot_trisurf
不能很好地处理 facecolors
(有一个 PR 可以修复它 here)
- 您可能想要添加一个允许禁用阴影的参数,请参阅 matplotlib 3D shading examples
- 分面、翻转轴等如果不进一步摆弄将无法工作 - 然而,这可以在未来解决,如 this plotnine issue about bringing 3D plots to plotnine.
中所讨论
编辑:如果数据集变得不可用,这里有一个基于 matplotlib 文档的 self-contained 示例:
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()
)
问题:使用python库'plotnine',我们可以绘制交互式3D表面图吗?
备份说明
我想做的是,在 python 环境下,使用 R 绘图语法创建交互式 3D 绘图,就像我们在 R 中使用 ggplot2 库一样。这是因为我很难记住 matplotlib 和其他库(如 seaborn)的语法的时间。
交互式 3D 绘图是指可以放大、缩小、上下滚动等的 3D 绘图。
似乎只有 Java 支持的绘图库 scuh as bokeh 或 plotly 可以创建交互式 3D 图。但是我想用库 'plotnine' 创建它,因为该库支持类似 ggplot 的语法,这很容易记住。
例如,我可以用库 'plotnine' 绘制像下面这样的 3D 曲面图吗?
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')
上面的代码生成如下图
您可以在此 link
中查看完整的交互式 3D 表面图p.s。如果我可以使用类似 ggplot 的语法绘制交互式 3D 绘图,那么它不一定是我们应该使用的 'plotnine' 库。
感谢您花时间阅读这个问题!
如果您愿意稍微扩展 plotnine,这是可能的,并且需要注意。最终代码很简单:
(
ggplot_3d(mt_bruno_long)
+ aes(x='x', y='y', z='height')
+ geom_polygon_3d(size=0.01)
+ theme_minimal()
)
结果:
首先,您需要将数据转换为长格式:
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()})
然后,我们需要为 ggplot
和 geom_polygon
创建等同于第三维度的意识:
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
(pip install ipympl
,然后在 jupyter notebook 单元格中使用 %matplotlib widget
)。
注意事项是:
- 虽然着色效果很好,但
plot_trisurf
不能很好地处理facecolors
(有一个 PR 可以修复它 here) - 您可能想要添加一个允许禁用阴影的参数,请参阅 matplotlib 3D shading examples
- 分面、翻转轴等如果不进一步摆弄将无法工作 - 然而,这可以在未来解决,如 this plotnine issue about bringing 3D plots to plotnine. 中所讨论
编辑:如果数据集变得不可用,这里有一个基于 matplotlib 文档的 self-contained 示例:
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()
)