Open anhqle opened 7 years ago
You cannot draw subplots. It will be implemented when Matplotlib gets a better layout manager. At the moment, it would be hard to implement.
Thanks for the quick reply! Is there not even any workaround, such as assigning plotnine plots to variables and print them out in a grid?
There is no work around. Subplot layouts in Matplotlib are not a flexible as they could be, the solution is a better layout manager.
@has2k1 I've been working on the layout manager, but I'm not clear what flexibility you are looking for. Since I'm looking for use-cases, can you elaborate (either here or in https://github.com/matplotlib/matplotlib/issues/1109)? Thanks!
@jklymak, you declared your interest in tacking the layout issue within a day of me finally fiddling with Tillstens examples and trying to figure out what it would take to come up with a workable MEP. What I suggested as a plausible way forward has at the minimum (points 1 and 2) the flexibility that would be useful to plotnine.
From the your updates in that thread, I got more assured that the direction you were taking had the main concerns of this project covered. Nonetheless, I intended on working on top of your exploration to make sure of that. That would allow me to raise more substantive concerns. So far I have been reluctant to do that as I think the code you have is still in a lot of flux.
I will create a notebook to track the requirements of this project and test implementations using the constraint layout.
@has2k1
Sure - you can check out my draft MEP at https://github.com/jklymak/mplconstrainedlayout/blob/master/DraftMEP.ipynb
Its pretty rudimentary. OTOH, I've actually implemented most of it.
Is the layout manager in matplotlib v2.2 sufficient for proper subplots in plotnine?
@gokceneraslan, the layout manager is still experimental and I expect it to be lacking in some ways, the solution to which is to use it in an experimental branch in plotnine and make upstream contributions where necessary.
Sorry to wade in on this but this is the main piece of ggplot2 functionality I'm missing with plotnine and I'm using a lot more python these days. Is there any progress on this or is it still blocked by the matplotlib layout manager?
There is now an experimental constraint layout manager in Matplotlib. It is not yet ready for prime time.
What is missing from the layout manager?
@jklymak, These are my notes since when it was merged.
I have not yet followed up on them.
once there is a viable plotting manager, I find https://github.com/wilkelab/cowplot indispensable when working with ggplot in R so you might take some inspiration from that project for a feature set.
@dschneiderch, such a plotting manager would be very helpful, but it is hard to build since matplotlib is not made of nice components that can be easily moved around.
That's too bad. It seems though that some basic arranging should be possible with matplotlib.gridspec, no? I was looking over these answers at SO, but admittedly haven't quite figured it out yet. https://stackoverflow.com/questions/34028255/set-height-and-width-of-figure-created-with-plt-subplots-in-matplotlib https://stackoverflow.com/questions/47535866/how-to-iteratively-populate-matplotlib-gridspec-with-a-multipart-seaborn-plot
Thanks for those links. Yes, the basic stuff would be possible with gridspec and then the rest next to impossible. The problem is with plot labels, facets and legends i.e the items that fall outside the main plot area.
I will try to wrap my head around the axis copying technique together with gridspecs and see how much mileage we can get from that.
Any updates on this issue and the layout manager?
Any updates on this issue and the layout manager?
Such a layout manager is not possible, someone familiar with Matplotlib way more than me confirmed. So we have to find a definitely lesser (but still hard) solution.
Such a feature would be hugely helpful, given how much people use cowplot/gridExtra for R ggplot2. I understand that it's hard...
@ponnhide, Thanks for effort. I will certainly look at it more carefully. I thought about something similar (encapsulating the axes ...) but I had no idea how well it could work. The results are promising.
Thank you @ponnhide and @has2k1 for your amazing work. A friend and I are actually following this issue closely, and plan to switch our analysis workflow from R to Python once something like patchworklib is implemented for plotnine!
(We're academics and often need to make complex multi-panel figures for papers)
@ponnhide i've been following this thread since Aug 2018. Thank you so much for this contribution! I am looking forward to playing with this next week...
Hi, I'm the developer of patchworklib. This package provides the function to arrange multiple plotnine, matplotlib, and seaborn plots quickly using only the |
and /
as the patchwork of the R library.
When I posted here previously, I realized the package still had many bugs and problems, so I removed my posts once. Now, in most cases, patchworklib can properly handle plotnine plots as subplots, and you can align them with other plotnine, matplotlib, and seaborn plots. The following code is the simple example script to arrange multiple plotnine plots using patchworklib.
import patchworklib as pw
from plotnine import *
from plotnine.data import *
g1 = (ggplot(mtcars) + geom_point(aes("mpg", "disp")))
g2 = (ggplot(mtcars) + geom_boxplot(aes("gear", "disp", group="gear")))
g3 = (ggplot(mtcars, aes('wt', 'mpg', color='factor(gear)')) + geom_point() + stat_smooth(method='lm') + facet_wrap('~gear'))
g4 = (ggplot(data=diamonds) + geom_bar(mapping=aes(x="cut", fill="clarity"), position="dodge"))
g1 = pw.load_ggplot(g1, figsize=(2,3))
g2 = pw.load_ggplot(g2, figsize=(2,3))
g3 = pw.load_ggplot(g3, figsize=(3,3))
g4 = pw.load_ggplot(g4, figsize=(5,2))
g1234 = (g1|g2|g3)/g4
g1234.savefig()
If you are interested in patchworklib, please try to use the following example codes on Google colab.
If you face some problems, please let me know.
Hi there,
Super appreciative of plotnine
and impressed by the work done in patchworklib
. @wangmallory and I have also started working on a python package similar to cowplot
and gridExtra
(though we also plan to do some patchwork
stuff as well). In the backend we convert everything to svg objects (which may be seen a a bit little more hacky) but does means we avoid some limitations of matplotlib
.
We just completed our first MVP and welcome people also checking the package (documentation can be found here).
Here’s a following example of simple approach
import cowpatch as cow #our package
import plotnine as p9
import plotnine.data as p9_data
import numpy as np
# creation of some some ggplot objects
g0 = p9.ggplot(p9_data.mpg) +\
p9.geom_bar(p9.aes(x="hwy")) +\
p9.labs(title = 'Plot 0')
g1 = p9.ggplot(p9_data.mpg) +\
p9.geom_point(p9.aes(x="hwy", y = "displ")) +\
p9.labs(title = 'Plot 1')
g2 = p9.ggplot(p9_data.mpg) +\
p9.geom_point(p9.aes(x="hwy", y = "displ", color="class")) +\
p9.labs(title = 'Plot 2')
vis_patch = cow.patch(g0, g1, g2)
vis_patch += cow.layout(design = np.array([[0,1],
[0,2]]),
rel_heights = [1,2])
vis_patch.show(width = 11, height = 7)
Please let me report here. I have now updated patchworklib to support plotnine v0.9.0 and the inheritance of the plotnine theme (The previous version of patchworklib cannot handle style settings of plotnine properly). Now, you can freely arrange multiple plotnine plots using patchworklib as follows.
import patchworklib as pw
from plotnine import *
from plotnine.data import *
g1 = pw.load_ggplot(ggplot(mtcars)
+ geom_point(aes("mpg", "disp"))
+ theme(text=element_text(size=14), axis_title=element_text(size=18)),
figsize=(2,3))
g2 = pw.load_ggplot(ggplot(mtcars)
+ geom_boxplot(aes("gear", "disp", group="gear"))
+ theme(text=element_text(size=14), axis_title=element_text(size=18)),
figsize=(2,3))
g3 = pw.load_ggplot(ggplot(mtcars, aes('wt', 'mpg', color='factor(gear)'))
+ geom_point() + stat_smooth(method='lm')
+ facet_wrap('~gear')
+ theme(text=element_text(size=14), axis_title=element_text(size=18)),
figsize=(3,3))
g4 = pw.load_ggplot(ggplot(data=diamonds)
+ geom_bar(mapping=aes(x="cut", fill="clarity"), position="dodge")
+ theme(text=element_text(size=14), axis_title=element_text(size=20, color="blue"),
legend_text=element_text(size=14), legend_title=element_text(size=18)),
figsize=(5,2))
g1234 = (g1|g2|g3)/g4
g1234.savefig()
Furthermore, by using the newest version of patchworklib and plotnine, you can draw a scatter plot with marginal distributions as follows.
import patchworklib as pw
from plotnine import *
from plotnine.data import *
g1 = pw.load_ggplot(ggplot(mpg, aes(x='cty', color='drv', fill='drv')) +
geom_density(aes(y=after_stat('count')), alpha=0.1) +
scale_color_discrete(guide=False) +
theme(axis_ticks_major_x=element_blank(),
axis_text_x =element_blank(),
axis_title_x=element_blank(),
axis_text_y =element_text(size=12),
axis_title_y=element_text(size=14),
legend_position="none"),
figsize=(4,1))
g2 = pw.load_ggplot(ggplot(mpg, aes(x='hwy', color='drv', fill='drv')) +
geom_density(aes(y=after_stat('count')), alpha=0.1) +
coord_flip() +
theme(axis_ticks_major_y=element_blank(),
axis_text_y =element_blank(),
axis_title_y=element_blank(),
axis_text_x =element_text(size=12),
axis_title_x=element_text(size=14)
),
figsize=(1,4))
g3 = pw.load_ggplot(ggplot(mpg) +
geom_point(aes(x="cty", y="hwy", color="drv")) +
scale_color_discrete(guide=False) +
theme(axis_text =element_text(size=12),
axis_title=element_text(size=14)
),
figsize=(4,4))
pw.param["margin"] = 0.2
(g1/(g3|g2)[g3]).savefig() #By specifying g3 in (g3|g2), g1 is positioned exactly on g3.
If you face any problems, please let me know by creating issues on Github of patchworklib.
@has2k1 any update on including this in a future version of plotnine? I see you dropped it as a goal from 0.10.0.
@ponnhide I believe your solution does not work with the latest version(s) of plotnine, as reported at your repo.
@has2k1 any update on including this in a future version of plotnine? I see you dropped it as a goal from 0.10.0.
Putting a version number was too ambitious. This is a target we are already carefully working towards.
I managed to figure out a way to utilize plt.subplots()
in matplotlib
to arrange subplots in plotnine
. The idea basically came from using imread
and imshow
, and it seems that the result is not bad:
import matplotlib.pyplot as plt
from plotnine import (
aes,
facet_wrap,
geom_bar,
geom_boxplot,
geom_point,
geom_smooth,
ggplot,
)
from plotnine.data import diamonds, mtcars
p1 = ggplot(mtcars) + geom_point(aes('mpg', 'disp'))
p2 = ggplot(mtcars) + geom_boxplot(aes('gear', 'disp', group='gear'))
p3 = (
ggplot(mtcars, aes('wt', 'mpg', color='factor(gear)'))
+ geom_point()
+ geom_smooth(method='lm')
+ facet_wrap('gear')
)
p4 = ggplot(data=diamonds) + geom_bar(
mapping=aes(x="cut", fill="clarity"), position="dodge"
)
for i, pic in enumerate([p1, p2, p3, p4], start=1):
pic.save(f'pic_{i}.png', width=8, height=6, dpi=300)
fig, axes = plt.subplots(2, 2, figsize=(10, 8), dpi=300)
for i, ax in enumerate(axes.flat, start=1):
img = plt.imread(f'pic_{i}.png')
ax.imshow(img)
ax.set_axis_off()
fig.tight_layout()
fig.savefig('subplots.png')
What's the current status?
Anything else?
@stephematician, I have updated Patchworklib to support the newest version of plotnine. At least, it seems to work for basic examples. https://colab.research.google.com/drive/1zK_3_bFRYDIO-xLIvo1kzQxXGSk0ptuP?usp=sharing
It probably can't fully support plotnine yet, but it should help to a certain extent.
Does it make sense to have a portion of the plotnine test suite to stabilize API features needed by patchworklib or others? For me it would be great to be able to rely on continued compatibility of the two packages.
A strategy I've observed and tried before:
@pytest.mark.for_patchworklib
.-m "not for_patchworklib"
to exclude tests that are marked this way. This way, failures of these tests are not necessarily an obstacle to merging PRs.-m for_patchworklib
. This could be run less often than the main job, and be for information only.The added tests can be developed in different ways, e.g.:
main
and run it against the target ref for the GHA workflow — OR —If it helps I could make a PR to illustrate this approach.
plotnine
is made usingmatplotlib
as the back-end, so I'm guessing there must be a way to draw subplots (without using faceting).Is there a way to do so? I'd be happy to contribute to the documentation if someone points out a solution.