在matplotlib中创建堆叠的圆柱条形图
matplotlib 可以绘制如下图所示的堆积圆柱条形图吗?如果只有一根吧怎么办?
Can matplotlib draw a stacked cylinder bar plot like the one below? How about if it has only one bar?
如果不是,另一种选择就是通常的堆叠条形图,条形边缘为圆角——这可能吗?
If not, another option would be just a usual stacked bar plot with the bars having rounded edges - is that possible?
我搜索了 matplotlib 库和 bar()
文档,但找不到可以做的事情.
I searched the matplotlib gallery and bar()
documentation, but couldn't find something to do it.
我不知道直接函数来绘制堆积的圆柱条形图,我也不认为有任何简单的解决方法.问题在于它既不是真正的2D也不是3D.
I don't know of a direct function to plot a stacked cylinder bar plot and I don't think there is an easy workaround either. The problem is that it is neither really 2D or 3D.
使用 Matplotlib,您必须使 2D 看起来像 3D.这意味着您必须制作一个圆柱形状.为了让它看起来不错,你可能还需要一个纹理来提供阴影的外观.
With Matplotlib you have to make 2D look like 3D. This means you have to make a cylinder shape. To make it look good you'll probably need a texture as well to give the appearance of shadow.
mplot3d是Matplotlib的3D扩展,我用它来制作下面的图.我认为它看起来有点太 3D.顶部看起来有点扭曲,整个情节成一个角度...... mplot3d 使用起来也有点痛苦.要花费很多时间才能使圆柱体看起来不错.该代码不是很完善,但我确实对其进行了注释.
mplot3d is a 3D extension of Matplotlib and I used it to make the plot below. I think it looks a bit too 3D. The top looks a bit distorted and the whole plot is at an angle... mplot3d is also a bit of a pain to work with. It takes quite some effort to make the cylinder look nice. The code is not very polished, but I did annotate it.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from mpl_toolkits.mplot3d import Axes3D
import numpy
import matplotlib
import matplotlib.pyplot as plt
def plot_cylinder_element(x, z, dz, rx = 5, ry = 5, color = "b"):
"""
x: left, right
z: start height
dz: height of cylinder
rx, ry = radius of width (x) and depth (y)
color = color
Inspired by:
http://matplotlib.1069221.n5.nabble.com/plot-surface-shading-and-clipping-error-td14031.html
"""
N = 100 # number of elements
# a lower stride will give more faces. A cylinder with 4 faces is a cube :)
# I think with N=100 and rstride=2, it will have 50 faces
# cstride is the height, rstride the circle
cstride_side = 1000 # only 1 element needed
rstride_side = 1 # many elements to make a nice cylinder shape
cstride_top = 10
rstride_top = 10
# parameters of cylinder
phi = numpy.linspace(0, 2 * numpy.pi, N)
_r = numpy.ones(N)
_h = numpy.linspace(0, 1, N)
# cylinder
_x = rx * numpy.outer(numpy.cos(phi), _r) + x
_y = ry * numpy.outer(numpy.sin(phi), _r)
_z = dz * numpy.outer(numpy.ones(numpy.size(_r)), _h) + z
ax.plot_surface(_x, _y, _z, rstride = rstride_side, cstride = cstride_side, linewidth = 0, alpha = 1, color = color)
# to cover the gaps between the faces, plot the cylinder again at a slightly smaller radius
_x *= 0.99
_y *= 0.99
ax.plot_surface(_x, _y, _z, rstride = rstride_side + 1, cstride = cstride_side + 1, linewidth=0, alpha=1, color = color)
# top
_x = rx * numpy.outer(numpy.cos(phi), _h) + x
_y = ry * numpy.outer(numpy.sin(phi), _h)
_z = numpy.zeros([N,N]) + z + dz + 0.1
ax.plot_surface(_x, _y, _z, rstride = rstride_top, cstride = cstride_top, linewidth = 0, alpha = 1, color = color)
# plot again with different stride to mask the gaps
ax.plot_surface(_x, _y, _z, rstride = rstride_side + 1, cstride = cstride_side + 1, linewidth=0, alpha=1, color = color)
def plot_cylinder(x, z, rx = 5, ry = 5):
"""
x: left-right for each cylinder
z: list height difference (ie. not cumulative)
"""
# list with colors
colors = ["b", "g", "r", "c", "y", "k"]
# plot cylinder elements
_z = 0
for i in range(len(z)):
plot_cylinder_element(x, _z, z[i], rx = rx, ry = ry, color = colors[i % len(colors)])
_z += z[i]
def cylinder_plot(z, r = 10, dr = 30):
"""
z: list of different cylinders with for each a list height difference (ie. not cumulative)
r: radius
dr: distance between cylinders
"""
# different cylinders next to each other
x = numpy.arange(len(z)) * dr
# possible difference between width (x) and depth (y)
rx = r
ry = r
# make cylinders
for i in range(len(z)):
plot_cylinder(x[i], z[i], rx = rx, ry = ry)
# close earlier plots
plt.close("all")
# make figure
fig = plt.figure()
ax = Axes3D(fig)
# set 3D-view
ax.view_init(elev = 10, azim = 280)
# make 3 cylinders, with a different number of elements
cylinder_plot([[5, 10, 5], [3, 5], [1,2,3,4]])
# set the labels
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# show
plt.show()