2013-04-24 41 views
15

Tôi đang cố gắng để tạo ra một piechart facetted với ggplot và phải đối mặt với vấn đề với việc đặt văn bản ở giữa mỗi lát:ggplot, khía cạnh, piechart: đặt văn bản ở giữa biểu đồ hình tròn lát

dat = read.table(text = "Channel Volume Cnt 
         AGENT high 8344 
         AGENT medium 5448 
         AGENT low 23823 
         KIOSK high 19275 
         KIOSK medium 13554 
         KIOSK low 38293", header=TRUE) 

vis = ggplot(data=dat, aes(x=factor(1), y=Cnt, fill=Volume)) + 
    geom_bar(stat="identity", position="fill") + 
    coord_polar(theta="y") + 
    facet_grid(Channel~.) + 
    geom_text(aes(x=factor(1), y=Cnt, label=Cnt, ymax=Cnt), 
      position=position_fill(width=1)) 

Đầu ra: enter image description here

Cần phải điều chỉnh thông số nào của geom_text để đặt nhãn số ở giữa các lát piechart?

Câu hỏi liên quan là Pie plot getting its text on top of each other nhưng không xử lý trường hợp có khía cạnh.

UPDATE: theo lời khuyên của Paul Hiemstra và cách tiếp cận trong câu hỏi trên, chúng tôi đã thay đổi mã như sau:

---> pie_text = dat$Cnt/2 + c(0,cumsum(dat$Cnt)[-length(dat$Cnt)]) 

    vis = ggplot(data=dat, aes(x=factor(1), y=Cnt, fill=Volume)) + 
    geom_bar(stat="identity", position="fill") + 
    coord_polar(theta="y") + 
    facet_grid(Channel~.) + 
    geom_text(aes(x=factor(1), 
--->    y=pie_text, 
        label=Cnt, ymax=Cnt), position=position_fill(width=1)) 

Như tôi dự kiến ​​chỉnh coordiantes văn bản là tuyệt đối nhưng nó cần được bên trong dữ liệu khía cạnh: enter image description here

+3

giải pháp mới nhất của tôi cho vấn đề này là tránh các biểu đồ pie bất cứ khi nào có thể :-) – topchef

Trả lời

25

ĐÁP MỚI: Với sự ra đời của ggplot2 v2.2.0, position_stack() có thể được sử dụng để xác định vị trí các nhãn mà không cần phải tính toán một biến vị trí đầu tiên.Đoạn mã dưới đây sẽ cung cấp cho bạn kết quả tương tự như câu trả lời cũ:

ggplot(data = dat, aes(x = "", y = Cnt, fill = Volume)) + 
    geom_bar(stat = "identity") + 
    geom_text(aes(label = Cnt), position = position_stack(vjust = 0.5)) + 
    coord_polar(theta = "y") + 
    facet_grid(Channel ~ ., scales = "free") 

Để loại bỏ trung tâm "rỗng", điều chỉnh mã để:

ggplot(data = dat, aes(x = 0, y = Cnt, fill = Volume)) + 
    geom_bar(stat = "identity") + 
    geom_text(aes(label = Cnt), position = position_stack(vjust = 0.5)) + 
    scale_x_continuous(expand = c(0,0)) + 
    coord_polar(theta = "y") + 
    facet_grid(Channel ~ ., scales = "free") 

ĐÁP OLD: Giải pháp cho vấn đề này là tạo biến vị trí, có thể được thực hiện khá dễ dàng với cơ sở R hoặc với các gói data.table, plyr hoặc dplyr:

Bước 1: Tạo biến vị trí cho mỗi kênh

# with base R 
dat$pos <- with(dat, ave(Cnt, Channel, FUN = function(x) cumsum(x) - 0.5*x)) 

# with the data.table package 
library(data.table) 
setDT(dat) 
dat <- dat[, pos:=cumsum(Cnt)-0.5*Cnt, by="Channel"] 

# with the plyr package 
library(plyr) 
dat <- ddply(dat, .(Channel), transform, pos=cumsum(Cnt)-0.5*Cnt) 

# with the dplyr package 
library(dplyr) 
dat <- dat %>% group_by(Channel) %>% mutate(pos=cumsum(Cnt)-0.5*Cnt) 

Bước 2: Tạo cốt truyện facetted

library(ggplot2) 
ggplot(data = dat) + 
    geom_bar(aes(x = "", y = Cnt, fill = Volume), stat = "identity") + 
    geom_text(aes(x = "", y = pos, label = Cnt)) + 
    coord_polar(theta = "y") + 
    facet_grid(Channel ~ ., scales = "free") 

Kết quả:

enter image description here

+0

Cảm ơn sự trợ giúp cập nhật của bạn. Có cách nào để thoát khỏi trung tâm "rỗng" không? (Vòng tròn nhỏ màu trắng ở giữa) – jesusgarciab

+0

@jesusgarciab có thể hơi trễ một chút, nhưng tôi đã cập nhật câu trả lời – Jaap

5

Để tinh chỉnh vị trí của văn bản nhãn tương ứng với toạ độ, bạn có thể sử dụng các đối số vjusthjust của geom_text. Điều này sẽ xác định vị trí của tất cả các nhãn đồng thời, vì vậy đây có thể không phải là những gì bạn cần.

Hoặc, bạn có thể điều chỉnh tọa độ của nhãn. Xác định data.frame mới nơi bạn trung bình tọa độ Cnt (label_x[i] = Cnt[i+1] + Cnt[i]) để định vị nhãn ở giữa bánh cụ thể đó. Chỉ cần vượt qua số data.frame mới này để geom_text thay thế cho số gốc data.frame.

Ngoài ra, piecharts có một số lỗi giải thích trực quan. Nói chung, tôi sẽ không sử dụng chúng, đặc biệt là nơi có các lựa chọn thay thế tốt, ví dụ: một dotplot:

ggplot(dat, aes(x = Cnt, y = Volume)) + 
    geom_point() + 
    facet_wrap(~ Channel, ncol = 1) 

Ví dụ, từ cốt truyện này rõ ràng là Cnt cao cho Kiosk hơn cho Đại lý, thông tin này bị mất trong piechart.

enter image description here

+0

Cảm ơn bạn đã câu trả lời - tôi sẽ thử. Nhưng tôi không thể đồng ý rằng âm mưu đề xuất là thay thế cho piechart. Thực tế là Cnt là cao hơn bị mất chủ yếu là do nhãn văn bản unaligned, theo ý kiến ​​của tôi. – topchef

+0

Biểu đồ hình tròn không thể hiển thị sự khác biệt về độ lớn giữa AGENT và KIOSK vì biểu đồ chỉ hiển thị kích thước tương đối. Văn bản khó hiểu hơn nhiều trong quan điểm của tôi. Hiệu ứng này trở nên lớn hơn khi so sánh, ví dụ: 5 danh mục. Những cuốn sách như http://www.amazon.com/Elements-Graphing-Data-William-Cleveland/dp/0963488414 ủng hộ việc sử dụng các biểu đồ giống như âm mưu chấm có lợi cho piechart. Chỉ cần google cho 'biểu đồ cái ác '. –

+0

Thật không may câu trả lời này không giải quyết vấn đề của việc có các khía cạnh như độ dài cần sự liên kết trên biến khía cạnh. – topchef

0

câu trả lời Sau đây là một phần, vụng về và tôi sẽ không chấp nhận nó. Hy vọng là nó sẽ thu hút giải pháp tốt hơn.

text_KIOSK = dat$Cnt 
text_AGENT = dat$Cnt 
text_KIOSK[dat$Channel=='AGENT'] = 0 
text_AGENT[dat$Channel=='KIOSK'] = 0 
text_KIOSK = text_KIOSK/1.7 + c(0,cumsum(text_KIOSK)[-length(dat$Cnt)]) 
text_AGENT = text_AGENT/1.7 + c(0,cumsum(text_AGENT)[-length(dat$Cnt)]) 
text_KIOSK[dat$Channel=='AGENT'] = 0 
text_AGENT[dat$Channel=='KIOSK'] = 0 
pie_text = text_KIOSK + text_AGENT 


vis = ggplot(data=dat, aes(x=factor(1), y=Cnt, fill=Volume)) + 
    geom_bar(stat="identity", position=position_fill(width=1)) + 
    coord_polar(theta="y") + 
    facet_grid(Channel~.) + 
    geom_text(aes(y=pie_text, label=format(Cnt,format="d",big.mark=','), ymax=Inf), position=position_fill(width=1)) 

Nó tạo ra sau biểu đồ: enter image description here

Như bạn thấy tôi không thể di chuyển nhãn cho màu xanh lá cây (thấp).

+0

Bạn có thấy câu trả lời của tôi không? Tôi nghĩ nó mang lại giải pháp mà bạn đang yêu cầu. – Jaap

+1

Vâng, cảm ơn bạn. Tôi sẽ dính vào quy tắc "không có biểu đồ hình tròn" bất cứ khi nào có thể nhưng đối với trường hợp ngoại lệ, câu trả lời là :-) – topchef

+0

Bạn hoàn toàn đúng khi tránh các biểu đồ hình tròn càng nhiều càng tốt, nhưng một số người dường như thực sự yêu thích chúng. Hầu hết thời gian biểu đồ thanh được cải thiện rõ ràng hơn rất nhiều. – Jaap

4

Tôi muốn nói chống lại cách thông thường để làm bánh nướng trong ggplot2, đó là vẽ một thanh xếp chồng lên nhau trong các tọa độ cực. Trong khi tôi đánh giá cao sự sang trọng toán học của phương pháp đó, nó gây ra tất cả các loại đau đầu khi cốt truyện không nhìn khá theo cách nó được cho là. Đặc biệt, điều chỉnh chính xác kích thước của chiếc bánh có thể khó khăn. (Nếu bạn không biết ý tôi là gì, hãy thử tạo một biểu đồ hình tròn kéo dài đến tận mép của bảng điều khiển.)

Tôi thích vẽ bánh trong hệ tọa độ Descartes bình thường, sử dụng geom_arc_bar() từ ggforce . Nó đòi hỏi một chút công việc phụ ở mặt trước, bởi vì chúng ta phải tự tính toán góc độ, nhưng điều đó dễ dàng và mức độ kiểm soát mà chúng tôi nhận được kết quả là đáng giá hơn. Tôi đã sử dụng phương pháp này trong câu trả lời trước herehere.

Các dữ liệu (từ câu hỏi):

dat = read.table(text = "Channel Volume Cnt 
AGENT high 8344 
AGENT medium 5448 
AGENT low 23823 
KIOSK high 19275 
KIOSK medium 13554 
KIOSK low 38293", header=TRUE) 

Mã bánh vẽ:

library(ggplot2) 
library(ggforce) 
library(dplyr) 

# calculate the start and end angles for each pie 
dat_pies <- left_join(dat, 
         dat %>% 
         group_by(Channel) %>% 
         summarize(Cnt_total = sum(Cnt))) %>% 
    group_by(Channel) %>% 
    mutate(end_angle = 2*pi*cumsum(Cnt)/Cnt_total,  # ending angle for each pie slice 
     start_angle = lag(end_angle, default = 0), # starting angle for each pie slice 
     mid_angle = 0.5*(start_angle + end_angle)) # middle of each pie slice, for the text label 

rpie = 1 # pie radius 
rlabel = 0.6 * rpie # radius of the labels; a number slightly larger than 0.5 seems to work better, 
        # but 0.5 would place it exactly in the middle as the question asks for. 

# draw the pies 
ggplot(dat_pies) + 
    geom_arc_bar(aes(x0 = 0, y0 = 0, r0 = 0, r = rpie, 
        start = start_angle, end = end_angle, fill = Volume)) + 
    geom_text(aes(x = rlabel*sin(mid_angle), y = rlabel*cos(mid_angle), label = Cnt), 
      hjust = 0.5, vjust = 0.5) + 
    coord_fixed() + 
    scale_x_continuous(limits = c(-1, 1), name = "", breaks = NULL, labels = NULL) + 
    scale_y_continuous(limits = c(-1, 1), name = "", breaks = NULL, labels = NULL) + 
    facet_grid(Channel~.) 

enter image description here

Để cho thấy lý do tại sao tôi nghĩ rằng phương pháp này là mạnh hơn rất nhiều so với thông thường (coord_polar()) cách tiếp cận, giả sử chúng ta muốn các nhãn ở bên ngoài của chiếc bánh chứ không phải bên trong. Điều này tạo ra một vài vấn đề, chẳng hạn như chúng tôi sẽ phải điều chỉnh hjustvjust tùy thuộc vào mặt của chiếc bánh mà nhãn rơi xuống, và chúng tôi cũng phải làm cho bảng điều khiển rộng hơn cao để tạo không gian cho nhãn trên bên mà không tạo ra không gian quá mức trên và dưới.Giải quyết những vấn đề trong cách tiếp cận cực phối hợp không phải là niềm vui, nhưng đó là tầm thường trong tọa độ Descartes:

# generate hjust and vjust settings depending on the quadrant into which each 
# label falls 
dat_pies <- mutate(dat_pies, 
        hjust = ifelse(mid_angle>pi, 1, 0), 
        vjust = ifelse(mid_angle<pi/2 | mid_angle>3*pi/2, 0, 1)) 

rlabel = 1.05 * rpie # now we place labels outside of the pies 

ggplot(dat_pies) + 
    geom_arc_bar(aes(x0 = 0, y0 = 0, r0 = 0, r = rpie, 
        start = start_angle, end = end_angle, fill = Volume)) + 
    geom_text(aes(x = rlabel*sin(mid_angle), y = rlabel*cos(mid_angle), label = Cnt, 
       hjust = hjust, vjust = vjust)) + 
    coord_fixed() + 
    scale_x_continuous(limits = c(-1.5, 1.4), name = "", breaks = NULL, labels = NULL) + 
    scale_y_continuous(limits = c(-1, 1), name = "", breaks = NULL, labels = NULL) + 
    facet_grid(Channel~.) 

enter image description here

Các vấn đề liên quan