wyrównywanie odrębnych wykresów bez aspektu w ggplot2 przy użyciu Rpy2 w Pythonie

Łączę dwa różne wykresy w układ siatki zgrid jak sugeruje @lgautier w rpy2 za pomocą pythona. Górny wykres to gęstość, a na dole wykres słupkowy:

iris = r('iris')
import pandas

# define layout
lt = grid.layout(2, 1)
vp = grid.viewport(layout = lt)
vp.push()

# first plot
vp_p = grid.viewport(**{'layout.pos.row': 1, 'layout.pos.col':1})
p1 = ggplot2.ggplot(iris) + \
    ggplot2.geom_density(aes_string(x="Sepal.Width",
                                    colour="Species")) + \
    ggplot2.facet_wrap(Formula("~ Species"))
p1.plot(vp = vp_p)

# second plot
mean_df = pandas.DataFrame({"Species": ["setosa", "virginica", "versicolor"],
                            "X": [10, 2, 30],
                            "Y": [5, 3, 4]})
mean_df = pandas.melt(mean_df, id_vars=["Species"])
r_mean_df = get_r_dataframe(mean_df)
p2 = ggplot2.ggplot(r_mean_df) + \
     ggplot2.geom_bar(aes_string(x="Species",
                                 y="value",
                                 group="variable",
                                 colour="variable"),
                      position=ggplot2.position_dodge(),
                      stat="identity")
vp_p = grid.viewport(**{'layout.pos.row': 2, 'layout.pos.col':1})
p2.plot(vp = vp_p)

to, co otrzymuję, jest zbliżone do tego, czego chcę, ale wykresy nie są dokładnie wyrównane (pokazane przez strzałki, które dodałem):

Chciałbym, aby regiony fabuły (a nie legendy) dokładnie do siebie pasowały. Jak można to osiągnąć? różnica tutaj nie jest tak duża, ale gdy dodasz warunki do wykresu słupkowego poniżej lub sprawisz, że unikną wykresów słupkowych za pomocąposition_dodge różnice mogą stać się bardzo duże, a wykresy nie są wyrównane.

Standardowe rozwiązanie ggplot nie może być łatwo przetłumaczone na rpy2:

arrange Wydaje się, żegrid_arrange wgridExtra:

>>> gridExtra = importr("gridExtra")
>>> gridExtra.grid_arrange
<SignatureTranslatedFunction - Python:0x430f518 / R:0x396f678>

ggplotGrob nie jest dostępny zggplot2, ale można uzyskać do niego dostęp w ten sposób:

>>> ggplot2.ggplot2.ggplotGrob

Chociaż nie mam pojęcia, jak uzyskać dostępgrid::unit.pmax:

>>> grid.unit
<bound method type.unit of <class 'rpy2.robjects.lib.grid.Unit'>>
>>> grid.unit("pmax")
Error in (function (x, units, data = NULL)  : 
argument "units" is missing, with no default
rpy2.rinterface.RRuntimeError: Error in (function (x, units, data = NULL)  : 
argument "units" is missing, with no default

więc nie jest jasne, jak przetłumaczyć standardowe rozwiązanie ggplot2 na rpy2.

edytować: jak zauważyli innigrid::unit.pmax jestgrid.unit_pmax. Nadal nie wiem, jak uzyskać dostęp do rpy2 thewidths parametrgrob obiekty, które są niezbędne do ustawienia szerokości działek tak, aby były szersze. Mam:

gA = ggplot2.ggplot2.ggplotGrob(p1)
gB = ggplot2.ggplot2.ggplotGrob(p2)

g = importr("grid")
print "gA: ", gA
maxWidth = g.unit_pmax(gA.widths[2:5], gB.widths[2:5])

ThegA.widths nie jest poprawną składnią. Thegrob obiektgA drukuje jako:

gA:  TableGrob (8 x 13) "layout": 17 grobs
    z         cells       name                                    grob
1   0 ( 1- 8, 1-13) background          rect[plot.background.rect.350]
2   1 ( 4- 4, 4- 4)    panel-1                gTree[panel-1.gTree.239]
3   2 ( 4- 4, 7- 7)    panel-2                gTree[panel-2.gTree.254]
4   3 ( 4- 4,10-10)    panel-3                gTree[panel-3.gTree.269]
5   4 ( 3- 3, 4- 4)  strip_t-1    absoluteGrob[strip.absoluteGrob.305]
6   5 ( 3- 3, 7- 7)  strip_t-2    absoluteGrob[strip.absoluteGrob.311]
7   6 ( 3- 3,10-10)  strip_t-3    absoluteGrob[strip.absoluteGrob.317]
8   7 ( 4- 4, 3- 3)   axis_l-1 absoluteGrob[axis-l-1.absoluteGrob.297]
9   8 ( 4- 4, 6- 6)   axis_l-2         zeroGrob[axis-l-2.zeroGrob.298]
10  9 ( 4- 4, 9- 9)   axis_l-3         zeroGrob[axis-l-3.zeroGrob.299]
11 10 ( 5- 5, 4- 4)   axis_b-1 absoluteGrob[axis-b-1.absoluteGrob.276]
12 11 ( 5- 5, 7- 7)   axis_b-2 absoluteGrob[axis-b-2.absoluteGrob.283]
13 12 ( 5- 5,10-10)   axis_b-3 absoluteGrob[axis-b-3.absoluteGrob.290]
14 13 ( 7- 7, 4-10)       xlab             text[axis.title.x.text.319]
15 14 ( 4- 4, 2- 2)       ylab             text[axis.title.y.text.321]
16 15 ( 4- 4,12-12)  guide-box                       gtable[guide-box]
17 16 ( 2- 2, 4-10)      title               text[plot.title.text.348]

aktualizacja: poczynił pewne postępy w dostępie do szerokości, ale nadal nie może przetłumaczyć rozwiązania. Aby ustawić szerokości grobów, mam:

# get grobs
gA = ggplot2.ggplot2.ggplotGrob(p1)
gB = ggplot2.ggplot2.ggplotGrob(p2)
g = importr("grid")
# get max width
maxWidth = g.unit_pmax(gA.rx2("widths")[2:5][0], gB.rx2("widths")[2:5][0])
print gA.rx2("widths")[2:5]
wA = gA.rx2("widths")[2:5]
wB = gB.rx2("widths")[2:5]
print "before: ", wA[0]
wA[0] = robj.ListVector(maxWidth)
print "After: ", wA[0]
print "before: ", wB[0]
wB[0] = robj.ListVector(maxWidth)
print "after:", wB[0]
gridExtra.grid_arrange(gA, gB, ncol=1)

Działa, ale nie działa. Wyjście to:

[[1]]
[1] 0.740361111111111cm

[[2]]
[1] 1null

[[3]]
[1] 0.127cm


before:  [1] 0.740361111111111cm

After:  [1] max(0.740361111111111cm, sum(1grobwidth, 0.15cm+0.1cm))

before:  [1] sum(1grobwidth, 0.15cm+0.1cm)

after: [1] max(0.740361111111111cm, sum(1grobwidth, 0.15cm+0.1cm))

update2: zdałem sobie sprawę, że @baptiste wskazał, że pomocne byłoby pokazanie czystej wersji R tego, co próbuję odtworzyć w rpy2. Oto czysta wersja R:

df <- data.frame(Species=c("setosa", "virginica", "versicolor"),X=c(1,2,3), Y=c(10,20,30))
p1 <- ggplot(iris) + geom_density(aes(x=Sepal.Width, colour=Species))
p2 <- ggplot(df) + geom_bar(aes(x=Species, y=X, colour=Species))
gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)
maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5])
gA$widths[2:5] <- as.list(maxWidth)
gB$widths[2:5] <- as.list(maxWidth)
grid.arrange(gA, gB, ncol=1)

Myślę, że ogólnie działa to dla dwóch paneli z legendami, które mają różne aspekty w ggplot2 i chcę to zaimplementować w rpy2.

update3: prawie zadziałało, budującFloatVector w górę o jeden element na raz:

maxWidth = []
for x, y in zip(gA.rx2("widths")[2:5], gB.rx2("widths")[2:5]):
    pmax = g.unit_pmax(x, y)
    print "PMAX: ", pmax
    val = pmax[1][0][0]
    print "VAL->", val
    maxWidth.append(val)
gA[gA.names.index("widths")][2:5] = robj.FloatVector(maxWidth)
gridExtra.grid_arrange(gA, gB, ncol=1)

generuje to jednak segfault / core dump:

Error: VECTOR_ELT() can only be applied to a 'list', not a 'double'
*** longjmp causes uninitialized stack frame ***: python2.7 terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x37)[0x7f83742e2817]
/lib/x86_64-linux-gnu/libc.so.6(+0x10a78d)[0x7f83742e278d]
/lib/x86_64-linux-gnu/libc.so.6(__longjmp_chk+0x33)[0x7f83742e26f3]
...
7f837591e000-7f8375925000 r--s 00000000 fc:00 1977264                    /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
7f8375926000-7f8375927000 rwxp 00000000 00:00 0 
7f8375927000-7f8375929000 rw-p 00000000 00:00 0 
7f8375929000-7f837592a000 r--p 00022000 fc:00 917959                     /lib/x86_64-linux-gnu/ld-2.15.so
7f837592a000-7f837592c000 rw-p 00023000 fc:00 917959                     /lib/x86_64-linux-gnu/ld-2.15.so
7ffff4b96000-7ffff4bd6000 rw-p 00000000 00:00 0                          [stack]
7ffff4bff000-7ffff4c00000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted (core dumped)

Aktualizacja: nagroda została zakończona. Doceniam otrzymane odpowiedzi, ale żadna z nich nie używa rpy2 i jest to pytanie rpy2, więc technicznie odpowiedzi nie są na temat. Istnieje proste rozwiązanie R tego problemu (nawet jeśli ogólnie nie ma rozwiązania tego problemu, jak zauważył @baptiste), a pytanie brzmi po prostu, jak go przetłumaczyć na rpy2

questionAnswers(3)

yourAnswerToTheQuestion