2013-07-19 59 views
11

Tôi đang kết hợp hai ô riêng biệt vào bố cục lưới với grid như được gợi ý bởi @lgautier trong rpy2 bằng python. Cốt truyện đầu là một mật độ và và phía dưới một biểu đồ hình cột:căn chỉnh các ô phi khía cạnh riêng biệt trong ggplot2 bằng cách sử dụng Rpy2 trong Python

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) 

những gì tôi nhận được là gần với những gì tôi muốn nhưng âm mưu không chính xác liên kết (thể hiện bởi các mũi tên mà tôi thêm vào):

enter image description here

Tôi muốn khu vực ô (không phải là truyền thuyết) khớp chính xác. Làm thế nào có thể đạt được? sự khác biệt ở đây không quá lớn nhưng khi bạn thêm điều kiện vào biểu đồ thanh bên dưới hoặc làm cho biểu đồ thanh được né tránh với position_dodge sự khác biệt có thể trở nên rất lớn và các ô không được căn chỉnh.

Giải pháp ggplot tiêu chuẩn có thể không dễ dàng được dịch ra tiếng rpy2:

arrange dường như là grid_arrange trong gridExtra:

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

ggplotGrob là không thể truy cập từ ggplot2, nhưng có thể được truy cập như thế này:

>>> ggplot2.ggplot2.ggplotGrob 

Mặc dù tôi không biết cách truy cập grid::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 

vì vậy không rõ cách dịch giải pháp ggplot2 chuẩn thành rpy2.

chỉnh sửa: như những người khác đã chỉ ra grid::unit.pmax là . Tuy nhiên, tôi vẫn không biết cách truy cập vào rpy2 thông số widths của các đối tượng grob, điều này là cần thiết để đặt độ rộng của ô là giá trị của ô rộng hơn. Tôi có:

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]) 

gA.widths không đúng cú pháp. Các đối tượng grobgA in như:

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] 

update: thực hiện một số tiến bộ về độ rộng truy cập, nhưng vẫn không thể dịch các giải pháp. Để đặt chiều rộng của grobs, tôi có:

# 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) 

Nó chạy nhưng không hoạt động. Kết quả đầu ra là:

[[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: nhận ra là @baptiste chỉ ra rằng sẽ hữu ích khi hiển thị phiên bản R thuần túy của những gì tôi đang cố gắng tái tạo trong rpy2. Dưới đây là phiên bản R tinh khiết:

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) 

Tôi nghĩ rằng điều này trong tác phẩm chung của hai tấm với truyền thuyết rằng có những khía cạnh khác nhau trong ggplot2 và tôi muốn thực hiện điều này trong rpy2.

UPDATE3: gần như đã nhận nó để làm việc, bằng cách xây dựng một FloatVector lên một yếu tố tại một thời điểm:

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) 

tuy nhiên điều này tạo ra một bãi chứa segfault/lõi:

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) 

Cập nhật: tiền thưởng đã kết thúc. Tôi đánh giá cao các câu trả lời nhận được, nhưng không phải câu trả lời sử dụng rpy2 và đây là một câu hỏi rpy2, vì vậy về mặt kỹ thuật các câu trả lời không phải là về chủ đề. Có một giải pháp R đơn giản cho vấn đề này (ngay cả khi không có giải pháp cho điều này nói chung như @ baptiste đã chỉ ra) và câu hỏi đơn giản là cách dịch nó thành rpy2

+6

những người đã đánh dấu mục này là trùng lặp không đánh giá cao sự tinh tế của bản dịch mã từ R thành Rpy2. Có, đôi khi bản dịch rất dễ nhưng đôi khi nó phản trực giác. Đây là một câu hỏi rpy2. Không phải là một câu hỏi ggplot2 thẳng lên. Bạn chỉ đang can thiệp và chặn quá trình hỏi và trả lời mà không có lý do chính đáng. – user248237dfsf

+0

'unit.pmax' là một hàm tách biệt với' đơn vị' (mặc dù cả hai đều nằm trong gói 'grid'). Có 'grid.unit.pmax' có sẵn trong python sau' grid = importr ("grid") '? Hoặc không 'rpy' làm một số dịch để dấu chấm trong tên chức năng mà không liên quan đến công văn phương pháp S3? –

+2

@BrianDiggs có vẻ là 'grid :: unit.pmax' sẽ trở thành' grid.unit_pmax' – baptiste

Trả lời

2

Chia các truyền thuyết khỏi các ô (thấy ggplot separate legend and plot), sau đó sử dụng grid.arrange

library(gridExtra) 
g_legend <- function(a.gplot){ 
     tmp <- ggplot_gtable(ggplot_build(a.gplot)) 
    leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box") 
    legend <- tmp$grobs[[leg]] 
    legend 
} 
legend1 <- g_legend(p1) 
legend2 <- g_legend(p2) 

grid.arrange(p1 + theme(legend.position = 'none'), legend1, 
      p2 + theme(legend.position = 'none'), legend2, 
      ncol=2, widths = c(5/6,1/6)) 

Điều này rõ ràng là việc thực hiện R.

+0

cảm ơn nhưng nó không rõ ràng làm thế nào để dịch này để rpy2 và tôi muốn tránh xác định chức năng R thuần khiết mới kể từ đó làm phức tạp các dịch hơn – user248237dfsf

6

Căn chỉnh hai ô trở nên phức tạp hơn nhiều khi các khía cạnh có liên quan. Tôi không biết nếu có một giải pháp chung, ngay cả trong R. Hãy xem xét tình huống này,

p1 <- ggplot(mtcars, aes(mpg, wt)) + geom_point() + 
    facet_wrap(~ cyl, ncol=2,scales="free") 
p2 <- p1 + facet_null() + aes(colour=am) + ylab("this\nis taller") 

gridExtra::grid.arrange(p1, p2) 

enter image description here

Với một số công việc, bạn có thể so sánh độ rộng cho trục trái, và những huyền thoại (có thể có hoặc không có mặt ở bên phải).

library(gtable) 

# legend, if it exists, may be the second last item on the right, 
# unless it's not on the right side. 
locate_guide <- function(g){ 
    right <- max(g$layout$r) 
    gg <- subset(g$layout, (grepl("guide", g$layout$name) & r == right - 1L) | 
       r == right) 
    sort(gg$r) 
} 

compare_left <- function(g1, g2){ 

    w1 <- g1$widths[1:3] 
    w2 <- g2$widths[1:3] 
    unit.pmax(w1, w2) 
} 

align_lr <- function(g1, g2){ 

    # align the left side 
    left <- compare_left(g1, g2) 
    g1$widths[1:3] <- g2$widths[1:3] <- left 

    # now deal with the right side 

    gl1 <- locate_guide(g1) 
    gl2 <- locate_guide(g2) 

    if(length(gl1) < length(gl2)){ 
    g1$widths[[gl1]] <- max(g1$widths[gl1], g2$widths[gl2[2]]) + 
     g2$widths[gl2[1]] 
    } 
    if(length(gl2) < length(gl1)){ 
    g2$widths[[gl2]] <- max(g2$widths[gl2], g1$widths[gl1[2]]) + 
     g1$widths[gl1[1]] 
    } 
    if(length(gl1) == length(gl2)){ 
    g1$widths[[gl1]] <- g2$widths[[gl2]] <- unit.pmax(g1$widths[gl1], g2$widths[gl2]) 
    } 

    grid.arrange(g1, g2) 
} 

align_lr(g1, g2) 

enter image description here

Lưu ý rằng tôi đã không kiểm tra các trường hợp khác; Tôi chắc chắn nó rất dễ phá vỡ. Theo như tôi hiểu từ tài liệu, rpy2 cung cấp cơ chế cho use an arbitrary piece of R code, do đó chuyển đổi không phải là vấn đề.

+0

Nhưng mọi người làm cho lưới phức tạp của lô trong R tất cả các thời gian .. vì vậy những gì là cách chung để làm điều đó? Có cách nào để đạt được cốt truyện ban đầu tôi đã thực hiện trong bài viết bằng cách sử dụng các khía cạnh, tất cả trong khuôn mặt ggplot (với facet_wrap và facet_grid) để tránh lưới riêng biệt hoàn toàn? Có lẽ tôi chỉ không biết cách sử dụng tiên tiến của mặt trước – user248237dfsf

+0

một câu hỏi về ví dụ của bạn: nếu bạn loại bỏ chú thích cho 'am', bạn có thể sắp xếp chúng trong R? – user248237dfsf

+1

một lần theo dõi khác: phương pháp này sử dụng các loại geom khác nhau trên các tập con dữ liệu khác nhau như thế nào? điều này sẽ cho phép bạn tạo cốt truyện tôi dự định trong bài gốc bằng cách sử dụng kết hợp 'geom_density' và' geom_bar'? http://stackoverflow.com/questions/7903972/can-you-specify-different-geoms-for-different-facets-in-a-ggplot – user248237dfsf

1

Bản dịch chưa được trả lời của câu trả lời bằng cách sử dụng gridExtra 's grid.arrange(). Mặt trái của các ô (nơi các nhãn cho trục y) có thể không luôn được căn chỉnh.

from rpy2.robjects.packages import importr 
gridextra = importr('gridExtra') 
from rpy2.robjects.lib import ggplot2 
_ggplot2 = ggplot2.ggplot2 
def dollar(x, name): # should be included in rpy2.robjects, may be... 
    return x[x.index(name)] 

def g_legend(a_gplot): 
    tmp = _ggplot2.ggplot_gtable(_ggplot2.ggplot_build(a_gplot)) 
    leg = [dollar(x, 'name')[0] for x in dollar(tmp, 'grobs')].index('guide-box') 
    legend = dollar(tmp, 'grobs')[leg] 
    return legend 
legend1 = g_legend(p1) 
legend2 = g_legend(p2) 
nolegend = ggplot2.theme(**{'legend.position': 'none'}) 
gridexta.grid_arrange(p1 + nolegend, legend1, 
         p2 + nolegend, legend2, 
         ncol=2, widths = FloatVector((5.0/6,1.0/6))) 
Các vấn đề liên quan