2014-11-05 221 views
23

我想用R ggplot複製這enter image description here。我有完全一樣的數據:ggplot2餅圖和甜甜圈圖在同一個地塊

browsers<-structure(list(browser = structure(c(3L, 3L, 3L, 3L, 2L, 2L, 
2L, 1L, 5L, 5L, 4L), .Label = c("Chrome", "Firefox", "MSIE", 
"Opera", "Safari"), class = "factor"), version = structure(c(5L, 
6L, 7L, 8L, 2L, 3L, 4L, 1L, 10L, 11L, 9L), .Label = c("Chrome 10.0", 
"Firefox 3.5", "Firefox 3.6", "Firefox 4.0", "MSIE 6.0", "MSIE 7.0", 
"MSIE 8.0", "MSIE 9.0", "Opera 11.x", "Safari 4.0", "Safari 5.0" 
), class = "factor"), share = c(10.85, 7.35, 33.06, 2.81, 1.58, 
13.12, 5.43, 9.91, 1.42, 4.55, 1.65), ymax = c(10.85, 18.2, 51.26, 
54.07, 55.65, 68.77, 74.2, 84.11, 85.53, 90.08, 91.73), ymin = c(0, 
10.85, 18.2, 51.26, 54.07, 55.65, 68.77, 74.2, 84.11, 85.53, 
90.08)), .Names = c("browser", "version", "share", "ymax", "ymin" 
), row.names = c(NA, -11L), class = "data.frame") 

,它看起來像這樣:

> browsers 
    browser  version share ymax ymin 
1  MSIE  MSIE 6.0 10.85 10.85 0.00 
2  MSIE  MSIE 7.0 7.35 18.20 10.85 
3  MSIE  MSIE 8.0 33.06 51.26 18.20 
4  MSIE  MSIE 9.0 2.81 54.07 51.26 
5 Firefox Firefox 3.5 1.58 55.65 54.07 
6 Firefox Firefox 3.6 13.12 68.77 55.65 
7 Firefox Firefox 4.0 5.43 74.20 68.77 
8 Chrome Chrome 10.0 9.91 84.11 74.20 
9 Safari Safari 4.0 1.42 85.53 84.11 
10 Safari Safari 5.0 4.55 90.08 85.53 
11 Opera Opera 11.x 1.65 91.73 90.08 

到目前爲止,我已經繪製的各個組成部分(即版本的圓環圖,以及餅圖瀏覽器),像這樣:

ggplot(browsers) + geom_rect(aes(fill=version, ymax=ymax, ymin=ymin, xmax=4, xmin=3)) + 
coord_polar(theta="y") + xlim(c(0, 4)) 

enter image description here

ggplot(browsers) + geom_bar(aes(x = factor(1), fill = browser),width = 1) + 
coord_polar(theta="y") 

enter image description here

的問題是,我如何將二者結合起來,使其看起來最前面的圖像?我已經嘗試了很多方法,例如:

ggplot(browsers) + geom_rect(aes(fill=version, ymax=ymax, ymin=ymin, xmax=4, xmin=3)) +   geom_bar(aes(x = factor(1), fill = browser),width = 1) + coord_polar(theta="y") + xlim(c(0, 4)) 

但是,我所有的結果都是扭曲的或以錯誤消息結尾。

+0

不知這是後話了'Rcircos'或'circlize'包可以處理。 – jazzurro 2014-11-05 01:37:47

+0

@jazzurro哦,我不知道那些軟件包...將def檢查出來!謝謝;) – maryam 2014-11-05 02:25:46

+0

我不知道他們是否允許你有你想要的圖形。但是,看到內部和外部的圈子,我想在包裝中可能會有一些東西給你。 – jazzurro 2014-11-05 02:31:34

回答

17

我發現它首先在直角座標系下工作比較容易,如果這是正確的,那麼切換到極座標系。 x座標變成極座標的半徑。因此,在直角座標中,內積從零到一個數字,像3和外帶從3前進到4。

例如

ggplot(browsers) + 
    geom_rect(aes(fill=version, ymax=ymax, ymin=ymin, xmax=4, xmin=3)) + 
    geom_rect(aes(fill=browser, ymax=ymax, ymin=ymin, xmax=3, xmin=0)) + 
    xlim(c(0, 4)) + 
    theme(aspect.ratio=1) 

enter image description here

然後,當你切換到極地,你會得到你想要的東西。

ggplot(browsers) + 
    geom_rect(aes(fill=version, ymax=ymax, ymin=ymin, xmax=4, xmin=3)) + 
    geom_rect(aes(fill=browser, ymax=ymax, ymin=ymin, xmax=3, xmin=0)) + 
    xlim(c(0, 4)) + 
    theme(aspect.ratio=1) + 
    coord_polar(theta="y") 

enter image description here

這是一個開始,但可能需要微調依賴於Y(或角度),並且還計算出所述標記/圖例/着色...通過使用矩形兩者的內圈和外圈,這應該簡化調整着色。此外,使用reshape2 :: melt函數重新組織數據非常有用,因此使用組(或顏色)可以正確顯示圖例。

+0

你能夠得到你想要的情節,還是還有一個問題?也許你可以用這種方法更新原始問題。 – user3969377 2014-11-05 02:40:14

+0

@ user3969377哇,這絕對是我一直在尋找的東西。我現在要微調美學和好去!我開始認爲這在ggplot中不太可行。你的解釋幫助了一噸!非常感謝你!如果我遇到任何問題,我可以給你留言嗎? – maryam 2014-11-05 02:47:10

+0

如果您有其他問題,請將其作爲其他問題發佈。我去睡覺了。我建議你修改y,以便它取決於數值,而不是因素,以便更好地控制角度。另外,閱讀熔化。 – user3969377 2014-11-05 02:51:43

27

編輯2

我原來的答案是非常愚蠢的。這是一個簡短得多的版本,它使用更簡單的界面完成大部分工作。

#' x  numeric vector for each slice 
#' group vector identifying the group for each slice 
#' labels vector of labels for individual slices 
#' col colors for each group 
#' radius radius for inner and outer pie (usually in [0,1]) 

donuts <- function(x, group = 1, labels = NA, col = NULL, radius = c(.7, 1)) { 
    group <- rep_len(group, length(x)) 
    ug <- unique(group) 
    tbl <- table(group)[order(ug)] 

    col <- if (is.null(col)) 
    seq_along(ug) else rep_len(col, length(ug)) 
    col.main <- Map(rep, col[seq_along(tbl)], tbl) 
    col.sub <- lapply(col.main, function(x) { 
    al <- head(seq(0, 1, length.out = length(x) + 2L)[-1L], -1L) 
    Vectorize(adjustcolor)(x, alpha.f = al) 
    }) 

    plot.new() 

    par(new = TRUE) 
    pie(x, border = NA, radius = radius[2L], 
     col = unlist(col.sub), labels = labels) 

    par(new = TRUE) 
    pie(x, border = NA, radius = radius[1L], 
     col = unlist(col.main), labels = NA) 
} 

par(mfrow = c(1,2), mar = c(0,4,0,4)) 
with(browsers, 
    donuts(share, browser, sprintf('%s: %s%%', version, share), 
      col = c('cyan2','red','orange','green','dodgerblue2')) 
) 

with(mtcars, 
    donuts(mpg, interaction(gear, cyl), rownames(mtcars)) 
) 

enter image description here


原帖

你們沒有givemedonutsorgivemedeath功能?基本的圖形總是這種非常詳細的東西。儘管如此,想不到繪製中心餅圖標籤的高雅方式。

givemedonutsorgivemedeath('~/desktop/donuts.pdf') 

給我

enter image description here

注意,在?pie你看到

Pie charts are a very bad way of displaying information. 

代碼:

browsers <- structure(list(browser = structure(c(3L, 3L, 3L, 3L, 2L, 2L, 
    2L, 1L, 5L, 5L, 4L), .Label = c("Chrome", "Firefox", "MSIE", 
    "Opera", "Safari"), class = "factor"), version = structure(c(5L, 
    6L, 7L, 8L, 2L, 3L, 4L, 1L, 10L, 11L, 9L), .Label = c("Chrome 10.0", 
    "Firefox 3.5", "Firefox 3.6", "Firefox 4.0", "MSIE 6.0", "MSIE 7.0", 
    "MSIE 8.0", "MSIE 9.0", "Opera 11.x", "Safari 4.0", "Safari 5.0"), 
    class = "factor"), share = c(10.85, 7.35, 33.06, 2.81, 1.58, 
    13.12, 5.43, 9.91, 1.42, 4.55, 1.65), ymax = c(10.85, 18.2, 51.26, 
    54.07, 55.65, 68.77, 74.2, 84.11, 85.53, 90.08, 91.73), ymin = c(0, 
    10.85, 18.2, 51.26, 54.07, 55.65, 68.77, 74.2, 84.11, 85.53, 
    90.08)), .Names = c("browser", "version", "share", "ymax", "ymin"), 
    row.names = c(NA, -11L), class = "data.frame") 

browsers$total <- with(browsers, ave(share, browser, FUN = sum)) 

givemedonutsorgivemedeath <- function(file, width = 15, height = 11) { 
    ## house keeping 
    if (missing(file)) file <- getwd() 
    plot.new(); op <- par(no.readonly = TRUE); on.exit(par(op)) 

    pdf(file, width = width, height = height, bg = 'snow') 

    ## useful values and colors to work with 
    ## each group will have a specific color 
    ## each subgroup will have a specific shade of that color 
    nr <- nrow(browsers) 
    width <- max(sqrt(browsers$share))/0.8 

    tbl <- with(browsers, table(browser)[order(unique(browser))]) 
    cols <- c('cyan2','red','orange','green','dodgerblue2') 
    cols <- unlist(Map(rep, cols, tbl)) 

    ## loop creates pie slices 
    plot.new() 
    par(omi = c(0.5,0.5,0.75,0.5), mai = c(0.1,0.1,0.1,0.1), las = 1) 
    for (i in 1:nr) { 
    par(new = TRUE) 

    ## create color/shades 
    rgb <- col2rgb(cols[i]) 
    f0 <- rep(NA, nr) 
    f0[i] <- rgb(rgb[1], rgb[2], rgb[3], 190/sequence(tbl)[i], maxColorValue = 255) 

    ## stick labels on the outermost section 
    lab <- with(browsers, sprintf('%s: %s', version, share)) 
    if (with(browsers, share[i] == max(share))) { 
     lab0 <- lab 
    } else lab0 <- NA 

    ## plot the outside pie and shades of subgroups 
    pie(browsers$share, border = NA, radius = 5/width, col = f0, 
     labels = lab0, cex = 1.8) 

    ## repeat above for the main groups 
    par(new = TRUE) 
    rgb <- col2rgb(cols[i]) 
    f0[i] <- rgb(rgb[1], rgb[2], rgb[3], maxColorValue = 255) 

    pie(browsers$share, border = NA, radius = 4/width, col = f0, labels = NA) 
    } 

    ## extra labels on graph 

    ## center labels, guess and check? 
    text(x = c(-.05, -.05, 0.15, .25, .3), y = c(.08, -.12, -.15, -.08, -.02), 
     labels = unique(browsers$browser), col = 'white', cex = 1.2) 

    mtext('Browser market share, April 2011', side = 3, line = -1, adj = 0, 
     cex = 3.5, outer = TRUE) 
    mtext('stackoverflow.com:::maryam', side = 3, line = -3.6, adj = 0, 
     cex = 1.75, outer = TRUE, font = 3) 
    mtext('/questions/26748069/ggplot2-pie-and-donut-chart-on-same-plot', 
     side = 1, line = 0, adj = 1.0, cex = 1.2, outer = TRUE, font = 3) 
    dev.off() 
} 

givemedonutsorgivemedeath('~/desktop/donuts.pdf') 

編輯1

width <- 5 

tbl <- table(browsers$browser)[order(unique(browsers$browser))] 
col.main <- Map(rep, seq_along(tbl), tbl) 
col.sub <- lapply(col.main, function(x) 
    Vectorize(adjustcolor)(x, alpha.f = seq_along(x)/length(x))) 

plot.new() 

par(new = TRUE) 
pie(browsers$share, border = NA, radius = 5/width, 
    col = unlist(col.sub), labels = browsers$version) 

par(new = TRUE) 
pie(browsers$share, border = NA, radius = 4/width, 
    col = unlist(col.main), labels = NA) 
+1

這是你的rawr包的一部分嗎?非常好。+1 – jazzurro 2014-11-05 04:12:22

+0

也許很快。很難概括這樣的東西 – rawr 2014-11-05 04:17:01

+0

我最初認爲你的包裝裏有這個東西!這張圖很棒。 – jazzurro 2014-11-05 04:18:23

5

我創建通用甜甜圈積函數來做到這一點,這可能

  • 牽引環情節,即繪製餅圖爲panel和由着色每個扇形給定的百分比pctrcolors cols。戒指寬度可以通過outradius>radius>innerradius進行調整。
  • 將幾個環形圖疊加在一起。

主要功能實際上是繪製條形圖並將其彎曲成圓環,因此它是餅圖和條形圖之間的東西。

例餅圖,兩環:

Pie 1

瀏覽器餅圖

Pie 2

donuts_plot <- function(
         panel = runif(3), # counts 
         pctr = c(.5,.2,.9), # percentage in count 
         legend.label='', 
         cols = c('chartreuse', 'chocolate','deepskyblue'), # colors 
         outradius = 1, # outter radius 
         radius = .7, # 1-width of the donus 
         add = F, 
         innerradius = .5, # innerradius, if innerradius==innerradius then no suggest line 
         legend = F, 
         pilabels=F, 
         legend_offset=.25, # non-negative number, legend right position control 
         borderlit=c(T,F,T,T) 
         ){ 
    par(new=add) 
    if(sum(legend.label=='')>=1) legend.label=paste("Series",1:length(pctr)) 
    if(pilabels){ 
     pie(panel, col=cols,border = borderlit[1],labels = legend.label,radius = outradius) 
    } 
    panel = panel/sum(panel) 

    pctr2= panel*(1 - pctr) 
    pctr3 = c(pctr,pctr) 
    pctr_indx=2*(1:length(pctr)) 
    pctr3[pctr_indx]=pctr2 
    pctr3[-pctr_indx]=panel*pctr 
    cols_fill = c(cols,cols) 
    cols_fill[pctr_indx]='white' 
    cols_fill[-pctr_indx]=cols 
    par(new=TRUE) 
    pie(pctr3, col=cols_fill,border = borderlit[2],labels = '',radius = outradius) 
    par(new=TRUE) 
    pie(panel, col='white',border = borderlit[3],labels = '',radius = radius) 
    par(new=TRUE) 
    pie(1, col='white',border = borderlit[4],labels = '',radius = innerradius) 
    if(legend){ 
     # par(mar=c(5.2, 4.1, 4.1, 8.2), xpd=TRUE) 
     legend("topright",inset=c(-legend_offset,0),legend=legend.label, pch=rep(15,'.',length(pctr)), 
       col=cols,bty='n') 
    } 
    par(new=FALSE) 
} 
## col- > subcor(change hue/alpha) 
subcolors <- function(.dta,main,mainCol){ 
    tmp_dta = cbind(.dta,1,'col') 
    tmp1 = unique(.dta[[main]]) 
    for (i in 1:length(tmp1)){ 
     tmp_dta$"col"[.dta[[main]] == tmp1[i]] = mainCol[i] 
    } 
    u <- unlist(by(tmp_dta$"1",tmp_dta[[main]],cumsum)) 
    n <- dim(.dta)[1] 
    subcol=rep(rgb(0,0,0),n); 
    for(i in 1:n){ 
     t1 = col2rgb(tmp_dta$col[i])/256 
     subcol[i]=rgb(t1[1],t1[2],t1[3],1/(1+u[i])) 
    } 
    return(subcol); 
} 
### Then get the plot is fairly easy: 
# INPUT data 
browsers <- structure(list(browser = structure(c(3L, 3L, 3L, 3L, 2L, 2L, 
               2L, 1L, 5L, 5L, 4L), 
               .Label = c("Chrome", "Firefox", "MSIE","Opera", "Safari"),class = "factor"), 
          version = structure(c(5L,6L, 7L, 8L, 2L, 3L, 4L, 1L, 10L, 11L, 9L), 
               .Label = c("Chrome 10.0", "Firefox 3.5", "Firefox 3.6", "Firefox 4.0", "MSIE 6.0", 
                  "MSIE 7.0","MSIE 8.0", "MSIE 9.0", "Opera 11.x", "Safari 4.0", "Safari 5.0"), 
               class = "factor"), 
          share = c(10.85, 7.35, 33.06, 2.81, 1.58,13.12, 5.43, 9.91, 1.42, 4.55, 1.65), 
          ymax = c(10.85, 18.2, 51.26,54.07, 55.65, 68.77, 74.2, 84.11, 85.53, 90.08, 91.73), 
          ymin = c(0,10.85, 18.2, 51.26, 54.07, 55.65, 68.77, 74.2, 84.11, 85.53,90.08)), 
         .Names = c("browser", "version", "share", "ymax", "ymin"), 
         row.names = c(NA, -11L), class = "data.frame") 
## data clean 
browsers=browsers[order(browsers$browser,browsers$share),] 
arr=aggregate(share~browser,browsers,sum) 
### choose your cols 
mainCol = c('chartreuse3', 'chocolate3','deepskyblue3','gold3','deeppink3') 
donuts_plot(browsers$share,rep(1,11),browsers$version, 
     cols=subcolors(browsers,"browser",mainCol), 
     legend=F,pilabels = T,borderlit = rep(F,4)) 
donuts_plot(arr$share,rep(1,5),arr$browser, 
     cols=mainCol,pilabels=F,legend=T,legend_offset=-.02, 
     outradius = .71,radius = .0,innerradius=.0,add=T, 
     borderlit = rep(F,4)) 
###end of line