Python 如何在子图中绘制多个 Seaborn Jointplot
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35042255/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to plot multiple Seaborn Jointplot in Subplot
提问by Afloz
I'm having problem placing Seaborn Jointplot
inside a multicolumn subplot
.
我在将 SeabornJointplot
放入 multicolumn 时遇到问题subplot
。
import pandas as pd
import seaborn as sns
df = pd.DataFrame({'C1': {'a': 1,'b': 15,'c': 9,'d': 7,'e': 2,'f': 2,'g': 6,'h': 5,'k': 5,'l': 8},
'C2': {'a': 6,'b': 18,'c': 13,'d': 8,'e': 6,'f': 6,'g': 8,'h': 9,'k': 13,'l': 15}})
fig = plt.figure();
ax1 = fig.add_subplot(121);
ax2 = fig.add_subplot(122);
sns.jointplot("C1", "C2", data=df, kind='reg', ax=ax1)
sns.jointplot("C1", "C2", data=df, kind='kde', ax=ax2)
Notice how only a portion of the jointplot
is placed inside the subplot and the rest left inside another two plot frames. What I'd want is to have both the distributions
also inserted inside the subplots
.
请注意如何仅将一部分jointplot
放置在子图内,而其余部分则留在另外两个图框中。我想要的是distributions
同时插入subplots
.
Can anyone help with this?
有人能帮忙吗?
回答by CT Zhu
It can not be easily done without hacking. jointplot
calls JointGrid
method, which in turn creates a new figure
object every time it is called.
如果不进行黑客攻击,就无法轻松完成。 jointplot
callJointGrid
方法,该方法figure
每次调用时都会创建一个新对象。
Therefore, the hack is to make two jointplots (JG1
JG2
), then make a new figure, then migrate the axes objects from JG1
JG2
to the new figure created.
因此,hack 是制作两个联合图 ( JG1
JG2
),然后制作一个新图形,然后将轴对象从中迁移JG1
JG2
到创建的新图形。
Finally, we adjust the sizes and the positions of subplots in the new figure we just created.
最后,我们在刚刚创建的新图形中调整子图的大小和位置。
JG1 = sns.jointplot("C1", "C2", data=df, kind='reg')
JG2 = sns.jointplot("C1", "C2", data=df, kind='kde')
#subplots migration
f = plt.figure()
for J in [JG1, JG2]:
for A in J.fig.axes:
f._axstack.add(f._make_key(A), A)
#subplots size adjustment
f.axes[0].set_position([0.05, 0.05, 0.4, 0.4])
f.axes[1].set_position([0.05, 0.45, 0.4, 0.05])
f.axes[2].set_position([0.45, 0.05, 0.05, 0.4])
f.axes[3].set_position([0.55, 0.05, 0.4, 0.4])
f.axes[4].set_position([0.55, 0.45, 0.4, 0.05])
f.axes[5].set_position([0.95, 0.05, 0.05, 0.4])
It is a hack because we are now using _axstack
and _add_key
private methods, which might and might not stay the same as they are now in matplotlib
future versions.
这是一个黑客,因为我们现在使用_axstack
和_add_key
私有方法,这可能会,可能不会保持不变,因为他们现在是在matplotlib
未来的版本。
回答by ImportanceOfBeingErnest
Moving axes in matplotlib is not as easy as it used to be in previous versions. The below is working with the current version of matplotlib.
在 matplotlib 中移动轴不像以前的版本那么容易。下面是使用当前版本的 matplotlib。
As has been pointed out at several places (this question, also this issue) several of the seaborn commands create their own figure automatically. This is hardcoded into the seaborn code, so there is currently no way to produce such plots in existing figures. Those are PairGrid
, FacetGrid
, JointGrid
, pairplot
, jointplot
and lmplot
.
正如在几个地方(这个问题,也是这个问题)所指出的,一些seaborn 命令会自动创建自己的图形。这被硬编码到 seaborn 代码中,因此目前无法在现有图形中生成此类图。这些都是PairGrid
,FacetGrid
,JointGrid
,pairplot
,jointplot
和lmplot
。
There is a seaborn fork availablewhich would allow to supply a subplot grid to the respective classes such that the plot is created in a preexisting figure. To use this, you would need to copy the axisgrid.py
from the fork to the seaborn folder. Note that this is currently restricted to be used with matplotlib 2.1 (possibly 2.0 as well).
有一个seaborn fork 可用,它允许为相应的类提供子图网格,以便在预先存在的图形中创建图。要使用此功能,您需要axisgrid.py
将 fork 中的复制到 seaborn 文件夹中。请注意,这目前仅限于与 matplotlib 2.1(也可能是 2.0)一起使用。
An alternative could be to create a seaborn figure and copy the axes to another figure. The principle of this is shown in this answerand could be extended to Searborn plots. The implementation is a bit more complicated that I had initially expected. The following is a class SeabornFig2Grid
that can be called with a seaborn grid instance (the return of any of the above commands), a matplotlib figure and a subplot_spec
, which is a position of a gridspec
grid.
另一种方法是创建一个 seaborn 图形并将轴复制到另一个图形。此答案中显示了这一原理,并且可以扩展到 Searborn 图。实现比我最初预期的要复杂一些。下面是一个SeabornFig2Grid
可以使用 seaborn 网格实例(上述任何命令的返回)、一个 matplotlib 图和 a 调用的类subplot_spec
,它是gridspec
网格的位置。
Note: This is a proof of concept, it may work for most easy cases, but I would not recommend using it in production code.
注意:这是一个概念证明,它可能适用于大多数简单的情况,但我不建议在生产代码中使用它。
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import seaborn as sns
import numpy as np
class SeabornFig2Grid():
def __init__(self, seaborngrid, fig, subplot_spec):
self.fig = fig
self.sg = seaborngrid
self.subplot = subplot_spec
if isinstance(self.sg, sns.axisgrid.FacetGrid) or \
isinstance(self.sg, sns.axisgrid.PairGrid):
self._movegrid()
elif isinstance(self.sg, sns.axisgrid.JointGrid):
self._movejointgrid()
self._finalize()
def _movegrid(self):
""" Move PairGrid or Facetgrid """
self._resize()
n = self.sg.axes.shape[0]
m = self.sg.axes.shape[1]
self.subgrid = gridspec.GridSpecFromSubplotSpec(n,m, subplot_spec=self.subplot)
for i in range(n):
for j in range(m):
self._moveaxes(self.sg.axes[i,j], self.subgrid[i,j])
def _movejointgrid(self):
""" Move Jointgrid """
h= self.sg.ax_joint.get_position().height
h2= self.sg.ax_marg_x.get_position().height
r = int(np.round(h/h2))
self._resize()
self.subgrid = gridspec.GridSpecFromSubplotSpec(r+1,r+1, subplot_spec=self.subplot)
self._moveaxes(self.sg.ax_joint, self.subgrid[1:, :-1])
self._moveaxes(self.sg.ax_marg_x, self.subgrid[0, :-1])
self._moveaxes(self.sg.ax_marg_y, self.subgrid[1:, -1])
def _moveaxes(self, ax, gs):
#https://stackoverflow.com/a/46906599/4124317
ax.remove()
ax.figure=self.fig
self.fig.axes.append(ax)
self.fig.add_axes(ax)
ax._subplotspec = gs
ax.set_position(gs.get_position(self.fig))
ax.set_subplotspec(gs)
def _finalize(self):
plt.close(self.sg.fig)
self.fig.canvas.mpl_connect("resize_event", self._resize)
self.fig.canvas.draw()
def _resize(self, evt=None):
self.sg.fig.set_size_inches(self.fig.get_size_inches())
The usage of this class would look like this:
此类的用法如下所示:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import seaborn as sns; sns.set()
import SeabornFig2Grid as sfg
iris = sns.load_dataset("iris")
tips = sns.load_dataset("tips")
# An lmplot
g0 = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
palette=dict(Yes="g", No="m"))
# A PairGrid
g1 = sns.PairGrid(iris, hue="species")
g1.map(plt.scatter, s=5)
# A FacetGrid
g2 = sns.FacetGrid(tips, col="time", hue="smoker")
g2.map(plt.scatter, "total_bill", "tip", edgecolor="w")
# A JointGrid
g3 = sns.jointplot("sepal_width", "petal_length", data=iris,
kind="kde", space=0, color="g")
fig = plt.figure(figsize=(13,8))
gs = gridspec.GridSpec(2, 2)
mg0 = sfg.SeabornFig2Grid(g0, fig, gs[0])
mg1 = sfg.SeabornFig2Grid(g1, fig, gs[1])
mg2 = sfg.SeabornFig2Grid(g2, fig, gs[3])
mg3 = sfg.SeabornFig2Grid(g3, fig, gs[2])
gs.tight_layout(fig)
#gs.update(top=0.7)
plt.show()
Note that there might be several drawbacks from copying axes and the above is not (yet) tested thoroughly.
请注意,复制轴可能有几个缺点,并且上述内容尚未(尚未)彻底测试。
回答by Sun
Here is my solution, you can consider overwriting class method. No need to copy and paste axes.
这是我的解决方案,您可以考虑覆盖类方法。无需复制和粘贴轴。
import seaborn as sns
class myjoint(sns.JointGrid):
def __init__(self, x, y, data=None,height=6, ratio=5, space=.2,
dropna=True, xlim=None, ylim=None, size=None):
super(myjoint, self).__init__(x, y, data,height, ratio, space,
dropna, xlim, ylim, size)
plt.close(2)
# Set up the subplot grid
self.ax_joint = f.add_subplot(gs[1:, :-1])
self.ax_marg_x = f.add_subplot(gs[0, :-1], sharex=self.ax_joint)
self.ax_marg_y = f.add_subplot(gs[1:, -1], sharey=self.ax_joint)
# Turn off tick visibility for the measure axis on the marginal plots
plt.setp(self.ax_marg_x.get_xticklabels(), visible=False)
plt.setp(self.ax_marg_y.get_yticklabels(), visible=False)
Now you will be able to plot multi seaborn jointplot:
现在您将能够绘制多seaborn联合图:
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
import pandas as pd
tips = sns.load_dataset("tips")
ratio = 3
f = plt.figure(figsize=(10,10))
outer_grid = gridspec.GridSpec(2, 2, wspace=0.3, hspace=0.3)
weekdays = ["Sun","Sat","Thur","Fri"]
for i,weekday in enumerate(weekdays):
tips[tips["day"]==weekday]
gs = gridspec.GridSpecFromSubplotSpec(ratio+1, ratio+1,
subplot_spec=outer_grid[i], wspace=0.3, hspace=0.3)
g = myjoint(x="total_bill", y="tip", data=tips, ratio=ratio)
g = g.plot(sns.regplot, sns.distplot)
f.tight_layout()
You can adjust the plot behavior in the above code clip, pay attention, the default style of seaborn is not suitable for publications (they don't have digits showing on the probability density plot). Hope this can be helpful to students in MLES class as well.
您可以在上面的代码片段中调整绘图行为,注意,seaborn 的默认样式不适用于出版物(它们在概率密度图上没有显示数字)。希望这也能对 MLES 班的学生有所帮助。
回答by Pedro Herruzo
If you get into trouble despite the elegant solution of @ImportanceOfBeingErnest, you can still save seaborn plots to memory as images and use them to build your custom figure. Use other formats than '.png' if you seek a higher resolution.
如果您在@ImportanceOfBeingErnest 的优雅解决方案中遇到了麻烦,您仍然可以将seaborn 图作为图像保存到内存中,并使用它们来构建您的自定义图形。如果您寻求更高的分辨率,请使用“.png”以外的其他格式。
Here is the example is shown above using this nasty (but working) approach:
这是上面使用这种讨厌(但有效)方法显示的示例:
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import seaborn as sns
# data
iris = sns.load_dataset("iris")
tips = sns.load_dataset("tips")
############### 1. CREATE PLOTS
# An lmplot
g0 = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
palette=dict(Yes="g", No="m"))
# A PairGrid
g1 = sns.PairGrid(iris, hue="species")
g1.map(plt.scatter, s=5)
# A FacetGrid
g2 = sns.FacetGrid(tips, col="time", hue="smoker")
g2.map(plt.scatter, "total_bill", "tip", edgecolor="w")
# A JointGrid
g3 = sns.jointplot("sepal_width", "petal_length", data=iris,
kind="kde", space=0, color="g")
############### 2. SAVE PLOTS IN MEMORY TEMPORALLY
g0.savefig('g0.png')
plt.close(g0.fig)
g1.savefig('g1.png')
plt.close(g1.fig)
g2.savefig('g2.png')
plt.close(g2.fig)
g3.savefig('g3.png')
plt.close(g3.fig)
############### 3. CREATE YOUR SUBPLOTS FROM TEMPORAL IMAGES
f, axarr = plt.subplots(2, 2, figsize=(25, 16))
axarr[0,0].imshow(mpimg.imread('g0.png'))
axarr[0,1].imshow(mpimg.imread('g1.png'))
axarr[1,0].imshow(mpimg.imread('g3.png'))
axarr[1,1].imshow(mpimg.imread('g2.png'))
# turn off x and y axis
[ax.set_axis_off() for ax in axarr.ravel()]
plt.tight_layout()
plt.show()